关于如何从头开始创建环境,可以参考大神的博文OpenGL ES 3.0 数据可视化 0:Hello world,本文只是补充一些我在实践中的一些思考。
CAEAGLLayer
If you plan to use OpenGL for your rendering, use this class as the backing layer for your views by returning it from your view’s layerClass class method. The returned CAEAGLLayer object is a wrapper for a Core Animation surface that is fully compatible with OpenGL ES function calls.
CAEAGLLayer
根据官方文档的说明,这个layer用于OpenGL与Core Animation库之间的联系。这个layer的内容来自于一个 renderbuffer,而他自己所做的主要工作就是为renderbuffer分配内存,在用户绘制完成后讲renderbuffer送给Core Animation.使用的方法大家都知道,就是override view的layerClass静态方法,返回这个东西。
创建Framebuffer和Renderbuffer
创建这俩buffer相对容易理解,这里没有GLKViewController来替我们创建所需的OpenGL环境所以我们需要自己创建用与绘制的buffer,没有这俩buffer,相当于没有画板。我们用OpenGL做Render to texture这样的事情的时候也需要自己创建framebuffer object,但是那时候往往不用renderbuffer,而使用texture。这两者的区别是这样的,在过去那些美好时光里纹理是framebuffer附件的唯一可用的类型,后来引进的renderbuffer object,那么相比较texture,Renderbuffer的优点是,以OpenGL原生渲染格式储存它的数据,因此在离屏渲染的时候,这些数据就相当于被优化过的了。
渲染缓冲对象将所有渲染数据直接储存到它们的缓冲里,而不会进行针对特定纹理格式的任何转换,这样它们就成了一种快速可写的存储介质了。然而,渲染缓冲对象通常是只写的,不能修改它们(就像获取纹理,不能写入纹理一样)。可以用glReadPixels函数去读取,函数返回一个当前绑定的帧缓冲的特定像素区域,而不是直接返回附件本身。
因为它们的数据已经是原生格式了,在写入或把它们的数据简单地到其他缓冲的时候非常快。当使用渲染缓冲对象时,像切换缓冲这种操作变得异常高速。我们在每个渲染迭代末尾使用的那个glfwSwapBuffers函数,同样以渲染缓冲对象实现:我们简单地写入到一个渲染缓冲图像,最后交换到另一个里。渲染缓冲对象对于这种操作来说很完美。
Renderbuffer
有些离题,回到iOS这边,示例代码如下
GLuint colorRenderbuffer;
glGenRenderbuffers(1, &colorRenderbuffer);
glBindRenderbuffer(GL_RENDERBUFFER, colorRenderbuffer);
[myContext renderbufferStorage:GL_RENDERBUFFER fromDrawable:myEAGLLayer];
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorRenderbuffer);
该说明的地方大神的博文已经说明此处不再赘述,但是需要留心的事情是,因为renderbuffer的尺寸是从EAGLLayer中得到,如果EGLLayer的尺寸不正确,会导致最终的图像大小不如预期。在apple的 Supporting High-Resolution Screens In Views 这篇文章提到,在高分辨率的设备上渲染OpenGL ES, 如果不做设置,那么出现的图像会变得blockly,应该是说是块状的,就是不太清晰,其建议就是使用较大的scale值。可以这样设置:
eaglLayer.contentsScale = [UIScreen mainScreen].scale;
记住,如果将eagl layer 的scale值设置变大了,那么在glviewport()
的时候要使用相应的成倍数的尺寸。
我自己实践了一下,iPhone7 Plus 上,[UIScreen mainScreen].bounds
得到的尺寸为414x736,设置glviewport为这个尺寸,出来的图像正常。然后如下修改:
eaglLayer.contentsScale = [UIScreen mainScreen].scale;
glViewport(0, 0, (GLsizei) (size.width * scale),
(GLsizei) (size.height * scale));
即同时修改layer和viewport,得到的结果粗看起来和原来的差别不大,但是仔细查看细节边缘,前者的锯齿会更明显,后者分辨率更好。所以总的思路就是layer的scale和viewport要保持同步,两个都不改也可,两个都改也可。如果只修改了viewport的值,将其增大了,那就相当于视口变大了,底下的renderbuffer还是那么小,renderbuffer只能留下窗口的一个角落的图像。看起来就是,图像放的很大,然后只能看见角落里面的部分,一看就知道有问题。我们可以用下面的方法来确认renderbuffer的大小对不对。
// Get the renderbuffer size.
GLint width;
GLint height;
glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_WIDTH_OES, &width);
glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_HEIGHT_OES, &height);
好了,就说这么多吧。再见!