教程
OpenGLES入门教程1-Tutorial01-GLKit
OpenGLES入门教程2-Tutorial02-shader入门
OpenGLES入门教程3-Tutorial03-三维变换
OpenGLES入门教程4-Tutorial04-GLKit进阶
OpenGLES进阶教程1-Tutorial05-地球月亮
OpenGLES进阶教程2-Tutorial06-光线
OpenGLES进阶教程3-Tutorial07-粒子效果
这一次的内容是帧缓存。
概要
帧缓存:接收渲染结果的缓冲区叫做帧缓存。
在OpenGL的渲染管道中,几何数据和纹理通过一系列变换和测试后,变成渲染到屏幕上的二维像素。渲染的目标管道就是帧缓存区。
(In OpenGL rendering pipeline, the geometry data and textures are transformed and passed several tests, and then finally rendered onto a screen as 2D pixels. The final rendering destination of the OpenGL pipeline is calledframebuffer)
每一个iOS原生控件都有一个对应的CoreAnimation层。
CoreAnimation合成器使用OpenGL ES来尽可能高效地控制GPU、混合层和切换帧缓存。
思考:OpenGL ES的渲染结果会放到帧缓存区,如何与视图的显示联系起来?
效果展示
核心思路
如下图,帧缓存像素颜色的输出结果在GL_COLOR_ATTATCHMENT
开头的缓存区。
首先,我们用一个纹理缓存来作为OpenGL ES的第一次输出的缓存区,这样我们可以得到一个纹理Texture0。
然后用Texture0作为第二次绘制的纹理,得到最后的结果。
OpenGL的帧缓存
具体细节
- 弯路
在实现过程中,走过了几个弯路,先提出来,希望后来者不要再重复:
- 新建上下文。 因为需要拿到第一次渲染的结果-纹理Texture0,就想尝试新建上下文mExtraContext来渲染纹理,然后用原来的上下文mBaseContext来进行接收纹理和渲染。 结果是两个上下文是相互独立的,mExtraContext渲染出来的结果并不能在mBaseContext使用。
- 使用同一个GLKBaseEffect来渲染纹理Texture0和渲染最后结果。
- 在渲染纹理Texture0的时候使用不同的视口大小,但是没有调用glviewport()。
- 把纹理对象关联到帧缓存
1、新建纹理
2、设置纹理格式
3、分配纹理内存
4、新建帧缓存
5、切换帧缓存为纹理对象
代码语言:javascript复制GLuint colorTexture;
// 1
glGenTextures(1, &colorTexture);
glBindTexture(GL_TEXTURE_2D, colorTexture);
// 2
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,
GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,
GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
GL_LINEAR_MIPMAP_LINEAR);
// 3
glTexImage2D(GL_TEXTURE_2D,
0,
GL_RGBA,
fboWidth,
fboHeight,
0,
GL_RGBA,
GL_UNSIGNED_BYTE,
NULL);
//4
glGenFramebuffers(1, &fboName);
glBindFramebuffer(GL_FRAMEBUFFER, fboName);
//5
glFramebufferTexture2D(GL_FRAMEBUFFER,
GL_COLOR_ATTACHMENT0,
GL_TEXTURE_2D, colorTexture, 0);
- 渲染缓存关联到帧缓存
1、新建渲染缓存
2、分配渲染内存
3、新建帧缓存
4、切换帧缓存为渲染缓存
代码语言:javascript复制//1
glGenRenderbuffers(1, &colorRenderbuffer);
glBindRenderbuffer(GL_RENDERBUFFER, colorRenderbuffer);
//2
glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8_OES, viewport[2],viewport[3]);
//3
glGenFramebuffers(1, &framebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
//4
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorRenderbuffer);
结果解析
先渲染里面锥体,得到的结果作为纹理,进行第二次绘制。观察简化版,下图正方形白色区域为渲染后的纹理。
Paste_Image.png
原图如下。被渲染到一个纹理后,再被显示到屏幕上。
思考
答案:CAEGLayer
OpenGL ES会有连接到层,与层分享数据的帧缓存,至少包括一个像素颜色渲染缓存。
CAEAGLLyaer是CoreAnimation提供的标准层类之一,与OpenGL ES的帧缓存共享它的像素颜色仓库。
与一个Core Animation共享内存的像素颜色渲染缓存在层调整大小时会自动调整大小。其他缓存,例如深度缓存,不会自动调整大小。可以在layoutSubviews方法里面删除现存的深度缓存,并创建一个新的与像素颜色渲染缓存的新尺寸相匹配的深度缓存。
总结
这个demo不难,但是很考验对帧缓存的理解。上面的弯路还有包括多个顶点数组、GLKBaseEffect和shader混用等,本来是打算用shader来实现,但是iOS卷 推荐熟练使用GLKBaseEffect,最后还是用的GLKBaseEffect。
学习OpenGL ES对了解iOS的性能优化很有帮助。
现在再看上面那个图,会有不一样的认知。
参考帧缓存
这里有源码