一、什麼是原語?
原語就是可以用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 6GLfloat vertices[] = { … }; // (x, y, z) per vertexglEnableVertexAttribArray(VERTEX_POS_INDX);glVertexAttribPointer(VERTEX_POS_INDX, 3, GL_FLOAT, GL_FALSE,0, vertices);for (i=0; i<NUM_FACES; i++){ glDrawArrays(GL_TRIANGLE_FAN, first, 4); first += 4;}//or glDrawArrays(GL_TRIANGLES, 0, 36);
註:總共8個頂點,卻儲存了24個頂點或36個頂點的資料。
2)使用glDrawElements的代碼如下:
#define VERTEX_POS_INDX 0GLfloat vertices[] = { … };// (x, y, z) per vertexGLubyte 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掉了。