转自:http://blog.csdn.net/myarrow/article/details/7747733
一、什么是原语?
原语就是可以用glDrawArrays和glDrawElements来进行画图的几何对象。原语由一系列顶点来描述,每个顶点包含位置、颜色、法线和纹理坐标。
原语包括:点、线、三角行。
二、原语类型
1. 三角形原语类型
1)GL_TRIANGLES:三角形顶点互不重用,如顶点{V0,V1,V2,V3,V4,V5},则描述了2个三角形,三角形顶点分别为:(V0,V1,V2)和(V3,V4,V5)。
2)GL_TRIANGLE_STRIP:三角形顶点被重用,画一系列连接的三角形,三角形的最后2个顶点为下一个三角形的前面2个顶点。如顶点{V0,V1,V2,V3,V4},则描述了3个三角形,三角形顶点分别为:(V0,V1,V2),(V2,V1,V3)<注意此顺序,应该为逆时针方向> 和 (V2,V3,V4)。
3)GL_TRIANGLE_FAN:三角形顶点被重用,画一系列连接的三角形,前面三角形的第1和3个顶点,为后面三角形的第1和2个顶点。如顶点{V0,V1,V2,V3,V4},则描述了3个三角形,三角形顶点分别为:(V0,V1,V2), (V0,V2,V3)和(V0,V3,V4)。
2. 线原语类型
1)GL_LINES:线的顶点互不重用。如顶点{V0,V1,V2,V3,V4,V5},则描述了3条线,线顶点分别为:(V0,V1),(V2,V3)和 (V4,V5)。
2)GL_LINE_STRIP:线的顶点被重用,前面线的最后一个顶点为下一条线的第一个顶点。如顶点{V0,V1,V2,V3},则描述了3条线,线顶点分别为:(V0,V1),(V1,V2)和 (V2,V3)。
3)GL_LINE_LOOP:线的顶点被重用,与GL_LINE_STRIP类似,只是最后一条线的最后一个顶点与第一条线的第一个顶点相连。如顶点{V0,V1,V2,V3,V4},则描述了5条线,线顶点分别为:(V0,V1), (V1,V2), (V2,V3), (V3,V4)和 (V4,V0)。
线的宽度可以通过函数“void glLineWidth(GLfloat width)”来设置。
3. 顶点原语
GL_POINTS:对每个顶点进行画图。
注:窗口坐标(0,0)位于窗口左下角。而点的坐标(0,0)位于窗口左上角。
gl_PointSize是Vertex Shader中的一个内嵌变量,gl_PointCoord是Fragment Shader中的一个内嵌变量,其值取值范围为:[0.0,1.0],并且从左到右或从上到下地增加。
三、画图原语
OpenGL ES 2.0有以下两个画图原语:
1)glDrawArrays
2)glDrawElements.
1. glDrawArrays
void glDrawArrays(GLenum mode, GLint first, GLsizei count)
• mode:指定要画的原语类型,有效值如下:
GL_POINTS
GL_LINES
GL_LINE_STRIP
GL_LINE_LOOP
GL_TRIANGLES
GL_TRIANGLE_STRIP
GL_TRIANGLE_FAN
• first:起始顶点在enabled vertex arrays中的索引
• count:将被用于画图的顶点个数
举例:glDrawArrays(GL_TRIANGLES, 0, 6)画了两个三角形,顶点的顶点数组索引分别为:(0, 1, 2)和(3, 4, 5)。
2. glDrawElements
void glDrawElements(GLenum mode, GLsizei count,GLenum type, const GLvoid *indices)
• mode:指定要画的原语类型,有效值如下:
GL_POINTS
GL_LINES
GL_LINE_STRIP
GL_LINE_LOOP
GL_TRIANGLES
GL_TRIANGLE_STRIP
GL_TRIANGLE_FAN
• count:指示在*indices中的顶点索引个数
• type:指示在*indices中元素数据类型,有效值如下:
GL_UNSIGNED_BYTE
GL_UNSIGNED_SHORT
GL_UNSIGNED_INT(可选,只有OES_element_index_uint扩展被实现时,才可使用)
• indices:存储顶点索引的数组或指针
3. glDrawArrays &glDrawElements各自用武之地
如果Enbaled Vertex Array中的顶点不被多个原语所共享,则使用glDrawArrays较高效,因为省去了顶点索引数组;否则使用glDrawElements高效,因为同一个顶点在Enabled Vertex Array中只有一份数据,不存在多个数据copy,节省了内存空间。
举例如下(画一个立方体):
1) 使用glDrawArrays的代码如下:
- #define VERTEX_POS_INDX 0
- #define NUM_FACES 6
- GLfloat vertices[] = { … }; // (x, y, z) per vertex
- glEnableVertexAttribArray(VERTEX_POS_INDX);
- glVertexAttribPointer(VERTEX_POS_INDX, 3, GL_FLOAT, GL_FALSE,0, vertices);
- for (i=0; i
- {
- glDrawArrays(GL_TRIANGLE_FAN, first, 4);
- first += 4;
- }
- //or glDrawArrays(GL_TRIANGLES, 0, 36);
注:总共8个顶点,却保存了24个顶点或36个顶点的数据。
2)使用glDrawElements的代码如下:
- #define VERTEX_POS_INDX 0
- GLfloat vertices[] = { … };// (x, y, z) per vertex
- GLubyte indices[36] = { 0, 1, 2, 0, 2, 3,
- 0, 3, 4, 0, 4, 5,
- 0, 5, 6, 0, 6, 1,
- 7, 6, 1, 7, 1, 2,
- 7, 4, 5, 7, 5, 6,
- 7, 2, 3, 7, 3, 4 };
- glEnableVertexAttribArray(VERTEX_POS_INDX);
- glVertexAttribPointer(VERTEX_POS_INDX, 3, GL_FLOAT, GL_FALSE,0, vertices);
- glDrawElements(GL_TRIANGLES, sizeof(indices)/sizeof(GLubyte),GL_UNSIGNED_BYTE, indices);
4. 总结
只要有顶点重用,使用glDrawElements 的性能比glDrawArrays要好,因为不仅CPU与GPU之间传递数据的内存带宽更小,且在GPU中占用的内存也更少。
四、原语组装(Primitive Assembly)
OpenGL ES 2.0原语组装阶段如下图所示:
1. 坐标系统
2. 裁剪体(clip volume)
裁剪体由近、远、左、右、上、下共6个裁剪面组成。如下图所示:
在clipping阶段,每个原语都被裁剪体进行裁剪。并对每种原语执行如下操作来进行裁剪:
1)Clipping triangles
• 如果全部在裁剪体内,什么都不做
• 如果全部不在裁剪体内,则丢弃
• 如果部分在裁剪体内,则产生新的顶点并形成三角形FAN
2)Clipping lines
• 如果全部在裁剪体内,什么都不做
• 如果全部不在裁剪体内,则丢弃
• 如果部分在裁剪体内,则产生新的顶点并形成新的LINE
3)Clipping point sprites
• 如果全部在裁剪体内,什么都不做
• 如果全部不在裁剪体内,则丢弃
3. 透视除法(PerspectiveDivision)
它的目标是把裁剪坐标变为归一化设备坐标,其取值范围为:[-1,1]。其实现方法为把坐标的每个分量除以Wc。裁剪坐标(Xc,Yc,Zc,Wc) 经过透视除法(Xc/Wc),(Yc/Wc),(Zc/Wc)则变成了归一化设备坐标(Xd,Yd,Zd)。
4. 视口变换(Viewport Transformation)
它把归一化设备坐标(Xd,Yd,Zd)变换为窗口坐标或屏幕坐标(Xw,Yw,Zw)。
1)视口变换视口通过以下API设置:
void glViewport(GLint x, GLint y, GLsizei w, GLsizei h)
• x, y:指明视口左下角屏幕坐标(以像素为单位)
• w, h:指明视口的宽度和高度(以像素为单位)
2)视口变换深度通过以下API设置:
void glDepthRange(GLclampf n, GLclampf f)
• n, f:指明要求的深度范围。默认值:n=0.0,f=1.0,值的范围为[0.0,1.0]。
3)视口变换方法为:
Ox = (x + w)/2,Oy = (y + h)/2,n 和 f 表示要求的深度范围。
五、光栅化(Rasterization)
光栅化获取每个原语(如:点、线、三角形),然后为这个原语产生合适的Fragment。一个Fragment表示在屏幕空间中的一个像素位置(x,y),且fragment数据将被Fragment Shader处理以产生一个Fragment Color。
六、选择(Culling)
在三角形被光栅化之前,我需要确定哪些面对观察者,哪些面背对观察者。Culling操作丢弃哪些背对观察者的面,从而光栅化这些看不见的三角形,从而节约了GPU的时间,并提高了GPU的性能。相关函数如下:
1)void glFrontFace(GLenum dir)
• dir:指示面对观察者的三角形的方向(GL_CW<顺时针>或GL_CCW<逆时针>),默认值为:GL_CCW。
CW: Clockwise,CCW:Counter-Clockwise
2)void glCullFace(GLenum mode)
• mode:指示三角形的哪个面被选择(GL_FRONT、GL_BACK、GL_FRONT_AND_BACK),默认值是GL_BACK。
3)void glEnable(GLenum cap)
void glDisable(GLenum cap)
• cap:设置为GL_CULL_FACE,初始时,Culling被disabled掉了。