昨天學習瞭如何使用codeblocks來編譯運行一個opengl的項目。在建立一個新的opengl項目時他默認已經寫了一個示例,今天咱們就上面的例子進行下代碼的剖析,以此來敲開opengl的神祕大門。android
先把代碼貼上來(在此我爲每一個函數的做用都寫上了詳細的註釋):ios
1 /* 2 * 該代碼是由一位叫Nigel Stewart的寫於2003年11月,例子的目的是測試以glut實現球體,圓椎,圓環的紡紗線框和平滑陰影的形狀。 3 * 數量的幾何棧和切割可使用熱鍵「-」或「+」調整。 4 */ 5 6 #ifdef __APPLE__ //若是程序中沒有定義了 __APPLE__ 這個符號則加載glut,#ifdef 是預編譯命令 7 #include <GLUT/glut.h> 8 #else 9 #include <GL/glut.h> 10 #endif 11 12 #include <stdlib.h>//standard library標準庫頭文件,stdlib 頭文件裏包含了C、C++語言的最經常使用的系統函數,該文件包含了的C語言標準庫函數的定義 13 14 static int slices = 16; 15 static int stacks = 16; 16 17 /* GLUT callback Handlers */ 18 19 static void resize(int width, int height) 20 { 21 const float ar = (float) width / (float) height; 22 23 glViewport(0, 0, width, height);//佔據打開窗口的整個像素矩形 24 glMatrixMode(GL_PROJECTION);//指定當前矩陣爲投影矩陣 25 glLoadIdentity();//該函數的功能是重置當前指定的矩陣爲單位矩陣 26 glFrustum(-ar, ar, -1.0, 1.0, 2.0, 100.0);//建立一個透視投影的矩陣,而且用這個矩陣乘以當前矩陣 27 28 glMatrixMode(GL_MODELVIEW); 29 glLoadIdentity() ; 30 } 31 /* 32 *繪製 33 */ 34 static void display(void) 35 { 36 const double t = glutGet(GLUT_ELAPSED_TIME) / 1000.0;//返回兩次調用glutGet(GLUT_ELAPSED_TIME)的時間間隔,單位爲毫秒 37 const double a = t*90.0; 38 39 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);//清除顏色緩衝以及深度緩衝 40 glColor3d(1,0,0);//設置紅色爲當前顏色 41 42 glPushMatrix();//glPushMatrix(),glPopMatrix()這兩個函數是搭配使用的 43 glTranslated(-2.4,1.2,-6);//定義一個平移矩陣,該矩陣與當前矩陣相乘,使後續的圖形進行平移變換。 44 glRotated(60,1,0,0);//旋轉 45 glRotated(a,0,0,1); 46 glutSolidSphere(1,slices,stacks);//用於渲染一個絲狀球體 47 glPopMatrix(); 48 49 glPushMatrix(); 50 glTranslated(0,1.2,-6); 51 glRotated(60,1,0,0); 52 glRotated(a,0,0,1); 53 glutSolidCone(1,1,slices,stacks);//用於渲染一個絲狀圓錐 54 glPopMatrix(); 55 56 glPushMatrix(); 57 glTranslated(2.4,1.2,-6); 58 glRotated(60,1,0,0); 59 glRotated(a,0,0,1); 60 glutSolidTorus(0.2,0.8,slices,stacks);//用於渲染一個絲狀圓環 61 glPopMatrix(); 62 63 glPushMatrix(); 64 glTranslated(-2.4,-1.2,-6); 65 glRotated(60,1,0,0); 66 glRotated(a,0,0,1); 67 glutWireSphere(1,slices,stacks);//用於渲染一個實體球體 68 glPopMatrix(); 69 70 glPushMatrix(); 71 glTranslated(0,-1.2,-6); 72 glRotated(60,1,0,0); 73 glRotated(a,0,0,1); 74 glutWireCone(1,1,slices,stacks);//用於渲染一個實體圓錐 75 glPopMatrix(); 76 77 glPushMatrix(); 78 glTranslated(2.4,-1.2,-6); 79 glRotated(60,1,0,0); 80 glRotated(a,0,0,1); 81 glutWireTorus(0.2,0.8,slices,stacks);//用於渲染一個實體圓環 82 glPopMatrix(); 83 84 glutSwapBuffers();//由於使用的是雙緩存GLUT_DOUBLE,因此這裏必需要交換緩存纔會顯示 85 } 86 /* 87 *設置對應按鍵響應的不一樣事件 88 */ 89 static void key(unsigned char key, int x, int y) 90 { 91 switch (key) 92 { 93 case 27 : 94 case 'q': 95 exit(0); 96 break; 97 98 case '+': 99 slices++; 100 stacks++; 101 break; 102 103 case '-': 104 if (slices>3 && stacks>3) 105 { 106 slices--; 107 stacks--; 108 } 109 break; 110 } 111 112 glutPostRedisplay();//標記當前窗口須要從新繪製。 113 } 114 115 static void idle(void) 116 { 117 glutPostRedisplay(); 118 } 119 120 const GLfloat light_ambient[] = { 0.0f, 0.0f, 0.0f, 1.0f }; 121 const GLfloat light_diffuse[] = { 1.0f, 1.0f, 1.0f, 1.0f }; 122 const GLfloat light_specular[] = { 1.0f, 1.0f, 1.0f, 1.0f }; 123 const GLfloat light_position[] = { 2.0f, 5.0f, 5.0f, 0.0f }; 124 125 const GLfloat mat_ambient[] = { 0.7f, 0.7f, 0.7f, 1.0f }; 126 const GLfloat mat_diffuse[] = { 0.8f, 0.8f, 0.8f, 1.0f }; 127 const GLfloat mat_specular[] = { 1.0f, 1.0f, 1.0f, 1.0f }; 128 const GLfloat high_shininess[] = { 100.0f }; 129 130 /* Program entry point */ 131 132 int main(int argc, char *argv[]) 133 { 134 glutInit(&argc, argv); //調用glut函數前,要初始化glut,即調用glutInit() 135 glutInitWindowSize(640,480);//預約義窗口大小 136 glutInitWindowPosition(10,10);//設置窗口左上方位置 137 glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH);//指定了是使用RGBA模式仍是雙緩衝或者是深度緩衝 138 139 glutCreateWindow("GLUT Shapes");//建立一個支持opengl渲染環境的窗口 140 141 glutReshapeFunc(resize);//註冊一個resize函數,表示當窗口發生變化時應採起什麼行動,在這個裏面resize根據縮放後的窗口從新設置 142 glutDisplayFunc(display);//註冊一個繪圖函數display,這樣操做系統在必要時刻就會對窗體進行從新繪製操做。 143 glutKeyboardFunc(key);//註冊一個按鍵消息處理函數,這個函數是告訴窗口系統,哪個函數將會被調用來處理普通按鍵消息的。 144 glutIdleFunc(idle);//註冊一個回調函數,若是不存在其餘還沒有完成的事件(例如:當事件循環處於空閒的時候),就執行這個函數。 145 146 glClearColor(1,1,1,1);//設置窗口背景色 147 glEnable(GL_CULL_FACE);//用於啓動各類功能其功能由參數決定,GL_CULL_FACE啓用隱藏圖形材料的面。 開啓剔除操做效果。 148 glCullFace(GL_BACK);//glCullFace()參數包括GL_FRONT和GL_BACK,兩個參數分別表示禁用多邊形正面或者背面上的光照、陰影和顏色計算及操做,消除沒必要要的渲染計算。 149 150 glEnable(GL_DEPTH_TEST);//啓用深度測試 151 glDepthFunc(GL_LESS);//指定深度緩衝比較值,GL_LESS,若是輸入的深度值小於參考值,則經過。 152 153 glEnable(GL_LIGHT0);//開啓0號光源 154 glEnable(GL_NORMALIZE);//在進行光照計算以前自動單位化法向量。 155 glEnable(GL_COLOR_MATERIAL);//使用顏色材質 156 glEnable(GL_LIGHTING);//開啓燈光 157 158 glLightfv(GL_LIGHT0, GL_AMBIENT, light_ambient);//設置0號光源的環境強度 159 glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse);//光源的漫反射 160 glLightfv(GL_LIGHT0, GL_SPECULAR, light_specular);//光源鏡面反射 161 glLightfv(GL_LIGHT0, GL_POSITION, light_position);//指定光源位置 162 163 glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient);//材質屬性中的發射光 164 glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);//材質屬性中的散射光 165 glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);//材質屬性中的鏡面反射光 166 glMaterialfv(GL_FRONT, GL_SHININESS, high_shininess);//材質屬性中的光強度 167 168 glutMainLoop();//這裏讓整個繪圖循環進行,至關於死循環 169 170 return EXIT_SUCCESS; 171 }
結合註釋大概瀏覽下代碼,從上面的代碼中咱們能夠看出OpenGL中的函數都是由gl開頭的,相似的,opengl還定義了一些之前綴GL_開頭的常量,全部單詞都使用大寫形式,並如下劃線分隔。還有基本都是調用glut裏面的函數接口,而後傳入本身的參數去改變狀態,獲得本身想要的結果的。這就是由於opengl自己就是一個強大的圖形api,先來理清opengl的概念。編程
什麼是openglapi
opengl是圖形硬件的一種軟件接口,這個接口包含的函數超過700多個,這些函數能夠用於指定物體和操做,建立交互式的三維應用程序。opengl的設計目標就是做爲流線型的、獨立於硬件的接口,在許多不一樣硬件平臺上實現。咱們學習opengl目的就是去實現繪製本身想要的圖形效果,並且他的一大特色就是與平臺無關,與語言無關,好比咱們基於android使用opengl繪製圖形界面,那麼繪製出的這些界面不只能夠再android操做系統上引用也徹底能夠移植到ios上去。緩存
回到上面的例子,看懂上面代碼很簡單,頭文件加載什麼的就很少說了,看註釋。ide
咱們直接來到main函數總體觀看下main函數,沒有任何邏輯代碼,全是調用glut的接口函數。它的基本結構很是簡單就是初始化一些狀態(這些狀態用於控制opengl的渲染方式),並指定須要渲染的物體。還有須要注意的是,opengl跟經常使用計算機語言不同,若是你要自定義寫一個函數的話必須先引用glut裏的註冊函數註冊一遍,才能行之有效,由於opengl響應事件的觸發是分開的,因此會調用不一樣函數去初始化一遍,好比上面代碼中註冊的函數:函數
opengl是圖形api他封裝的接口有不少,咱們無須去了解每個函數接口的做用,只要瞭解經常使用的接口函數以及調用的邏輯,當咱們特殊須要時就去看他的api文檔來對應調用。oop
對於上面每一個函數的做用我都標上了註釋,因此這裏再也不敘述,這裏着重說下雙緩衝技術。學習
咱們知道在現實生活中的動畫都是咱們把關鍵幀畫完而後再進行播放,可是在計算機中不一樣,計算機是畫完一張用一張,當要用另外一張的時候再畫,可是當咱們要進行十分複雜的畫圖操做的時候,可能就會有明顯的閃爍或者卡頓現象了,解決這個問題就要用到雙緩衝技術。 測試
所謂的雙緩衝技術,其實就是使用兩個緩衝區,前臺緩衝和後臺緩衝。前臺緩衝是咱們看到的,後臺緩衝則是在內存中的。每次的畫圖操做都是再後臺緩衝中進行,而後複製到屏幕中。這樣就不會由於頻繁刷新而出現閃爍了。 使用雙緩衝技術也會當後臺緩衝尚未畫好的時候,這時前臺會停留在當前畫面直到後臺繪製完成才進行切換。
在OpenGL中實現雙緩衝的一種方式就是調用glutSwapBuffers()。
從代碼中還會發現涉及到不少線性代數相關的東西,因此還需學好線性代數,推薦學習書籍《線性代數及其應用》,《opengl編程指南》