前言
开发OpenGL App一年多了, 平时就是在各种赶项目, 赶功能; 做了不少笔记, 踩了不少坑, 但是仅仅是工作需要就去学习哪块, 很少静下来做总结;
今年年春换了一家东家, 开发直播类App, 需要视频处理等知识, 正好总结一番;
目前发现,在OpenGL的世界里 相对于渲染管线来说, 数学才是真正的大头;
相比于复杂的理论知识, 我们直接手上吧.
-
本文环境
Xcode 9.0OpenGLES 3.0
基本操作步骤
导入
GLKit框架直接集成
GLKViewController, 或者GLKView创建
OpenGL上下文EAGLContext-
创建程序对象
ShaderProgram- 连接
顶点着色器和片段着色器
- 连接
指定清屏颜色,填充到
ColorBuffer
导入框架, 直接集成与GLKViewController
#import <GLKit/GLKit.h>
@interface ViewController : GLKViewController
@end
创建OpenGL 上下文
-
EAGLContext调度中枢
保存绘制命令, 状态, 资源所有调度;
因为
OpenGL 是状态机机制, 故需要一个调度中枢的存在这里直接使用
OpenGLES 3.0
#pragma mark - setupContext
- (void)setupContext {
// 1.0 创建OpenGl上下文
self.context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3];
#if DEBUG
if (!self.context) {
NSLog(@"Create OpenGLES3.0 context fail.");
}
#endif
}
- 设置
Context为当前上下文, 并传递到glview
[EAGLContext setCurrentContext:self.context];
GLKView *glView = (GLKView *)self.view;
glView.context = self.context;
创建程序对象Program
-
创建
顶点着色器, 片段着色器着色器最终以字符串的形式传递到
program下面是最简单的两个着色器, 有影响即可, 暂无需深入


- 顶点着色器
Vertxt.glsl
// 顶点着色器
// 输入变量
attribute vec4 position;
attribute vec4 color;
// 输出变量
varying vec4 fragColor;
void main(void) {
// 传递到片段着色器
fragColor = color;
// 内建变量
gl_Position = position;
}
- 片段着色器
Fragment.glsl
// 片段着色器
varying lowp vec4 fragColor;
void main(void) {
// 内建变量
gl_FragColor = fragColor;
}
-
创建
program固定套路创建
program加载着色器, 并关联到
program链接
porgram释放
shader资源
GLuint program, vertShader, fragShader;
// 创建program
program = glCreateProgram();
// 加载shader (顶点着色器, 片段着色器)
*shader = glCreateShader(type);
glShaderSource(*shader, 1, &source, NULL);
glCompileShader(*shader);
// 关联着色器到program
glAttachShader(program, vertShader);
glAttachShader(program, fragShader);
// 链接programe
glLinkProgram(program);
// 释放shader资源
glDetachShader(program, vertShader);
glDeleteShader(vertShader);
glDetachShader(program, fragShader);
glDeleteShader(fragShader);
渲染
-
实现
GLKViewDelegate代理方法- 该方法会被定时调用默认是
1s/60帧
- 该方法会被定时调用默认是
设置清屏颜色
#pragma mark - GLKViewDelegate
- (void)update {
}
- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect {
// 设置清屏颜色, 并填充到colorBuffer
glClearColor(0.0, 0.0, 1.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
}
全流程已经实现, 运行即可.
题外话, 如果对上述一些关键名字不甚理解的话, 也无妨; 大概有个影响即可;
毕竟后面见多了, 也就熟悉了.
Others
除了使用GLKViewController, 是否还有其他方法?
当然, 还可以直接使用UIViewController, 重写- (void)loadView方法, 返回GLKView.
- (void)loadView {
self.view = [[GLKView alloc] initWithFrame:[UIScreen mainScreen].bounds];
}
- (void)setupGLKView {
GLKView *glView = (GLKView *)self.view;
// 此时需设置代理, 如果是`GLKViewController`已经默认设置代理为本身, 如`UITablviewController`
glView.delegate = self;
glView.context = self.context;
}
// 其他和使用`GLKViewController`一致
-
此时发现代理方法只会执行一次
- 需创建定时器, 重复调用代理方法
const NSInteger DefaultFramesPerSecond = 30;
// 手动刷新
self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(draw)];
[self.displayLink setPreferredFramesPerSecond:MAX(1, 60.0f / DefaultFramesPerSecond)];
[self.displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
- (void)draw {
GLKView *glView = (GLKView *)self.view;
[self update];
// 不能手动调用`- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect`; 需调用`display`
[glView display];
}
一层层往下扒的话, 发现真正用来渲染的是CAEAGLLayer;
GLKView对CAEAGLLayer做了一层封装;
GLKViewController对GLKView做了一层封装, 设置自身为GLKView代理;
苹果到底对GLKView做什么?
首先是GLKView的layer是CAEAGLLayer
+ (Class)layerClass {
// OpenGL在iOS中,只能在CAEAGLLayer类型的layer上绘制内容
return [CAEAGLLayer class];
}
// 设置layer的属性
- (void)setupLayer{
_eaglLayer = (CAEAGLLayer *)self.layer;
// 设置layer为不透明, 其默认为透明
_eaglLayer.opaque = YES;
// 设置绘制属性
_eaglLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:NO],
kEAGLDrawablePropertyRetainedBacking,
kEAGLColorFormatRGBA8,
kEAGLDrawablePropertyColorFormat,
nil];
}
- 创建一个
FrameBuffer, 并关联ColorBuffer
// 设置渲染buffer, RenderBuffer分为三大模块: colorBuffer, depthBuffer, stencilBuffer
- (void)setupRenderBuffer {
// 创建1个buffer, 返回的id不为0, 0为系统保留
glGenRenderbuffers(1, &_colorBuffer);
// 绑定buffer为当前渲染缓冲对象
glBindRenderbuffer(GL_RENDERBUFFER, _colorBuffer);
// 为_colorBuffer分配存储空间
[self.context renderbufferStorage:GL_RENDERBUFFER fromDrawable:_eaglLayer];
}
// 创建帧缓存对象(framebuffer object), FBO
- (void)setupFrameBuffer {
// 创建framebuffer
glGenFramebuffers(1, &_frameBuffer);
glBindFramebuffer(GL_FRAMEBUFFER, _frameBuffer);
// 将_colorBuffer到装配到FBO的GL_COLOR_ATTACHMENT0装配点上
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, _colorBuffer);
}
-
渲染
- 将当前
context指定的colorBuffer渲染到屏幕
- 将当前
- (void)renderTest {
// 设置清屏颜色R, G, B, A
glClearColor(0.0, 1.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
// 将context中指定的buffer渲染到屏幕上
[self.context presentRenderbuffer:GL_RENDERBUFFER];
}
- 运行结果

目前OpnGL状况
在WWDC 18已经把OpnGL 相关API标记为弃用; 说不定什么时候就被移除掉了, 苹果正大力推荐自己的Metal;
前段时间, 看过Metal相关知识, 发现有OpenGL经验能较好上手, 而且无论是Directx11还是OpenGL, Metal图像渲染流程, 数学运用手段都是一样的;
为甚么还要学习OpenGL?
如Swift一般, OpenGL API什么时候才会真正被弃用?
市面上OpenGL学习书籍以及论坛相比于Metal更丰富, 完善以及面向新手.
对于红宝书OpenGL编程指南, 蓝宝书OpenGL超级宝典;
蓝宝书虽然事无巨细,但更面向新手;
而红宝书默认你熟悉整个渲染管线流程;
后续会陆续发布其他文章, 如果有错误, 还请指正;