前言
开发OpenGL App
一年多了, 平时就是在各种赶项目, 赶功能; 做了不少笔记, 踩了不少坑, 但是仅仅是工作需要就去学习哪块, 很少静下来做总结;
今年年春换了一家东家, 开发直播类App
, 需要视频处理等知识, 正好总结一番;
目前发现,在OpenGL
的世界里 相对于渲染管线
来说, 数学
才是真正的大头;
相比于复杂的理论知识, 我们直接手上吧.
-
本文环境
Xcode 9.0
OpenGLES 3.0
基本操作步骤
导入
GLKit
框架直接集成
GLKViewController
, 或者GLKView
创建
OpenGL上下文
EAGLContext
-
创建程序对象
ShaderProgram
- 连接
顶点着色器
和片段着色器
- 连接
指定清屏颜色,填充到
ColorBuffer
导入框架, 直接集成与GLKViewController
#import
@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超级宝典
;
蓝宝书虽然事无巨细
,但更面向新手;
而红宝书默认你熟悉整个渲染管线流程
;
后续会陆续发布其他文章, 如果有错误, 还请指正;