Win32 OpenGL編程(5) 頂點數組詳細介紹(轉載)
write by 九天雁翎(JTianLing) -- blog.csdn.net/vagrxie
提要
本文主要講解了OpenGL中使用頂點數組來繪制圖形的需求及方法,
需求
按照前面已經講過的內容來說,簡單的2D圖形繪制已經沒有問題了,並且,很多東西其實已經涉及到3D,但是會發現,復雜圖形的繪制時,glBegin和 glEnd對中指定頂點的函數調用會非常多,glVertex函數調用的開銷會過大。(雖然在C/C++中函數調用開銷其實已經比較小)在3D圖形繪制 時,此問題更加嚴重,想想,一個四邊形,4個頂點,一個四方體,就有8個頂點了,但是按照我們目前描述平面的方式來描述就是需要指定6 * 4 = 24個頂點。這正是OpenGL作為高性能圖像接口應該極力避免的事情,事實上,OpenGL當然不會一直要求我們用如此低效的方式來完成任務,我們有新 的高效的工具——頂點數組。事實上,在強調高效和盡量減少API的OpenGL ES中,甚至根本就沒有glBegin,glEnd和glVertex*等API。
頂點數組相對來說是OpenGL中用於提高效率的手段,不是核心概念。(雖然OpenGL ES中完全以頂點數組繪制圖元,但是事實上OpenGL 1.1版中才加入了定點數組)頂點數組不能使我們繪制出以前以前繪制不了的圖元,但是卻實實在在的提高了我們繪制圖形的效率,但是事實上,對於高性能圖像 處理來說,提高效率是如此的重要,因為效率的提升從本質上來說沒有增加我們繪制更多圖形的能力(即不用提高效率手段也能繪制出相同圖形),但是,隨着效率 的提高,我們卻能在同樣的機器上繪制更多的圖元,那么,意味着我們可以更加豐富和逼真地表現圖像,這不正是圖形繪制所追求的目標嗎?
概念
有的東西說簡單就簡單,說復雜就復雜,頂點數組的概念如此的簡單,如其名,就是將原來由一系列的glVertex*指定的頂點放在一個數組中,(其意思就 是頂點數據組成的數組)由頂點數組處理函數一次性指定。事實上,不僅如此,其一次性指定的不僅僅是數組,還可以是其他系列的東西,比如表面發現,RGBA 顏色,輔助顏色等。
使用方式初步
使用方式,編程手段的使用方式最佳講解手段肯定是程序,我們以一個例子來講解使用方式。
glVertexArray:
1:
//OpenGL初始化開始
2:
void SceneInit(int w,int h)
3:
{
4:
glClearColor(0.0, 0.0, 0.0, 0.0);
5:
// 啟用頂點數組
6:
glEnableClientState(GL_VERTEX_ARRAY);
7:
8:
// 頂點數組數據
9:
static GLfloat fVertices[] = { -0.5, -0.5,
10:
0.5, -0.5,
11:
0.5, 0.5,
12:
-0.5, 0.5};
13:
14:
glVertexPointer(2, GL_FLOAT, 0, fVertices);
15:
}
16:
17:
//這里進行所有的繪圖工作
18:
void SceneShow(GLvoid)
19:
{
20:
glClear(GL_COLOR_BUFFER_BIT); // 清空顏色緩沖區
21:
glColor4f(1.0, 1.0, 1.0, 1.0);
22:
23:
glBegin(GL_QUADS);
24:
glArrayElement(0);
25:
glArrayElement(1);
26:
glArrayElement(2);
27:
glArrayElement(3);
28:
glEnd();
29:
30:
// 上述函數調用與下面的效果一樣
31:
//glBegin(GL_QUADS);
32:
//glVertex3f(-0.5, -0.5, 0.0);
33:
//glVertex3f(0.5, -0.5, 0.0);
34:
//glVertex3f(0.5, 0.5, 0.0);
35:
//glVertex3f(-0.5, 0.5, 0.0);
36:
//glEnd();
37:
38:
glFlush();
39:
}
為節省篇幅僅貼出關鍵片段,完整源代碼見我博客源代碼的2009-10-18/glVertexArray/ 目錄,獲取方式見文章最后關於獲取博客完整源代碼的說明。
上述例子的效果與以前SimpleRectangle的效果完全一樣,請參考本OpenGL系列文章3,4,(以下簡稱XO3,XO4^^),截圖如以前一樣。
分步驟說明一下上述源代碼的含義:
glEnableClientState(GL_VERTEX_ARRAY);
用於表示啟用頂點數組,此函數還可以搭配其他參數用於指定其他數組
glEnableClientState — enable or disable client-side capability
void glEnableClientState( GLenum cap);
Parameters:Specifies the capability to enable.
Symbolic constants
GL_COLOR_ARRAY,
GL_EDGE_FLAG_ARRAY,
GL_FOG_COORD_ARRAY,
GL_INDEX_ARRAY,
GL_NORMAL_ARRAY,
GL_SECONDARY_COLOR_ARRAY,
GL_TEXTURE_COORD_ARRAY, and
GL_VERTEX_ARRAY
are accepted.
此例中僅僅使用了GL_VERTEX_ARRAY一種用法。
glVertexPointer也是關鍵函數,用於指定頂點數組的數據。
glVertexPointer — define an array of vertex data
C Specification
void glVertexPointer( GLint size,
GLenum type,
GLsizei stride,
const GLvoid * pointer);
Parameters
size
Specifies the number of coordinates per vertex. Must be 2, 3, or
4. The initial value is 4.
typeSpecifies the data type of each coordinate in the array.
Symbolic constants
GL_SHORT,
GL_INT,
GL_FLOAT,
or GL_DOUBLE
are accepted. The initial value is GL_FLOAT.
strideSpecifies the byte offset between consecutive
vertices. If stride is 0, the vertices are understood to be tightly packed in
the array. The initial value
is 0.
pointerSpecifies a pointer to the first coordinate of the first vertex in the
array. The initial value is 0.
此例中,數據以2個位一組,類型都是GL_FLOAT,跨度為0表示數據緊密排列,指針自然指向剛才分配的頂點數組數據。
glArrayElement用於指定頂點數組函數具體的頂點,放在glBegin與glEnd之間,產生glVertex*一樣的效果,即一次指定一個頂點。
glArrayElement — render a vertex using the specified vertex array element
C Specification
void glArrayElement( GLint i);
Parametersi
Specifies an index into the enabled vertex data arrays.
上例中分別制定了0,1,2,3共4個頂點,與以前在SimpleRectangle中指定的4個頂點一樣,注意取數據的方式,我們上面通過 glVertexPointer制定數據的時候說明了是2個數據一組,(如同縮進所示)那么這里指定的0,1,2,3其實分別是從數組的0,2,4,6個 數據開始取,每次取2個,意思就是此處glArrayElement指定的並不是頂點數組數據實際對應的C語言數組索引而是按照 glVertexPointer指定分組分組后的分組索引。
附帶顏色的使用
上述內容就已經包含了定點數組的核心內容,但是我們無法發現使用頂點數組的任何好處,甚至發現更加復雜,函數調用更多了。-_-!下面通過一個附帶顏色的例子,加深理解頂點數組使用方式,並可以發現此時其初步減少函數調用次數的作用。
1:
//OpenGL初始化開始
2:
void SceneInit(int w,int h)
3:
{
4:
glClearColor(0.0, 0.0, 0.0, 0.0);
5:
// 啟用頂點數組
6:
glEnableClientState(GL_VERTEX_ARRAY);
7:
8:
// 顏色數組也需要啟用
9:
glEnableClientState(GL_COLOR_ARRAY);
10:
11:
// 默認就是此參數,可忽略,為了明確說明特意指定
12:
glShadeModel(GL_SHADE_MODEL);
13:
14:
// 頂點數組數據
15:
static GLfloat fVertices[] = { -0.5, -0.5,
16:
0.5, -0.5,
17:
0.5, 0.5,
18:
-0.5, 0.5};
19:
20:
// 顏色數組
21:
static GLfloat fColor[] = { 1.0, 0.0, 0.0,
22:
0.0, 1.0, 0.0,
23:
0.0, 0.0, 1.0,
24:
1.0, 1.0, 1.0};
25:
26:
// 指定頂點數組數據
27:
glVertexPointer(2, GL_FLOAT, 0, fVertices);
28:
29:
// 制定顏色數組
30:
glColorPointer(3, GL_FLOAT, 0, fColor);
31:
32:
}
33:
34:
//這里進行所有的繪圖工作
35:
void SceneShow(GLvoid)
36:
{
37:
glClear(GL_COLOR_BUFFER_BIT); // 清空顏色緩沖區
38:
39:
glPushMatrix();
40:
glTranslatef(-0.5, 0, 0);
41:
glBegin(GL_QUADS);
42:
glArrayElement(0);
43:
glArrayElement(1);
44:
glArrayElement(2);
45:
glArrayElement(3);
46:
glEnd();
47:
glPopMatrix();
48:
49:
// 上述函數調用與下面的效果一樣
50:
glPushMatrix();
51:
glTranslatef(0.5, 0.0, 0.0);
52:
glBegin(GL_QUADS);
53:
glColor3f(1.0, 0.0, 0.0);
54:
glVertex3f(-0.5, -0.5, 0.0);
55:
56:
glColor3f(0.0, 1.0, 0.0);
57:
glVertex3f(0.5, -0.5, 0.0);
58:
59:
glColor3f(0.0, 0.0, 1.0);
60:
glVertex3f(0.5, 0.5, 0.0);
61:
62:
glColor3f(1.0, 1.0, 1.0);
63:
glVertex3f(-0.5, 0.5, 0.0);
64:
glEnd();
65:
glPopMatrix();
66:
67:
glFlush();
68:
}
為節省篇幅僅貼出關鍵片段,完整源代碼見我博客源代碼的2009-10-18/glVertexArrayWithColor/ 目錄,獲取方式見文章最后關於獲取博客完整源代碼的說明。
注 釋已經詳細說明了大部分新添內容的意義,此例子為加強對比,將用頂點數組繪制的圖形移到了屏幕的左側,而用glVertex*繪制的圖形放在了屏幕的右 側。同樣的效果,在此例中由於glArrayElement同時指定了頂點和顏色,已經顯示出了節省函數調用的作用,何況頂點數組還可以同時指定輔助顏 色,法線向量,霧坐標等等東西,此示例程序運行效果如下圖所示。
更進一步,強大的glDrawElements
glArrayElement的作用我們已經看到了,但是對於的數據仍然一個一個通過glArrayElement指定也不符合OpenGL高效的性格特 點,OpenGL提供了一系列更加高效的函數以完成對效率要求苛刻的任務,我們將逐一介紹,會發現,使用難度越來越高,適用范圍越來越窄,但是函數調用越 來越少,功能越來越強大,事實上,函數調用少並不是OpenGL這樣設計的唯一理由,越是這樣同時處理多個數據的接口,因為一個接口掌握的信息越多,那么 也就越能更多的對其進行優化,此是題外話。
glDrawElements是首先要講到的。
glDrawElements — render primitives from array data
C Specification
void glDrawElements( GLenum mode,
GLsizei count,
GLenum type,
const GLvoid * indices);
Parametersmode
Specifies what kind of primitives to render.
Symbolic constants
GL_POINTS,
GL_LINE_STRIP,
GL_LINE_LOOP,
GL_LINES,
GL_TRIANGLE_STRIP,
GL_TRIANGLE_FAN,
GL_TRIANGLES,
GL_QUAD_STRIP,
GL_QUADS,
and GL_POLYGON are accepted.
countSpecifies the number of elements to be rendered.
typeSpecifies the type of the values in indices. Must be one of
GL_UNSIGNED_BYTE, GL_UNSIGNED_SHORT, or
GL_UNSIGNED_INT.
indicesSpecifies a pointer to the location where the indices are stored.
在《OpenGL Programming Guide 》中對其效果的描述為:
1:
glBegin(mode)
2:
for ( i = 0; i < count; i++)
3:
glArrayElement(indices[i]);
4:
glEnd();
理解了glArrayElement,那么理解glDrawElemets也就不難了。甚至因為此函數作用之大,有了它,我們連glBegin,glEnd都不需要了(OpenGL ES 2.0中就將上述函數都省了,只留下了glDrawElements)
下面看一個例子 。
1:
//這里進行所有的繪圖工作
2:
void SceneShow(GLvoid)
3:
{
4:
glClear(GL_COLOR_BUFFER_BIT); // 清空顏色緩沖區
5:
6:
static GLubyte byRectIndices[] = { 0, 1, 2, 3};
7:
glPushMatrix();
8:
glTranslatef(-0.5, 0, 0);
9:
glDrawElements(GL_QUADS, 4, GL_UNSIGNED_BYTE, byRectIndices);
10:
glPopMatrix();
11:
12:
// 上述函數調用與下面的效果一樣
13:
glPushMatrix();
14:
glTranslatef(0.5, 0, 0);
15:
16:
glBegin(GL_QUADS);
17:
glArrayElement(0);
18:
glArrayElement(1);
19:
glArrayElement(2);
20:
glArrayElement(3);
21:
glEnd();
22:
glPopMatrix();
23:
24:
glFlush();
25:
}
為節省篇幅僅貼出關鍵片段,完整源代碼見我博客源代碼的2009-10-18/glRectWithDrawElements/目錄,獲取方式見文章最后關於獲取博客完整源代碼的說明。
看了這個例子以后,真會感嘆強中自有強中手啊。。。。。glArrayElement擊敗了glVertex*,但是glDrawElements又比 glArrayElement更高。glDrawElements使用的時候將一連串glArrayElement需要使用的數據放在一個數組中(此例中 是byRectIndices),然后通過一個函數調用一次指定。注意啊,glDrawElements自帶glBegin及glEnd效果,不再需要它 們。(事實上,在OpenGL中命名中帶Draw的一般都不需要glBegin和glEnd)程序運行的效果與 glVertexArrayWithColor例子運行效果相同。
更強大的glMultiDrawElements
以前有句話說牛逼是無止盡的,OpenGL函數的強大也是一樣。glDrawElements是目前看過的OpenGL函數中最強大的頂點指定函數了,但是glMultiDrawElements如其名,可以一個頂兩->N個glDrawElemetns。
glMultiDrawElements — render multiple sets of primitives by specifying indices of array data elements
C Specification
void glMultiDrawElements( GLenum mode,
const GLsizei * count,
GLenum type,
const GLvoid ** indices,
GLsizei primcount);
Parametersmode
Specifies what kind of primitives to render.
Symbolic constants
GL_POINTS,
GL_LINE_STRIP,
GL_LINE_LOOP,
GL_LINES,
GL_TRIANGLE_STRIP,
GL_TRIANGLE_FAN,
GL_TRIANGLES,
GL_QUAD_STRIP,
GL_QUADS,
and GL_POLYGON are accepted.
countPoints to an array of the elements counts.
typeSpecifies the type of the values in indices. Must be one of
GL_UNSIGNED_BYTE, GL_UNSIGNED_SHORT, or
GL_UNSIGNED_INT.
indicesSpecifies a pointer to the location where the indices are stored.
primcountSpecifies the size of the count array.
在《OpenGL Programming Guide 》中,對其進行的程序語言描述如下:
1:
for (i = 0; i < primcount; i++)
2:
{
3:
if ( count[i] > 0)
4:
glDrawElements(mode, count[i], type, indices[i]);
5:
}
還是看個例子,但是因為上面那個矩形的例子都已經無法承載(^^)glMultiDrawElements的強大了,我們換了個更加復雜一些的例子,用4個三角形拼出來的矩形。頂點數組數據也很簡單,只需要增加一個中心點就行了。
1:
// OpenGL需要的頭文件
2:
#include <GL/glew.h>
3:
#include <GL/wglew.h>
4:
5:
//定義程序鏈接時所需要調用的OpenGL程序庫,簡化工程配置
6:
#pragma comment( lib, "opengl32.lib" )
7:
#pragma comment( lib, "glu32.lib" )
8:
#pragma comment( lib, "glew32.lib" )
9:
#pragma comment( lib, "glew32s.lib" )
10:
11:
//OpenGL初始化開始
12:
void SceneInit(int w,int h)
13:
{
14:
GLenum err = glewInit();
15:
if (err != GLEW_OK)
16:
{
17:
MessageBox(NULL, _T("Error" ), _T("Glew init failed." ), MB_OK);
18:
exit(-1);
19:
}
20:
21:
glClearColor(0.0, 0.0, 0.0, 0.0);
22:
// 啟用頂點數組
23:
glEnableClientState(GL_VERTEX_ARRAY);
24:
25:
// 顏色數組也需要啟用
26:
glEnableClientState(GL_COLOR_ARRAY);
27:
28:
// 默認就是此參數,可忽略,為了明確說明特意指定
29:
glShadeModel(GL_SHADE_MODEL);
30:
31:
// 頂點數組數據
32:
static GLfloat fVertices[] = { -0.5, -0.5,
33:
0.5, -0.5,
34:
0.5, 0.5,
35:
-0.5, 0.5,
36:
0.0, 0.0}; // 添加的原點
37:
38:
// 顏色數組
39:
static GLfloat fColor[] = { 1.0, 0.0, 0.0,
40:
0.0, 1.0, 0.0,
41:
0.0, 0.0, 1.0,
42:
0.0, 0.0, 0.0,
43:
1.0, 1.0, 1.0}; // 原點顏色為白色
44:
45:
// 指定頂點數組數據
46:
glVertexPointer(2, GL_FLOAT, 0, fVertices);
47:
48:
// 制定顏色數組
49:
glColorPointer(3, GL_FLOAT, 0, fColor);
50:
51:
}
52:
53:
//這里進行所有的繪圖工作
54:
void SceneShow(GLvoid)
55:
{
56:
glClear(GL_COLOR_BUFFER_BIT); // 清空顏色緩沖區
57:
58:
static GLubyte byTopIndices[] = { 2, 3, 4};
59:
static GLubyte byLeftIndices[] = { 3, 0, 4};
60:
static GLubyte byBottomIndices[] = { 0, 1, 4};
61:
static GLubyte byRightIndices[] = { 1, 2, 4};
62:
63:
static GLsizei iCounts[] = { 3, 3, 3, 3};
64:
static GLvoid *indices[] = { byTopIndices, byLeftIndices, byBottomIndices, byRightIndices};
65:
glPushMatrix();
66:
glTranslatef(-0.5, 0, 0);
67:
glMultiDrawElements(GL_TRIANGLES, (GLsizei*)iCounts, GL_UNSIGNED_BYTE, (const GLvoid**)indices, 4);
68:
69:
glPopMatrix();
70:
71:
72:
// 上述函數調用與下面的效果一樣
73:
glPushMatrix();
74:
glTranslatef(0.5, 0, 0);
75:
glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_BYTE, byTopIndices);
76:
glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_BYTE, byLeftIndices);
77:
glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_BYTE, byBottomIndices);
78:
glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_BYTE, byRightIndices);
79:
glPopMatrix();
80:
81:
glFlush();
82:
}
為 節省篇幅僅貼出關鍵片段,完整源代碼見我博客源代碼的2009-10-18/glRectWithMultiDrawElements/ 目錄,獲取方式見文章最后關於獲取博客完整源代碼的說明。 運行效果見下圖,由於顏色的搭配加上漸變效果,感覺就像是兩個從上向下看的金字塔^^
為了方便頂點排列順序的理解和上例中參數的含義,我特意畫了張草圖:
此例中首先需要說明的是,因為glMultiDrawElements函數是OpenGL 1.4版本引進的,而Windows僅僅實現了1.1,所以在Windows下必須需要進行額外處理才能使用了,我使用的是glew庫,詳細情況見 XO2,其中詳細說明了怎么獲取Windows中缺失的OpenGL函數。glMultiDrawElements函數僅僅通過最后一個參數(此例中為 4)表示指定4次glDrawElements調用,其他的參數與glDrawElements函數意義一致,僅僅是第2,4參數變成數組形式,這是種非 常有意思又很容易理解和記憶的參數排列方式,也是值得一學的。
序列頂點數組函數
還 是為了優化考慮,在對於排好序的頂點序列而言,可以通過接口指明這一點以允許OpenGL實現進一步的優化,OpenGL中對應的函數為 glDrawArrays和glMultiDrawArrays,分別於上述glDrawElements和glMultiDrawElements函數 對應,使用上由於有頂點數組按順序這個優先條件在,參數更加簡單一些,需在需要額外的索引數組,(前面例子中的Indice類數組)只需要指明起點和數量 就行。
glDrawArrays — render primitives from array data
C Specification
void glDrawArrays( GLenum mode,
GLint first,
GLsizei count);
Parameters
mode
Specifies what kind of primitives to render.
Symbolic constants
GL_POINTS,
GL_LINE_STRIP,
GL_LINE_LOOP,
GL_LINES,
GL_TRIANGLE_STRIP,
GL_TRIANGLE_FAN,
GL_TRIANGLES,
GL_QUAD_STRIP,
GL_QUADS,
and GL_POLYGON are accepted.
firstSpecifies the starting index in the enabled arrays.
countSpecifies the number of indices to be rendered.
glMultiDrawArrays — render multiple sets of primitives from array data
C Specification
void glMultiDrawArrays( GLenum mode,
GLint * first,
GLsizei * count,
GLsizei primcount);
Parametersmode
Specifies what kind of primitives to render.
Symbolic constants
GL_POINTS,
GL_LINE_STRIP,
GL_LINE_LOOP,
GL_LINES,
GL_TRIANGLE_STRIP,
GL_TRIANGLE_FAN,
GL_TRIANGLES,
GL_QUAD_STRIP,
GL_QUADS,
and GL_POLYGON are accepted.
firstPoints to an array of starting indices in the enabled arrays.
countPoints to an array of the number of indices to be rendered.
primcountSpecifies the size of the first and count
下面是個例子
1:
//OpenGL初始化開始
2:
void SceneInit(int w,int h)
3:
{
4:
GLenum err = glewInit();
5:
if (err != GLEW_OK)
6:
{
7:
MessageBox(NULL, _T("Error" ), _T("Glew init failed." ), MB_OK);
8:
exit(-1);
9:
}
10:
11:
glClearColor(0.0, 0.0, 0.0, 0.0);
12:
// 啟用頂點數組
13:
glEnableClientState(GL_VERTEX_ARRAY);
14:
15:
// 顏色數組也需要啟用
16:
glEnableClientState(GL_COLOR_ARRAY);
17:
18:
// 默認就是此參數,可忽略,為了明確說明特意指定
19:
glShadeModel(GL_SHADE_MODEL);
20:
21:
// 頂點數組數據
22:
static GLfloat fVertices[] = { -0.5, -0.5,
23:
0.5, -0.5,
24:
0.5, 0.5,
25:
-0.5, 0.5,
26:
0.0, 0.0, // 添加的原點
27:
-0.5, -0.5}; // 添加的第一個點
28:
29:
// 顏色數組
30:
static GLfloat fColor[] = { 1.0, 0.0, 0.0,
31:
0.0, 1.0, 0.0,
32:
0.0, 0.0, 1.0,
33:
0.0, 0.0, 0.0,
34:
1.0, 1.0, 1.0, // 原點顏色為白色
35:
1.0, 0.0, 0.0}; // 回歸添加第一個點為紅色
36:
37:
// 指定頂點數組數據
38:
glVertexPointer(2, GL_FLOAT, 0, fVertices);
39:
40:
// 制定顏色數組
41:
glColorPointer(3, GL_FLOAT, 0, fColor);
42:
43:
}
44:
45:
//這里進行所有的繪圖工作
46:
void SceneShow(GLvoid)
47:
{
48:
glClear(GL_COLOR_BUFFER_BIT); // 清空顏色緩沖區
49:
50:
static GLubyte byTopIndices[] = { 2, 3, 4};
51:
static GLubyte byLeftIndices[] = { 3, 0, 4};
52:
static GLubyte byBottomIndices[] = { 0, 1, 4};
53:
static GLubyte byRightIndices[] = { 1, 2, 4};
54:
55:
static GLsizei iCounts[] = { 3, 3, 3, 3};
56:
static GLvoid *indices[] = { byTopIndices, byLeftIndices, byBottomIndices, byRightIndices};
57:
58:
static GLint iFirsts[] = { 0, 2, 3};
59:
60:
// 左上角的圖形展示了glDrawArrays的效果,glDrawArray連續的繪制0到4索引指定頂點的圖形
61:
glPushMatrix();
62:
glTranslatef(-0.5, 0.0, 0.0);
63:
glDrawArrays(GL_QUADS, 0, 4);
64:
glPopMatrix();
65:
66:
// 左下角的圖展示了glMultiDrawArrays調用的效果
67:
68:
glPushMatrix();
69:
glTranslatef(0.5, 0.0, 0.0);
70:
glMultiDrawArrays(GL_TRIANGLES, iFirsts, iCounts, 3);
71:
glPopMatrix();
72:
73:
glFlush();
74:
}
為節省篇幅僅貼出關鍵片段,完整源代碼見我博客源代碼的2009-10-18/glRectWithArrayDraw 目錄,獲取方式見文章最后關於獲取博客完整源代碼的說明。
按 上面例子中的頂點排序,我們沒有辦法再組織處一個完整的矩形了,這里我通過{0,1,2},{2,3,4}繪制出了3/4,然后補上了一個第一個點坐標通 過繪制{3,4,5},完成完整的矩形。記住array函數的按順序特點就很好理解了。程序運行效果如下圖,注意實現的不同導致與前面例子中的效果差異。
范圍限定頂點數組函數
事 實上除了上述函數,OpenGL還有一組為了優化而存在的指定索引范圍的函數,(非Multi組的函數都有,在有些擴展中也有Multi的Range函 數)以Range加入上述函數名中的方式命名,使用方式與類似函數一致,僅僅是加入了兩個參數表示索引的范圍,以方便OpenGL的實現優化,如下例,就 是glDrawElements對應的范圍函數glDrawRangeElements的例子,其他函數也類似,這里不再詳述。
1:
glPushMatrix();
2:
glTranslatef(0.5, 0, 0);
3:
glDrawRangeElements(GL_TRIANGLES, 0, 4, 3, GL_UNSIGNED_BYTE, byTopIndices);
4:
glDrawRangeElements(GL_TRIANGLES, 0, 4, 3, GL_UNSIGNED_BYTE, byLeftIndices);
5:
glDrawRangeElements(GL_TRIANGLES, 0, 4, 3, GL_UNSIGNED_BYTE, byBottomIndices);
6:
glDrawRangeElements(GL_TRIANGLES, 0, 4, 3, GL_UNSIGNED_BYTE, byRightIndices);
7:
glPopMatrix();
glDrawRangeElements — render primitives from array data
C Specification
void glDrawRangeElements( GLenum mode,
GLuint start,
GLuint end,
GLsizei count,
GLenum type,
const GLvoid * indices);
Parametersmode
Specifies what kind of primitives to render.
Symbolic constants
GL_POINTS,
GL_LINE_STRIP,
GL_LINE_LOOP,
GL_LINES,
GL_TRIANGLE_STRIP,
GL_TRIANGLE_FAN,
GL_TRIANGLES,
GL_QUAD_STRIP,
GL_QUADS,
and GL_POLYGON are accepted.
startSpecifies the minimum array index contained in indices.
endSpecifies the maximum array index contained in indices.
countSpecifies the number of elements to be rendered.
typeSpecifies the type of the values in indices. Must be one of
GL_UNSIGNED_BYTE, GL_UNSIGNED_SHORT, or
GL_UNSIGNED_INT.
indicesSpecifies a pointer to the location where the indices are stored.
混合數組
原來的數組都是指定頂點的指定頂點,指定顏色的指定顏色,事實上OpenGL允許將頂點數組及顏色數組混用,即所謂的混合數組。
前 面glVertexPointer中的第三個參數跨距就是用來指定這種情況的數據的,事實上,我們還有更簡單(更復雜?)的辦法,那就是使用 glInterleavedArray函數以指定數據,需要注意的是glInterleavedArray與glVertexPointer等函數一樣, 僅僅是指定數據而已,我們還得通過Draw一組的函數去繪制圖形。並且,個人感覺,glInterleavedArrays的參數那個復雜啊。。。。《OpenGL Programming Guide 》),中甚至需要通過一張極為復雜的表來表示。這里我也不想做過多解釋了,其參數之復雜也不是一下解釋的清楚的。建議還是參考《OpenGL Programming Guide 》)一書Table 2-5 : (continued) Variables that Direct glInterleavedArrays()的這個表比較好。
glInterleavedArrays — simultaneously specify and enable several interleaved arrays
C Specification
void glInterleavedArrays( GLenum format,
GLsizei stride,
const GLvoid * pointer);
Parametersformat
Specifies the type of array to enable. Symbolic constants
GL_V2F,
GL_V3F,
GL_C4UB_V2F,
GL_C4UB_V3F,
GL_C3F_V3F,
GL_N3F_V3F,
GL_C4F_N3F_V3F,
GL_T2F_V3F,
GL_T4F_V4F,
GL_T2F_C4UB_V3F,
GL_T2F_C3F_V3F,
GL_T2F_N3F_V3F,
GL_T2F_C4F_N3F_V3F,
and
GL_T4F_C4F_N3F_V4F
are accepted.
strideSpecifies the offset in bytes between each aggregate array element.
我這里有兩個例子,分別使用了glVertexPointer,glColorPointer制定跨距的形式和glInterleavedArrays的形式,運行效果一致。
1:
//OpenGL初始化開始
2:
void SceneInit(int w,int h)
3:
{
4:
GLenum err = glewInit();
5:
if (err != GLEW_OK)
6:
{
7:
MessageBox(NULL, _T("Error" ), _T("Glew init failed." ), MB_OK);
8:
exit(-1);
9:
}
10:
11:
glEnableClientState(GL_VERTEX_ARRAY);
12:
glEnableClientState(GL_COLOR_ARRAY);
13:
14:
// 前三列是顏色數組,后三列是頂點數組數據,
15:
static GLfloat fVertices[] = { 1.0, 0.0, 0.0, -0.5, -0.5, 0.0,
16:
0.0, 1.0, 0.0, 0.5, -0.5, 0.0,
17:
0.0, 0.0, 1.0, 0.5, 0.5, 0.0,
18:
0.0, 0.0, 0.0, -0.5, 0.5, 0.0,
19:
1.0, 1.0, 1.0, 0.0, 0.0, 0.0 };
20:
21:
glVertexPointer(3, GL_FLOAT, 6 * sizeof (GL_FLOAT), &fVertices[3]);
22:
glColorPointer(3, GL_FLOAT, 6 * sizeof (GL_FLOAT), fVertices);
23:
}
為了更好分辨區別,我將上面例子中被替換掉功能的函數注釋掉然后保留。
1:
void SceneInit(int w,int h)
2:
{
3:
GLenum err = glewInit();
4:
if (err != GLEW_OK)
5:
{
6:
MessageBox(NULL, _T("Error" ), _T("Glew init failed." ), MB_OK);
7:
exit(-1);
8:
}
9:
10:
//glEnableClientState(GL_VERTEX_ARRAY);
11:
//glEnableClientState(GL_COLOR_ARRAY);
12:
13:
// 前三列是顏色數組,后三列是頂點數組數據,
14:
static GLfloat fVertices[] = { 1.0, 0.0, 0.0, -0.5, -0.5, 0.0,
15:
0.0, 1.0, 0.0, 0.5, -0.5, 0.0,
16:
0.0, 0.0, 1.0, 0.5, 0.5, 0.0,
17:
0.0, 0.0, 0.0, -0.5, 0.5, 0.0,
18:
1.0, 1.0, 1.0, 0.0, 0.0, 0.0 };
19:
20:
//glVertexPointer(3, GL_FLOAT, 6 * sizeof(GL_FLOAT), &fVertices[3]);
21:
//glColorPointer(3, GL_FLOAT, 6 * sizeof(GL_FLOAT), fVertices);
22:
glInterleavedArrays(GL_C3F_V3F, 6 * sizeof (GL_FLOAT), fVertices);
23:
}
為節省篇幅僅貼出關鍵片段,完整源代碼見我博客源代碼的2009-10-18/glInterleavedArrays/目錄,獲取方式見文章最后關於獲取博客完整源代碼的說明。
可見glInterleavedArrays功能是很強大的,甚至能夠管理狀態的開啟,可以預見,在大規模程序中,需要切換狀態較多時,此函數對於效率的提升作用還是會較多的(其實也較有限),但是此函數實在復雜-_-!使用時甚至需要通過查表來尋找參數,暈倒。
小結
到 此為止,OpenGL頂點數組相關的用法算是基本講完了,雖然頂點數組不是個OpenGL中的核心概念,但是對於效率的提升來說是很有價值的,所以 OpenGL提供了一系列一個比一個更有效率的函數,在程序規模擴大后,不僅效率會獲得提升,事實上將數據按照OpenGL的規范存在數組中對於編程也會 帶來很大的便利,相對於用數組加循環的方式來調用glVertex*會更加的容易理解和維護,畢竟,代碼量的減少在很多時候那是工程容易維護的王道。
參考資料
1. 《OpenGL Reference Manual 》,OpenGL參考手冊
2. 《OpenGL 編程指南》(《OpenGL Programming Guide 》),Dave Shreiner,Mason Woo,Jackie Neider,Tom Davis 著,徐波譯,機械工業出版社
3. 《Nehe OpenGL Tutorials》,Nehe著,在http://nehe.gamedev.net/ 上可以找到教程及相關的代碼下載,(有PDF版本教程下載)Nehe自己還做了一個面向對象的框架,作為演示程序來說,這樣的框架非常合適。也有中文版 ,各取所需吧。
4. 《OpenGL入門學習》 ,eastcowboy著,這是我在網上找到的一個比較好的教程,較為完善,而且非常通俗。這是第一篇的地址:http://bbs.pfan.cn/post-184355.html
本系列下一篇 《Win32 OpenGL編程(6) 踏入3D世界 》
本OpenGL系列其他文章
1. 《 Win32 OpenGL 編程(1)Win32下的OpenGL編程必須步驟 》
2. 《Win32 OpenGL編程(2) 尋找缺失的OpenGL函數 》
3. 《Win32 OpenGL編程(3) 基本圖元(點,直線,多邊形)的繪制 》
4. 《Win32 OpenGL編程(4) 2D圖形基礎(顏色及坐標體系進階知識) 》
完整源代碼獲取說明
由於篇幅限制,本文一般僅貼出代碼的主要關心的部分,代碼帶工程(或者makefile)完整版(如果有的話)都能用Mercurial在Google Code中下載。文章以博文發表的日期分目錄存放,請直接使用Mercurial克隆下庫:
https://blog-sample-code.jtianling.googlecode.com/hg/
Mercurial使用方法見《分布式的,新一代版本控制系統Mercurial的介紹及簡要入門 》
要是僅僅想瀏覽全部代碼也可以直接到google code上去看,在下面的地址:
http://code.google.com/p/jtianling/source/browse?repo=blog-sample-code