2畫個三角形
ios
繪製三角形時,要用到頂點緩存(VBO)的知識。我的感受,這部分已是GL比較中級的知識了。因此這裏不作詳細解釋,也不拿VBO和頂點數組(VA)和顯示列表做對比。我以爲,像GL這種實踐性很強的技術,仍是先動手把效果實現了再說。《OpenGL超級寶典》之因此提供GLTools,就是想先把複雜的原理屏蔽掉,讓初學者能快速實現效果,隨着程序寫的越多,有些原理天然而然就理解了。不然,一開始就灌輸那麼多原理的東西,但連個最基本的三角形都畫不出來,我想大多數人都會很快失去興趣,尤爲是像DirectX和OpenGL這種偏底層的技術。編程
因此,這裏我只簡單說一下VBO的做用。VBO全名Vertex Buffer Object——頂點緩存對象。它的做用就是存儲頂點的一系列屬性,好比頂點的位置,顏色,法線,紋理座標等等。GL在繪製的過程當中,就是根據VBO的數據把圖形畫出來。數組
既然,VBO是存儲頂點的屬性,那首先得有碗吧。緩存
因此,第一步就是建立VBO安全
voidglGenBuffers(GLsizein,GLuint*buffers); 服務器
n:要產生的VBO的數量ide
buffers:存儲VBO的句柄。oop
PS:GLsizei=intGLuint= unsignedint學習
有人會問,幹嗎這麼麻煩,直接用int不就行了。OpenGL是跨平臺的API,因此要兼顧各個平臺,因此本身從新定義了類型。這個知道就行,建議養成用GL專有類型的好習慣。ui
voidglBindBuffer(GLenum target,GLuint buffer);
綁定一個緩衝區對象。
target:可能取值是:GL_ARRAY_BUFFER, GL_ELEMENT_ARRAY_BUFFER, GL_PIXEL_PACK_BUFFER, or GL_PIXEL_UNPACK_BUFFER.
咱們如今主要使用GL_ARRAY_BUFFER和 GL_ELEMENT_ARRAY_BUFFER。
GL_ARRAY_BUFFER用於保存以前說的,頂點的各類屬性
GL_ELEMENT_ARRAY_BUFFER用於保存頂點索引,這個在之後用頂點索引模型繪製時會用到。
當進行綁定以後,之前的綁定就失效了。
在綁定的同時,也將該緩存設置成當前緩存。GL對緩存的一切操做,都是對當前緩存有效。
PS:這麼作的緣由,是由於OpenGL採用狀態機的模式。glBindBuffer就是講GL的緩存模式切換到當前緩存。可能,習慣面向對象的人不習慣這種模式。GL是一種C語言API,因此GL中並無真正的對象改變。GL中的對象,實際上就是一個int類型的標識符,對於熟悉Windows編程的朋友來講,也就是所謂的句柄。因此,GL要對使用或者改變某對象,採用的就是綁定該對象句柄的方式。而不是,C++的真正類,能夠直接調用對象的成員方法。
選擇好要操做的VBO以後,就要爲VBO裝數據,這裏的數據,能夠是頂點位置,頂點顏色等等
voidglBufferData(GLenum target,GLsizeiptr size,constGLvoid * data,GLenum usage);
target:和以前glBindBuffer中的target含義是同樣的。
size:填充數據的字節數,注意是字節數,不是個數
data:具體的數據。一般用數組
usage:緩存區的用途。當不肯定用途時,使用GL_DYNAMIC_DRAW是一個比較安全的值。
準備工做都作好了,如今就能夠開畫了。
咱們畫的三角形要到了頂點的兩個屬性:位置和顏色。
因此用到了兩個VBO,咱們要建立兩個VBO。
首先定義兩個全局變量
在init()方法內,添加以下代碼
GLuint vbo[2]; glGenBuffers(2, vbo); posBuf = vbo[0]; colorBuf = vbo[1];
而後,就是繪製三角形的代碼了。
首先在display內,在glClear()和glSwapBuffer()之間,添加以下代碼。
srand(time(0));
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
GLfloat verts[] = {
200, 100, 0,
600, 100, 0,
400, 400, 0
};
GLfloat colors[] = {
(rand() % 256) / 255.0f, (rand() % 256) / 255.0f, (rand() % 256) / 255.0f,
(rand() % 256) / 255.0f, (rand() % 256) / 255.0f, (rand() % 256) / 255.0f,
(rand() % 256) / 255.0f, (rand() % 256) / 255.0f, (rand() % 256) / 255.0f
};
glBindBuffer(GL_ARRAY_BUFFER, posBuf);
glBufferData(GL_ARRAY_BUFFER, sizeof(verts), verts, GL_STATIC_DRAW);
glVertexPointer(3, GL_FLOAT, 0, 0);
glBindBuffer(GL_ARRAY_BUFFER, colorBuf);
glBufferData(GL_ARRAY_BUFFER, sizeof(colors), colors, GL_STATIC_DRAW);
glColorPointer(3, GL_FLOAT, 0, 0);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
glDrawArrays(GL_TRIANGLES, 0, 3);
glDisableClientState(GL_COLOR_ARRAY);
glEnableClientState(GL_VERTEX_ARRAY);
#1:爲隨機數設置種子,種子爲當前時間。關於C++隨機數的問題,問Google~~,做爲一名合格的碼農,Google是必備技能。
#3 - #4:設置當前爲模型視圖模式,並重置模型試圖矩陣。這裏不詳細解釋,這些步驟都是必須的。等後面講到GLSL時,天然而然就明白了。
#6 - #10:定義三角形的三個頂點。爲了方面閱讀,特地分紅3行。3個頂點分別爲(200, 100, 0), (600, 100, 0), (400, 400, 0)。注意哦,這個例子中,咱們使用的是二維座標系,原點在左下角,寬是窗口的寬度(800),高是窗口的高度(600)。至於,怎麼設置,稍後分析
#12 - #16:定義三角形的顏色。這下咱們srand()就派上用場了。所謂會變色,其實就是每秒隨機生成一種顏色。GL設置顏色是使用浮點類型的,RGBA的範圍都是0.0f~1.0f。
#18 - #20:這裏就是關鍵代碼了。
1.glBindBuffer(GL_ARRAY_BUFFER, posBuf);首先,先選擇要操做的VBO。咱們先操做,存儲頂點位置的混存。
2.glBufferData(GL_ARRAY_BUFFER, sizeof(verts), verts, GL_STATIC_DRAW);
而後,爲該緩衝區填充數據。注意第二個參數是字節大小,因此可使用sizeof獲取。
注意,sizeof有個陷阱哦。由於sizeof()和verts是在同一方法體內,因此sizeof(verts)取得的是verts數組的字節大小。可是,若是verts是經過方法形參傳遞給sizeof,那sizeof(verts)取得的是float類型指針的大小,不是數組的大小。由於,數組在做爲方法實參時,已經退化成指針了。因此,這時候咱們就要改爲 N *sizeof(float), N是數組的個數
3.glVertexPointer(3, GL_FLOAT, 0, 0);設置頂點位置的指針。說明白點,就是讓GL知道,若是想要頂點位置,就從當前緩存區裏取就能夠了。
參數1:頂點位置的大小。由於是3維座標(x,y,z),天然取3
參數2:頂點位置的類型。GL_FlOAT,這個很明白了
參數3:每一個頂點屬性的間隔。簡單地說,咱們頂點位置和顏色都是分開設置的,利用不一樣的數組。因此,每一個頂點位置都是連續的。好比(200, 100, 0),後面就是下個頂點(600, 100, 0),頂點位置之間的間隔爲0。
參數4:這個參數可能有點迷糊人。雖然它的參數類型是指針類型,但在使用VBO時,實際上指的是偏移位置。咱們要用到VBO內的頂點位置,因此設爲0
#22 - #24:設置顏色的方法,和設置頂點位置的方法是同樣的。因此就不重複了。惟一的區別,就是告訴GL,這個緩存colorBuf保存的是頂點顏色。
glColorPointer(3, GL_FLOAT, 0, nullptr);
#26 - #27:開啓對應的頂點屬性。GL對於頂點的各個屬性,默認都是關閉的。
GL_VERTEX_ARRAY:頂點位置
GL_COLOR_ARRAY:頂點顏色
注意到方法名有個Client嗎?Client是客戶端的意思(+_+沒有人不知道吧~~)。這個例子,咱們用的仍是固定管線的作法,可是拋棄了glBegin/glEndglVertexglNormal。那玩意實在是太舊了。Client指的就是當前的GL程序,咱們在GL程序裏設置好頂點屬性後,再將屬性傳給管線,管線就是服務器了。由於是固定管線,咱們不能夠干涉管線的工做。也由於這樣,咱們纔會調用glVertexPointer/glColorPointer來告訴GL,哪一個是頂點位置,哪一個是頂點顏色。等到後面,可編程管線時。咱們就能操做,管線的工做了。到那時,全部的頂點屬性,統統調用glVertexAttribPointer來指定。固然,這是後話。
#28:這句就是真正把三角形畫出來了。
glDrawArrays(GL_TRIANGLES, 0, 3);
GL有多重繪製模型,包括點,線,三角形,三角形扇等等。這裏,咱們使用GL_TRIANGLES,來指定GL畫三角形。
#29 - #30:恢復GL的默認設置。養成好習慣,繪製完成後,恢復GL的設置。
大功告成,來試試吧~~
說好的三角形呢?
對OpenGL瞭解的朋友,可能已經知道問題所在。到目前爲止,都沒有出現glViewport,gluOrtho,gluPerspective,致使沒有設置GL的繪製區域和投影模式。如今咱們就來設置。
void reshape(int width, int height){
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glViewport(0, 00, width, height);
gluOrtho2D(0, width, 0, height);
}
#2:選擇當前模式爲投影模式。
#3:重置當前的投影模式。
#5:glViewport:設置GL的繪製區域,這裏咱們設置爲整個窗口的大小。
#6:gluOrtho2D:設置GL的投影模式爲正交投影。關於正交投影和透視投影,老方法,Google一大把。
實際上,以前咱們設定的二維座標,就是這句代碼起的做用。
打完收工。這下再來看看
三角形是有了,顏色也有了。but~~說好的變色呢。~~
其實新的顏色已經產生了,可是沒有畫出來而已,咱們只需在display()中的glSwapBuffers下面添加一句就能夠了
glutPostRedisplay();
這句代碼就是告訴glut,從新畫一幀。
終於操蛋的出來了,不容易啊。
再來幾張~~
附上所有源碼
#define GLEW_STATIC #include <GL/glew.h> #include <GL/freeglut.h> #include <iostream> #include <ctime> GLuint posBuf, colorBuf; void init(){ glClearColor(0, 0, 0 ,1); glEnable(GL_DEPTH_TEST); GLuint vbo[2]; glGenBuffers(2, vbo); posBuf = vbo[0]; colorBuf = vbo[1]; } void display(){ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); srand(time(0)); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); GLfloat verts[] = { 200, 100, 0, 600, 100, 0, 400, 400, 0 }; GLfloat colors[] = { (rand() % 256) / 255.0f, (rand() % 256) / 255.0f, (rand() % 256) / 255.0f, (rand() % 256) / 255.0f, (rand() % 256) / 255.0f, (rand() % 256) / 255.0f, (rand() % 256) / 255.0f, (rand() % 256) / 255.0f, (rand() % 256) / 255.0f }; glBindBuffer(GL_ARRAY_BUFFER, posBuf); glBufferData(GL_ARRAY_BUFFER, sizeof(verts), verts, GL_STATIC_DRAW); glVertexPointer(3, GL_FLOAT, 0, 0); glBindBuffer(GL_ARRAY_BUFFER, colorBuf); glBufferData(GL_ARRAY_BUFFER, sizeof(colors), colors, GL_STATIC_DRAW); glColorPointer(3, GL_FLOAT, 0, 0); glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_COLOR_ARRAY); glDrawArrays(GL_TRIANGLES, 0, 3); glDisableClientState(GL_COLOR_ARRAY); glEnableClientState(GL_VERTEX_ARRAY); glutSwapBuffers(); glutPostRedisplay(); } void reshape(int width, int height){ glMatrixMode(GL_PROJECTION); glLoadIdentity(); glViewport(0, 00, width, height); gluOrtho2D(0, width, 0, height); } void keyboard(unsigned char key, int x, int y){ } void keyboardUp(unsigned char key, int x, int y){ } void specialKey(int key, int x, int y){ } void specialKeyUp(int key, int x, int y){ } int main(int argc, char **argv){ glutInit(&argc, argv); glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH | GLUT_STENCIL); glutInitWindowSize(800, 600); glutCreateWindow("會變色的三角形"); glutReshapeFunc(reshape); glutKeyboardFunc(keyboard); glutKeyboardUpFunc(keyboardUp); glutSpecialFunc(specialKey); glutSpecialUpFunc(specialKeyUp); glutDisplayFunc(display); glFlush(); GLenum err = glewInit(); if (GLEW_OK != err) { fprintf(stderr, "GLEW Error: %s\n", glewGetErrorString(err)); return 1; } init(); glutMainLoop(); return 0; }
本回到此爲止,欲知後事如何,且聽下回分解~~
PS:本篇做爲一篇試用博文,最想看到就是效果如何。第一次寫這麼詳細的文章,前先後後花了5個小時。其實,我也是接觸OpenGL的一點皮毛,做爲一個初學者,仍是能體會學習GL的難度。從剛開始接觸NEHE教程,到紅皮書,到國內出的雜七雜八的GL書籍,再到最後的藍寶書。我也走了很多彎路,爲了讓之後的剛剛接觸GL的初學者不要踏進以前我踩過的坑,因而萌生了把本身的學習心得記錄下來的想法。
寫的過程當中,本身確定有理解的誤差,若是有任何不對的地方,敬請指正~~。若是對本文的寫做風格有任何建議的話,儘管提~~
謝謝!