OpenGL 1.1 in iOS


Renderer

openGL渲染图像,使用的是自己的context和layer:CAEAGLLayer和EAGLContext,context指向一块内存缓冲区。openGL需要用到两个缓冲区:

/**
	帧缓冲区:渲染单独一帧需要保存的各种数据
	渲染缓冲区:渲染这一帧所在的位置,渲染之后会变成CAEAGLayer的缓冲区
*/
    GLuint viewFramebuffer,viewRenderbuffer;

此外,还有一个深度缓冲区,在渲染3d场景时,物体间有深度关系,假设有两个物体A,B。A在B的前面,那么我们如果先渲染A,再渲染B,那就会导致B显示在A的前面,因此需要一个深度缓冲区,引擎会通过深度缓冲区对物体进行深度排序,但这样会损耗性能,好的办法是,按照深度顺序来渲染object。如果是2d图像,则不需要使用深度缓冲区。

当renderer创建的时候,我们需要通知openGL,我们的缓冲区在哪里:

glGenFramebuffersOES(1, &viewFramebuffer);
glGenRenderbuffersOES(1, &viewRenderbuffer);

glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer);
glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);
glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, viewRenderbuffer);

然后,当要渲染layer的时候,根据layer的width和height确定缓冲去的大小:

glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewFramebuffer);
[self.context renderbufferStorage:GL_RENDERBUFFER_OES fromDrawable:layer];
glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_WIDTH_OES, &backingWidth);
glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_HEIGHT_OES, &backingHeight);
  

Coordinates

  • view port

初始化view port一般是这样一句代码:

glViewport(0, 0, backingWidth, backingHeight);

我理解view port的坐标系和Quartz的坐标系一致,左下角为原点。一般来说,view port的坐标系不重要,覆盖整个openGL绘制的区域即可。

然后,我们来决定是绘制2D还是3D。

  • projection

无论绘制2D还是3D都需要对object进行投影,得到他们在当前layer中的位置:

glMatrixMode(GL_PROJECTION);
glLoadIdentity();

openGL是基于矩阵运算,是一种有状态api,某一个时刻只能操作一种矩阵,所以在操作矩阵前,需要通过glMatrixMode指定当前操作的是什么矩阵。

  • orth(正交)

如果是绘制2D图像,投影矩阵通常为正交阵:

glOrthof(-1.0f, 1.0f, -1.5f, 1.5f, -1.0f, 1.0f);

改怎么理解这个正交阵呢?

Alt text

如果是2D图像,那我们眼睛的位置为Z轴正方向,正交投影就是投在x,y平面上的面积。 那数值是什么意思呢?

openGL的空间坐标系是屏幕中心为(0,0),上下左右各位单位向量的正方体,也就是说它的坐标范围在(-1,-1,-1)到(1,1,1)。 假如我们现在有个立方体,它的空间坐标为:

const GLfloat squareVertices[] = 
{
        -0.75f, -0.75f,
        0.75f,  -0.75f,
        -0.75f,  0.75f,
        0.75f,   0.75f,
};

得到的投影为左图,然后我们修改投影立方体为:glOrthof(-2.0f, 2.0f, -2f, 2f, -1.0f, 1.0f),这个立方体仍然包含openGL单位立方体,得到的图为中间图,最我们再修改投影立方体为:glOrthof(0.0f, 1.0f, 0.0f, 1.5f, -1.0f, 1.0f);这个时候,我们的投影立方体已经不能完全包含openGL的单位立方体,x轴,y轴各少了一半,得到的图为右图:

  • 透视视角

如果绘制3D图像,通常用视椎体:

glFrustumf(-1.0f, 1.0f, -1.0f, 1.0f, 5.0f, 10.0f);

是椎体是一种人眼观察物体的透视视角,和正交投影相比有远近的感觉,假如我们把上面的立方体,沿Y轴旋转45度,分别用正交视角和透视角,观察如下:

对于正交投影没有远近之分,对于透视投影有远近的感觉

Drawing Geometry

在确定了openGL的投影矩阵后,绘制object就很简单了,首先定义好object的顶点位置,和颜色分布,然后调用openGL的api绘制就可以了:

 glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
 glClear(GL_COLOR_BUFFER_BIT);
 glVertexPointer(2, GL_FLOAT, 0, squareVertices);
 glEnableClientState(GL_VERTEX_ARRAY);
 glColorPointer(4, GL_UNSIGNED_BYTE, 0, squareColors);
 glEnableClientState(GL_COLOR_ARRAY); 
 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);   
 glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);
 [self.context presentRenderbuffer:GL_RENDERBUFFER_OES];

(全文完)

Further Reading: