OpenGL是渲染2D、3D矢量圖形硬件的一種軟件接口。本質上說,它是一個3D圖形和模型庫,具備高度的可移植性,而且具備很是快的渲染速度。OpenGL並非一種語言,而是更像一個C運行時函數庫。它提供了一些預包裝的功能,幫助開發人員編寫功能強大的三維應用程序。 OpenGL能夠再多種操做系統平臺上運行,例如各類版本的Windows、UNIX/Linux、Mac OS 和 OS/2等。現在,OpenGL普遍流行於遊戲、醫學影像、地理信息、氣象模擬等領域,是高性能圖像和交互性場景處理的工業標準。
OpenGL的高效實現(利用了圖形加速硬件)存在於Windows,部分UNIX平臺和Mac OS。這些實現通常由顯示設備廠商提供,並且很是依賴於該廠商提供的硬件。html
OpenGL ES (OpenGL for Embedded Systems) 是 OpenGL 三維圖形 API 的子集,針對手機、PDA和遊戲主機等嵌入式設備而設計git
WebGL(全寫Web Graphics Library)是一種3D繪圖協議,這種繪圖技術標準容許把JavaScript和OpenGL ES 結合在一塊兒,經過增長OpenGL ES 的一個JavaScript綁定,WebGL能夠爲HTML5 Canvas提供硬件3D加速渲染,這樣Web開發人員就能夠藉助系統顯卡來在瀏覽器裏更流暢地展現3D場景和模型了,還能建立複雜的導航和數據視覺化。github
OpenGL是個開放的標準,雖然它由SGI(美國硅圖公司)獨創,但它的標準並非控制在SGI的手中,而是由OpenGL體系結構審覈委員會(ARB)所掌管。 ARB由SGC、DEC、IBM、Intel和Microsoft等著名公司1992年創立,後來又陸續添加了nVidia、ATI等圖形芯片領域的巨擎。 ARB每隔4年開一次會,對OpenGL規範進行維護和改善,並出臺計劃對OpenGL標準進行升級,使OpenGL一直保持與時代的同步。編程
2006年,SGIG公司把OpenGL標準的控制從ARB移交給一個新的工做組:Khronos小組(www.khronos.org)。 Khronos是一個由成員提供資金的行業協會,專一於開放媒體標準的建立和維護。windows
在正式開始學習OpenGL以前,咱們須要先配置好OpenGL的軟件環境。數組
支持OpenGL的IDE有不少,OpenGL的開發環境咱們選擇的是Visual Studio,能夠從Visual Studio官網下載最新的版本。瀏覽器
OpenGL是一個圖形庫,而要畫圖,就須要先建立一個窗口。不幸的是,OpenGL並無提供建立窗口的功能,必須本身建立窗口。而建立窗口在每個操做系統上都不一樣的(在Windows上代碼量也很多),爲了方便,咱們會使用一個窗口庫來簡化這一過程。經常使用的OpenGL窗口庫有GLUT、GLFW和SDL,此處爲咱們選擇使用得比較多的GLFW。app
Visual Studio對於OpenGL(gl.h)只支持到1.1,而咱們使用的是OpenGL 3.3。可是,OpenGL是由顯卡支持的,顯卡已經提供了咱們須要的OpenGL函數。所以就須要在運行程序時動態地獲取函數地址。在Windows下,以glGenBuffers爲例,大概是這樣的:函數
#include <windows.h> #include <GL/gl.h> ... // define the functions' prototypes typedef void * (*WGLGETPROCADDRESS)(const char *); typedef void (*GLGENBUFFERS)(GLsizei, GLsizei *); // load opengl32.dll and query wglGetProcAddress' address HMODULE hDll = LoadLibrary("opengl32.dll"); WGLGETPROCADDRESS wglGetProcAddress = (WGLGETPROCADDRESS)GetProcAddress(hDll, "wglGetProcAddress"); // query OpenGL functions' addresses GLGENBUFFERS glGenBuffers = (GLGENBUFFERS)wglGetProcAddress("glGenBuffers"); // now the function can be used as normal GLuint vbo; glGenBuffers(1, &vbo);
固然,GLFW能夠從它的官方網站上下載。而後,你能夠直接下載它的binaries,或者本身使用CMake編譯。若是本身使用CMake編譯,能夠參考下面的文章: GLFW 環境配置,建立窗口oop
若是下載已經編譯好的binaries,解壓並打開,能夠找到一個include文件夾和若干lib-xxxx文件夾(xxxx是編譯器名)。include文件夾裏含有一個GLFW文件夾,裏面有glfw3.h(還有一個glfw3native.h不用管)
詳細文檔能夠參考官方的介紹,或者直接從GLFW官方網站的下載頁上獲取源代碼包。
OpenGL的數據類型定義能夠與其它語言一致,但建議在ANSI C下最好使用如下定義的數據類型,例如GLint、GLfloat等。
前綴 | 數據類型 | 相應C語言類型 | OpenGL類型 |
---|---|---|---|
b | 8-bit integer | signed char | GLbyte |
s | 16-bit integer | short | GLshort |
i | 32-bit integer | long | GLint,GLsizei |
f | 32-bit floating-point | float | GLfloat,GLclampf |
d | 64-bit floating-point | double | GLdouble,GLclampd |
ub | 8-bit unsigned integer | unsigned char | GLubyte,GLboolean |
us | 16-bit unsigned integer | unsigned short | GLushort |
ui | 32-bit unsigned integer | unsigned long | GLuint,GLenum,GLbitfield |
從上表能夠看出,OpenGL的庫函數命名方式頗有規律,瞭解這種規律後閱讀和編寫程序都比較容易方便。
首先,每一個庫函數有前綴gl、glu、glx或aux,表示此函數分屬於基本庫、實用庫、X窗口擴充庫或輔助庫,其後的函數名頭字母大寫,後綴是參數類型的簡寫,取i、f。例如:
glVertex2i(2,4); glVertex3f(2.0,4.0,5.0);
如上,有的函數參數類型後綴前帶有數字二、三、4。其中,2表明二維,3表明三維,4表明alpha值。
除此以外,有些OpenGL函數最後帶一個字母v,表示函數參數可用一個指針指向一個向量(或數組)來替代一系列單個參數值。下面兩種格式都表示設置當前顏色爲紅色,兩者等價。
glColor3f(1.0,0.0,0.0); float color_array[]={1.0,0.0,0.0}; glColor3fv(color_array);
除了以上基本命名方式外,還有一種帶「」星號的表示方法,例如glColor(),它表示能夠用函數的各類方式來設置當前顏色。同理,glVertex*v()表示用一個指針指向全部類型的向量來定義一系列頂點座標值。
例若有下面一個示例程序,也是一個初學者學習的第一個示例程序。源碼以下:
// main.cpp // opengl_progress_struct #include <GLUT/GLUT.h> #include <OpenGL/OpenGL.h> // 初始化參數 void init() { glClearColor(0.1, 0.1, 0.4, 0.0); glShadeModel(GL_SMOOTH); } // 繪圖回調函數 void display() { // 清除以前幀數據 glClear(GL_COLOR_BUFFER_BIT); // 繪製三角形 glBegin(GL_TRIANGLES); glColor3f(1, 0, 0); glVertex3f(-1, -1, -5); glColor3f(0, 1, 0); glVertex3f(1, -1, -5); glColor3f(0, 0, 1); glVertex3f(0, 1, -5); glEnd(); // 執行繪圖命令 glFlush(); } // 窗口大小變化回調函數 void reshape(int w, int h) { glViewport(0, 0, w, h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(60.0, (GLfloat)w/(GLfloat)h, 0.1, 100000.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } int main(int argc, const char * argv[]) { // 初始化顯示模式 glutInit(&argc, const_cast<char **>(argv)); glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB); // 初始化窗口 glutInitWindowSize(500, 500); glutInitWindowPosition(100, 100); glutCreateWindow(argv[0]); init(); glutReshapeFunc(reshape); glutDisplayFunc(display); // 開始主循環繪製 glutMainLoop(); return 0; }
運行效果以下:
在空間直角座標系中,任意一點可用一個三維座標矩陣[x y z]表示。若是將該點用一個四維座標的矩陣[Hx Hy Hz H]表示時,則稱爲齊次座標表示方法。在齊次座標中,最後一維座標H稱爲比例因子。
在OpenGL中,二維座標點全看做三維座標點,全部點都用齊次座標來描述,統一做爲三維齊次點來處理。每一個齊次點用一個向量(x, y, z, w)表示,其中四個元素全不爲零。齊次點具備下列幾個性質:
1)若是實數a非零,則(x, y, x, w)和(ax, ay, az, aw)表示同一個點,相似於x/y = (ax)/( ay)。
2)三維空間點(x, y, z)的齊次點座標爲(x, y, z, 1.0),二維平面點(x,y)的齊次座標爲(x, y, 0.0, 1.0)。
3)當w不爲零時,齊次點座標(x, y, z, w)即三維空間點座標(x/w, y/w, z/w);當w爲零時,齊次點(x, y, z, 0.0)表示此點位於某方向的無窮遠處。
注意:OpenGL中指定w大於或等於0.0。
在集合圖形中,會涉及到幾個概念:
用浮點值表示的點稱爲頂點(Vertex)。全部頂點在OpenGL內部計算時都做爲三維點處理,用二維座標(x, y)定義的點在OpenGL中默認z值爲0。全部頂點座標用齊次座標(x, y, z, w) 表示,若是w不爲0.0,這些齊次座標表示的頂點即爲三維空間點(x/w, y/w, z/w)。編程者能夠本身指定w值,但不多這樣作。通常來講,w缺省爲1.0。
在OpenGL中,線表明線段(Line Segment),不是數學意義上的那種沿軸兩個方向無限延伸的線。這裏的線由一系列頂點順次連結而成,有閉合和不閉合兩種。
OpenGL中定義的多邊形是由一系列線段依次連結而成的封閉區域。這些線段不能交叉,區域內不能有空洞,多邊形必須在凸多邊形,不然不能被OpenGL函數接受。
在OpenGL中,全部幾何物體最終都由有必定順序的頂點集來描述的。函數glVertex{234}{sifd}v能夠用二維、三維或齊次座標定義頂點。例如:
glVertex2s(2,3); glVertex3d(0.0,1.0,3.1414926535); glVertex4f(2.4,1.0,-2.2,2.0); GLfloat pp[3]={5.0,2.0,10.2}; glVertex3fv(pp);
第一例子表示一個空間頂點(2, 3, 0),第二個例子表示用雙精度浮點數定義一個頂點,第三個例子表示用齊次座標定義一個頂點,其真實座標爲(1.2, 0.5, -1.1),最後一個例子表示用一個指針(或數組)定義頂點。
在實際應用中,一般用一組相關的頂點序列以必定的方式組織起來定義某個幾何圖元,而不採用單獨定義多個頂點來構造幾何圖元。在OpenGL中,全部被定義的頂點必須放在glBegain()和glEnd()兩個函數之間才能正確表達一個幾何圖元或物體,不然,glVertex*()不完成任何操做。例如:
glBegin(GL_POLYGON); glVertex2f(0.0,0.0); glVertex2f(0.0,3.0); glVertex2f(3.0,3.0); glVertex2f(4.0,1.5); glVertex2f(3.0,0.0); glEnd();
以上這段程序定義了一個多邊形,若是將glBegin()中的參數GL_POLYGON改成GL_POINTS,則圖形變爲一組頂點(5個)。
點函數glBegin(GLenum mode)標誌描述一個幾何圖元的頂點列表的開始,其參數mode表示幾何圖元的描述類型。全部類型及說明見下表:
類型 | 說明 |
---|---|
GL_POINTS | 單個頂點集 |
GL_LINES | 多組雙頂點線段 |
GL_POLYGON | 單個簡單填充凸多邊形 |
GL_TRAINGLES | 多組獨立填充三角形 |
GL_QUADS | 多組獨立填充四邊形 |
GL_LINE_STRIP | 不閉合折線 |
GL_LINE_LOOP | 閉合折線 |
GL_TRAINGLE_STRIP | 線型連續填充三角形串 |
GL_TRAINGLE_FAN | 扇形連續填充三角形串 |
GL_QUAD_STRIP | 連續填充四邊形串 |
上面表用幾何圖形表示的化,以下圖。
在glBegin()和glEnd()之間最重要的信息就是由函數glVertex*()定義的頂點,必要時也可爲每一個頂點指定顏色、法向、紋理座標或其餘,即調用相關的函數,以下表。
函數 | 說明 |
---|---|
glVertex*() | 設置頂點座標 |
glColor*() | 設置當前顏色 |
glIndex*() | 設置當前顏色表 |
glNormal*() | 設置法向座標 |
glCallList(),glCallLists() | 執行顯示列表 |
glTexCoord*() | 設置紋理座標 |
glEdgeFlag*() | 控制邊界繪製 |
glMaterial*() | 設置材質 |
看一個示例:
glBegin(GL_POINTS); glColor3f(1.0,0.0,0.0); /* red color */ glVertex(...); glColor3f(0.0,1.0,0.0); /* green color */ glColor3f(0.0,0.0,1.0); /* blue color */ glVertex(...); glVertex(...); glEnd();
爲了更好的理解OpenGL幾何圖形的繪製,下面看一個綜合的示例。
#include <GLUT/GLUT.h> #include <OpenGL/OpenGL.h> // 初始化參數 void init() { glClearColor(0.1, 0.1, 0.4, 0.0); glShadeModel(GL_SMOOTH); } void DrawMyObjects(void){ /* draw some points */ glBegin(GL_POINTS); glColor3f(1.0,0.0,0.0); glVertex2f(-10.0,11.0); glColor3f(1.0,1.0,0.0); glVertex2f(-9.0,10.0); glColor3f(0.0,1.0,1.0); glVertex2f(-8.0,12.0); glEnd(); /* draw some line_segments */ glBegin(GL_LINES); glColor3f(1.0,1.0,0.0); glVertex2f(-11.0,8.0); glVertex2f(-7.0,7.0); glColor3f(1.0,0.0,1.0); glVertex2f(-11.0,9.0); glVertex2f(-8.0,6.0); glEnd(); /* draw one opened_line */ glBegin(GL_LINE_STRIP); glColor3f(0.0,1.0,0.0); glVertex2f(-3.0,9.0); glVertex2f(2.0,6.0); glVertex2f(3.0,8.0); glVertex2f(-2.5,6.5); glEnd(); /* draw one closed_line */ glBegin(GL_LINE_LOOP); glColor3f(0.0,1.0,1.0); glVertex2f(7.0,7.0); glVertex2f(8.0,8.0); glVertex2f(9.0,6.5); glVertex2f(10.3,7.5); glVertex2f(11.5,6.0); glVertex2f(7.5,6.0); glEnd(); /* draw one filled_polygon */ glBegin(GL_POLYGON); glColor3f(0.5,0.3,0.7); glVertex2f(-7.0,2.0); glVertex2f(-8.0,3.0); glVertex2f(-10.3,0.5); glVertex2f(-7.5,-2.0); glVertex2f(-6.0,-1.0); glEnd(); /* draw some filled_quandrangles */ glBegin(GL_QUADS); glColor3f(0.7,0.5,0.2); glVertex2f(0.0,2.0); glVertex2f(-1.0,3.0); glVertex2f(-3.3,0.5); glVertex2f(-0.5,-1.0); glColor3f(0.5,0.7,0.2); glVertex2f(3.0,2.0); glVertex2f(2.0,3.0); glVertex2f(0.0,0.5); glVertex2f(2.5,-1.0); glEnd(); /* draw some filled_strip_quandrangles */ glBegin(GL_QUAD_STRIP); glVertex2f(6.0,-2.0); glVertex2f(5.5,1.0); glVertex2f(8.0,-1.0); glColor3f(0.8,0.0,0.0); glVertex2f(9.0,2.0); glVertex2f(11.0,-2.0); glColor3f(0.0,0.0,0.8); glVertex2f(11.0,2.0); glVertex2f(13.0,-1.0); glColor3f(0.0,0.8,0.0); glVertex2f(14.0,1.0); glEnd(); /* draw some filled_triangles */ glBegin(GL_TRIANGLES); glColor3f(0.2,0.5,0.7); glVertex2f(-10.0,-5.0); glVertex2f(-12.3,-7.5); glVertex2f(-8.5,-6.0); glColor3f(0.2,0.7,0.5); glVertex2f(-8.0,-7.0); glVertex2f(-7.0,-4.5); glVertex2f(-5.5,-9.0); glEnd(); /* draw some filled_strip_triangles */ glBegin(GL_TRIANGLE_STRIP); glVertex2f(-1.0,-8.0); glVertex2f(-2.5,-5.0); glColor3f(0.8,0.8,0.0); glVertex2f(1.0,-7.0); glColor3f(0.0,0.8,0.8); glVertex2f(2.0,-4.0); glColor3f(0.8,0.0,0.8); glVertex2f(4.0,-6.0); glEnd(); /* draw some filled_fan_triangles */ glBegin(GL_TRIANGLE_FAN); glVertex2f(8.0,-6.0); glVertex2f(10.0,-3.0); glColor3f(0.8,0.2,0.5); glVertex2f(12.5,-4.5); glColor3f(0.2,0.5,0.8); glVertex2f(13.0,-7.5); glColor3f(0.8,0.5,0.2); glVertex2f(10.5,-9.0); glEnd(); } // 繪圖回調函數 void display() { // 清除以前幀數據 glClear(GL_COLOR_BUFFER_BIT); DrawMyObjects(); // 執行繪圖命令 glFlush(); } // 窗口大小變化回調函數 void reshape(int w, int h) { glViewport(0, 0, w, h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(60.0, (GLfloat)w/(GLfloat)h, 0.1, 100000.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); gluLookAt(0, 0, 25, 0, 0, -1, 0, 1, 0); } int main(int argc, const char * argv[]) { // 初始化顯示模式 glutInit(&argc, const_cast<char **>(argv)); glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB); // 初始化窗口 glutInitWindowSize(500, 500); glutInitWindowPosition(100, 100); glutCreateWindow(argv[0]); init(); glutReshapeFunc(reshape); glutDisplayFunc(display); // 開始主循環繪製 glutMainLoop(); return 0; }
運行效果以下圖:
openGL採用右手座標系,關於左右手座標系區別可參考下圖。
openGL 空間分爲:
局部空間是指物體所在的座標空間,即對象最開始所在的地方。想象你在一個建模軟件中建立了一個立方體。你建立的立方體的原點有可能位於(0, 0, 0),即使它有可能最後在程序中處於徹底不一樣的位置。甚至有可能你建立的全部模型都以(0, 0, 0)爲初始位置。因此,你的模型的全部頂點都是在局部空間中,它們相對於你的物體來講都是局部的。
若是咱們將咱們全部的物體導入到程序當中,它們有可能會全擠在世界的原點(0, 0, 0)上,這並非咱們想要的結果。咱們想爲每個物體定義一個位置,從而能在更大的世界當中放置它們。世界空間中的座標正如其名:是指頂點相對於世界的座標。若是你但願將物體分散在世界上擺放(特別是很是真實的那樣),這就是你但願物體變換到的空間。物體的座標將會從局部變換到世界空間;該變換是由模型矩陣(Model Matrix)實現的。
模型矩陣是一種變換矩陣,它能經過對物體進行位移、縮放、旋轉來將它置於它本應該在的位置或朝向。你能夠將它想像爲變換一個房子,你須要先將它縮小(它在局部空間中太大了),並將其位移至郊區的一個小鎮,而後在y軸上往左旋轉一點以搭配附近的房子。你也能夠把上一節將箱子處處擺放在場景中用的那個矩陣大體看做一個模型矩陣;咱們將箱子的局部座標變換到場景/世界中的不一樣位置。
觀察空間常常被人們稱之OpenGL的攝像機(Camera)(因此有時也稱爲攝像機空間(Camera Space)或視覺空間(Eye Space))。觀察空間是將世界空間座標轉化爲用戶視野前方的座標而產生的結果。所以觀察空間就是從攝像機的視角所觀察到的空間。而這一般是由一系列的位移和旋轉的組合來完成,平移/旋轉場景從而使得特定的對象被變換到攝像機的前方。這些組合在一塊兒的變換一般存儲在一個觀察矩陣(View Matrix)裏,它被用來將世界座標變換到觀察空間。
在一個頂點着色器運行的最後,OpenGL指望全部的座標都能落在一個特定的範圍內,且任何在這個範圍以外的點都應該被裁剪掉(Clipped)。被裁剪掉的座標就會被忽略,因此剩下的座標就將變爲屏幕上可見的片斷。這也就是裁剪空間(Clip Space)名字的由來。
由於將全部可見的座標都指定在−1.0 −1.0到1.0 1.0的範圍內不是很直觀,因此咱們會指定本身的座標集(Coordinate Set)並將它變換回標準化設備座標系,就像OpenGL指望的那樣。
爲了將頂點座標從觀察變換到裁剪空間,咱們須要定義一個投影矩陣(Projection Matrix),它指定了一個範圍的座標,好比在每一個維度上的−1000 −1000到1000 1000。投影矩陣接着會將在這個指定的範圍內的座標變換爲標準化設備座標的範圍(−1.0,1.0) (−1.0,1.0)。全部在範圍外的座標不會被映射到在−1.0 −1.0到1.0 1.0的範圍之間,因此會被裁剪掉。在上面這個投影矩陣所指定的範圍內,座標(1250,500,750) (1250,500,750)將是不可見的,這是因爲它的x x座標超出了範圍,它被轉化爲一個大於1.0 1.0的標準化設備座標,因此被裁剪掉了。
若是隻是圖元(Primitive),例如三角形,的一部分超出了裁剪體積(Clipping Volume),則OpenGL會從新構建這個三角形爲一個或多個三角形讓其可以適合這個裁剪範圍。
由投影矩陣建立的觀察箱(Viewing Box)被稱爲平截頭體(Frustum),每一個出如今平截頭體範圍內的座標都會最終出如今用戶的屏幕上。將特定範圍內的座標轉化到標準化設備座標系的過程(並且它很容易被映射到2D觀察空間座標)被稱之爲投影(Projection),由於使用投影矩陣能將3D座標投影(Project)到很容易映射到2D的標準化設備座標系中。
最終的座標將會被映射到屏幕空間中(使用glViewport中的設定),並被變換成片斷。
爲了將座標從一個座標系變換到另外一個座標系,咱們須要用到幾個變換矩陣,最重要的幾個分別是模型(Model)、觀察(View)、投影(Projection)三個矩陣。物體頂點的起始座標再局部空間(Local Space),這裏稱它爲局部座標(Local Coordinate),它在以後會變成世界座標(world Coordinate),觀測座標(View Coordinate),裁剪座標(Clip Coordinate),並最後以屏幕座標(Screen Corrdinate)的形式結束。
下面這張圖闡釋了 空間變換過程當中的具體過程和結果。
空間變化相關的API有:
模型矩陣變換
void glTranslate{fd}(TYPE x,TYPE y,TYPE z) void glRotate{fd}(TYPE angle,TYPE x,TYPE y,TYPE z) void glScale{fd}(TYPE x,TYPE y,TYPE z)
視圖矩陣變換
void gluLookAt(GLdouble eyex,GLdouble eyey,GLdouble eyez,GLdouble centerx,GLdouble centery,GLdouble centerz,GLdouble upx,GLdouble upy,GLdouble upz);
投影變換
void glOrtho(GLdouble left,GLdouble right,GLdouble bottom,GLdouble top, GLdouble near,GLdouble far) void gluOrtho2D(GLdouble left,GLdouble right,GLdouble bottom,GLdouble top) void glFrustum(GLdouble left,GLdouble Right,GLdouble bottom,GLdouble top, GLdouble near,GLdouble far); void gluPerspective(GLdouble fovy,GLdouble aspect,GLdouble zNear, GLdouble zFar);
視口變換
glViewport(GLint x,GLint y,GLsizei width, GLsizei height);
通用變換
void glLoadMatrix{fd}(const TYPE *m) void glMultMatrix{fd}(const TYPE *m)
在三維圖形中,紋理映射(Texture Mapping)的方法運用得很廣,尤爲描述具備真實感的物體。好比繪製一面磚牆,就能夠用一幅真實的磚牆圖像或照片做爲紋理貼到一個矩形上,這樣,一面逼真的磚牆就畫好了。若是不用紋理映射的方法,則牆上的每一塊磚都必須做爲一個獨立的多邊形來畫。另外,紋理映射可以保證在變換多邊形時,多邊形上的紋理圖案也隨之變化。例如,以透視投影方式觀察牆面時,離視點遠的磚塊的尺寸就會縮小,而離視點 較近的就會大些。此外,紋理映射也經常運用在其餘一些領域,如飛行仿真中常把一大片植被的圖像映射到一些大多邊形上用以表示地面,或用大理石、木材、布匹等天然物質的圖像做爲紋理映射到多邊形上表示相應的物體。
按照紋理的使用場景和表現形式來分,紋理主要分爲如下幾類:
void glTexImage1D(GLenum target,GLint level,GLint components,GLsizei width, GLint border,GLenum format,GLenum type,const GLvoid *pixels);
定義一個一維紋理映射,除了第一個參數target應設置爲GL_TEXTURE_1D外,其他全部的參數與函數TexImage2D()的一致,不過紋理圖像是一維紋素數組,其寬度值必須是2的冪,如有邊界則爲2m+2。
void glTexImage2D(GLenum target,GLint level,GLint components, GLsizei width, glsizei height,GLint border, GLenum format,GLenum type, const GLvoid *pixels);
定義一個二維紋理映射。其中參數target是常數GL_TEXTURE_2D。參數level表示多級分辨率的紋理圖像的級數,若只有一種分辨率,則level設爲0。
參數components是一個從1到4的整數,指出選擇了R、G、B、A中的哪些份量用於調整和混合,1表示選擇了R份量,2表示選擇了R和A兩個份量,3表示選擇了R、G、B三個份量,4表示選擇了R、G、B、A四個份量。
參數width和height給出了紋理圖像的長度和寬度,參數border爲紋理邊界寬度,它一般爲0,width和height必須是2m+2b,這裏m是整數,長和寬能夠有不一樣的值,b是border的值。紋理映射的最大尺寸依賴於OpenGL,但它至少必須是使用64x64(若帶邊界爲66x66),若width和height設置爲0,則紋理映射有效地關閉。
參數format和type描述了紋理映射的格式和數據類型,它們在這裏的意義與在函數glDrawPixels()中的意義相同,事實上,紋理數據與glDrawPixels()所用的數據有一樣的格式。參數format能夠是GL_COLOR_INDEX、GL_RGB、GL_RGBA、GL_RED、GL_GREEN、GL_BLUE、GL_ALPHA、GL_LUMINANCE或GL_LUMINANCE_ALPHA(注意:不能用GL_STENCIL_INDEX和GL_DEPTH_COMPONENT)。相似地,參數type是GL_BYPE、GL_UNSIGNED_BYTE、GL_SHORT、 GL_UNSIGNED_SHORT、GL_INT、GL_UNSIGNED_INT、GL_FLOAT或GL_BITMAP。
參數pixels包含了紋理圖像數據,這個數據描述了紋理圖像自己和它的邊界。
OpenGL中的紋理控制函數以下:
void glTexParameter{if}[v](GLenum target,GLenum pname,TYPE param);
第一個參數target能夠是GL_TEXTURE_1D或GL_TEXTURE_2D,它指出是爲一維或二維紋理說明參數;後兩個參數的可能值見下表。
參數 | 對應的值 |
---|---|
GL_TEXTURE_WRAP_S | GL_CLAMP ,GL_REPEAT |
GL_TEXTURE_WRAP_T | GL_CLAMP,GL_REPEAT |
GL_TEXTURE_MAG_FILTER | GL_NEAREST,GL_LINEAR |
GL_TEXTURE_MIN_FILTER | GL_NEAREST,GL_LINEAR,GL_NEAREST_MIPMAP_NEAREST ,GL_NEAREST_MIPMAP_LINEAR ,GL_LINEAR_MIPMAP_NEAREST ,GL_LINEAR_MIPMAP_LINEAR |
通常來講,紋理圖像爲正方形或長方形。但當它映射到一個多邊形或曲面上並變換到屏幕座標時,紋理的單個紋素不多對應於屏幕圖像上的象素。根據所用變換和所用紋理映射,屏幕上單個象素能夠對應於一個紋素的一小部分(即放大)或一大批紋素(即縮小)。下面用函數glTexParameter*()說明放大和縮小的方法:
glTexParameter*(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST); glTexParameter*(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
實際上,第一個參數能夠是GL_TEXTURE_1D或GL_TEXTURE_2D,即代表所用的紋理是一維的仍是二維的;第二個參數指定濾波方法,其中參數值GL_TEXTURE_MAG_FILTER指定爲放大濾波方法,GL_TEXTURE_MIN_FILTER指定爲縮小濾波方法;第三個參數說明濾波方式,其值見表12-1所示。
若選擇GL_NEAREST則採用座標最靠近象素中心的紋素,這有可能使圖像走樣;若選擇GL_LINEAR則採用最靠近象素中心的四個象素的加權平均值。GL_NEAREST所需計算比GL_LINEAR要少,於是執行得更快,但GL_LINEAR提供了比較光滑的效果。
同時,紋理座標能夠超出(0, 1)範圍,而且在紋理映射過程當中能夠重複映射或約簡映射。在重複映射的狀況下,紋理能夠在s,t方向上重複。例如:
glTexParameterfv(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_REPEAT); glTexParameterfv(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_REPEAT);
在繪製紋理映射場景時,不只要給每一個頂點定義幾何座標,並且也要定義紋理座標。通過多種變換後,幾何座標決定頂點在屏幕上繪製的位置,而紋理座標決定紋理圖像中的哪個紋素賦予該頂點。而且頂點之間的紋理座標插值與基礎篇中所講的平滑着色插值方法相同。
紋理圖像是方形數組,紋理座標一般可定義成1、2、三或四維形式,稱爲s,t,r和q座標,以區別於物體座標(x, y, z, w)和其餘座標。一維紋理經常使用s座標表示,二維紋理經常使用(s, t)座標表示,目前忽略r座標,q座標象w同樣,一半值爲1,主要用於創建齊次座標。OpenGL座標定義的函數是:
void gltexCoord{1234}{sifd}[v](TYPE coords);
設置當前紋理座標,此後調用glVertex()所產生的頂點都賦予當前的紋理座標。對於gltexCoord1(),s座標被設置成給定值,t和r設置爲0,q設置爲1;用gltexCoord2()能夠設置s和t座標值,r設置爲0,q設置爲1;對於gltexCoord3(),q設置爲1,其它座標按給定值設置;用gltexCoord4*()能夠給定全部的座標。使用適當的後綴(s,i,f或d)和TYPE的相應值(GLshort、GLint、glfloat或GLdouble)來講明座標的類型。注意:整型紋理座標能夠直接應用,而不是象普通座標那樣被映射到[-1, 1]之間。
#include <GLUT/GLUT.h> #include <OpenGL/OpenGL.h> #include "BMPLoader.h" GLuint tex2D; GLfloat angle; // 初始化參數 void init() { glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LESS); glClearColor(0.1, 0.1, 0.4, 0.0); glShadeModel(GL_SMOOTH); CBMPLoader bmpLoader; bmpLoader.LoadBmp("/123-bmp.bmp"); // 建立紋理 glGenTextures(1, &tex2D); glBindTexture(GL_TEXTURE_2D, tex2D); // 紋理濾波參數設置 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); // 設置紋理數據 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, bmpLoader.imageWidth, bmpLoader.imageHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, bmpLoader.image); angle = 0; } /** 繪製木箱 */ void DrawBox(){ glEnable(GL_TEXTURE_2D); /** 選擇紋理 */ glBindTexture(GL_TEXTURE_2D, tex2D); /** 開始繪製四邊形 */ glBegin(GL_QUADS); /// 前側面 glNormal3f(0.0f, 0.0f, 1.0f); /**指定法線指向觀察者 */ glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f); glTexCoord2f(1.0f, 0.0f); glVertex3f(1.0f, -1.0f, 1.0f); glTexCoord2f(1.0f, 1.0f); glVertex3f(1.0f, 1.0f, 1.0f); glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 1.0f); /// 後側面 glNormal3f(0.0f, 0.0f, -1.0f); /** 指定法線背向觀察者 */ glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f); glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, 1.0f, -1.0f); glTexCoord2f(1.0f, 1.0f); glVertex3f(1.0f, 1.0f, -1.0f); glTexCoord2f(0.0f, 1.0f); glVertex3f(1.0f, -1.0f, -1.0f); /// 頂面 glNormal3f(0.0f, 1.0f, 0.0f); /**指定法線向上 */ glTexCoord2f(0.0f, 0.0f);glVertex3f(-1.0f, 1.0f, 1.0f); glTexCoord2f(1.0f, 0.0f);glVertex3f(1.0f, 1.0f, 1.0f); glTexCoord2f(1.0f, 1.0f);glVertex3f(1.0f, 1.0f, -1.0f); glTexCoord2f(0.0f, 1.0f);glVertex3f(-1.0f, 1.0f, -1.0f); /// 底面 glNormal3f(0.0f, -1.0f, 0.0f); /** 指定法線朝下 */ glTexCoord2f(0.0f, 0.0f);glVertex3f(-1.0f, -1.0f, 1.0f); glTexCoord2f(1.0f, 0.0f);glVertex3f(1.0f, -1.0f, 1.0f); glTexCoord2f(1.0f, 1.0f);glVertex3f(1.0f, -1.0f, -1.0f); glTexCoord2f(0.0f, 1.0f);glVertex3f(-1.0f, -1.0f, -1.0f); /// 右側面 glNormal3f(1.0f, 0.0f, 0.0f); /**指定法線朝右 */ glTexCoord2f(0.0f, 0.0f); glVertex3f(1.0f, -1.0f, -1.0f); glTexCoord2f(1.0f, 0.0f); glVertex3f(1.0f, 1.0f, -1.0f); glTexCoord2f(1.0f, 1.0f); glVertex3f(1.0f, 1.0f, 1.0f); glTexCoord2f(0.0f, 1.0f); glVertex3f(1.0f, -1.0f, 1.0f); /// 左側面 glNormal3f(-1.0f, 0.0f, 0.0f); /**指定法線朝左 */ glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f); glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, 1.0f, -1.0f); glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 1.0f); glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, -1.0f, 1.0f); glEnd(); glDisable(GL_TEXTURE_2D); } // 繪圖回調函數 void display() { // 清除以前幀數據 glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); glPushMatrix(); glTranslatef(0.0f, 0.0f, -5.0f); glRotated(angle, 1, 1, 0); DrawBox(); glPopMatrix(); // 執行繪圖命令 glFlush(); angle ++; glutPostRedisplay(); } // 窗口大小變化回調函數 void reshape(int w, int h) { glViewport(0, 0, w, h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(60.0, (GLfloat)w/(GLfloat)h, 0.1, 100000.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } int main(int argc, const char * argv[]) { // 初始化顯示模式 glutInit(&argc, const_cast<char **>(argv)); glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB|GLUT_DEPTH); // 初始化窗口 glutInitWindowSize(500, 500); glutInitWindowPosition(100, 100); glutCreateWindow(argv[0]); init(); glutReshapeFunc(reshape); glutDisplayFunc(display); // 開始主循環繪製 glutMainLoop(); return 0; }
運行效果以下:
當光照射到一個物體表面上時,會出現三種情形。首先,光能夠經過物體表面向空間反射,產生反射光。其次,對於透明體,光能夠穿透該物體並從另外一端射出,產生透射光。最後,部分光將被物體表面吸取而轉換成熱。在上述三部分光中,僅僅是透射光和反射光可以進入人眼產生視覺效果。這裏介紹的簡單光照模型只考慮被照明物體表面的反射光影響,假定物體表面光滑不透明且由理想材料構成,環境假設爲由白光照明。
通常來講,反射光能夠分紅三個份量,即環境反射、漫反射和鏡面反射。環境反射份量假定入射光均勻地從周圍環境入射至景物表面並等量地向各個方向反射出去,一般物體表面還會受到從周圍環境來的反射光(如來自地面、天空、牆壁等的反射光)的照射,這些光常統稱爲環境光(Ambient Light);漫反射份量表示特定光源在景物表面的反射光中那些向空間各方向均勻反射出去的光,這些光常稱爲漫射光(Diffuse Light);鏡面反射光爲朝必定方向的反射光,如一個點光源照射一個金屬球時會在球面上造成一塊特別亮的區域,呈現所謂「高光(Highlight)」,它是光源在金屬球面上產生的鏡面反射光(Specular Light)。對於較光滑物體,其鏡面反射光的高光區域小而亮;相反,粗糙表面的鏡面反射光呈發散狀態,其高光區域大而不亮。
在OpenGL簡單光照模型中的幾種光分爲:輻射光(Emitted Light)、環境光(Ambient Light)、漫射光(Diffuse Light)、鏡面光(Specular Light)。
光源有許多特性,如顏色、位置、方向等。選擇不一樣的特性值,則對應的光源做用在物體上的效果也不同,這在之後的章節中會逐步介紹的。下面詳細講述定義光源特性的函數glLight*():
void glLight{if}[v](GLenum light , GLenum pname, TYPE param)
建立具備某種特性的光源。其中第一個參數light指定所建立的光源號,如GL_LIGHT0、GL_LIGHT一、...、GL_LIGHT7。第二個參數pname指定光源特性,這個參數的輔助信息見表1-3所示。最後一個參數設置相應的光源特性值。
pname 參數名 | 默認值 | 說明 |
---|---|---|
GL_AMBIENT | (0.0, 0.0, 0.0, 1.0) | RGBA模式下環境光 |
GL_DIFFUSE | (1.0, 1.0, 1.0, 1.0) | RGBA模式下漫反射光 |
GL_SPECULAR | (1.0,1.0,1.0,1.0) | RGBA模式下鏡面光 |
GL_POSITION | (0.0,0.0,1.0,0.0) | 光源位置齊次座標(x,y,z,w) |
GL_SPOT_DIRECTION | (0.0,0.0,-1.0) | 點光源聚光方向矢量(x,y,z) |
GL_SPOT_EXPONENT | 0.0 | 點光源聚光指數 |
GL_SPOT_CUTOFF | 180.0 | 點光源聚光截止角 |
GL_CONSTANT_ATTENUATION | 1.0 | 常數衰減因子 |
GL_LINER_ATTENUATION | 0.0 | 線性衰減因子 |
GL_QUADRATIC_ATTENUATION | 0.0 | 平方衰減因子 |
以上列出的GL_DIFFUSE和GL_SPECULAR的缺省值只能用於GL_LIGHT0,其餘幾個光源的GL_DIFFUSE和GL_SPECULAR缺省值爲(0.0,0.0,0.0,1.0)。另外,表中後六個參數的應用放在下一篇中介紹。在上面例程中,光源的建立爲:
GLfloat light_position[] = { 1.0, 1.0, 1.0, 0.0 }; glLightfv(GL_LIGHT0, GL_POSITION, light_position);
其中light_position是一個指針,指向定義的光源位置齊次座標數組。其它幾個光源特性都爲缺省值。一樣,咱們也可用相似的方式定義光源的其餘幾個特性值。例如:
GLfloat light_ambient [] = { 0.0, 0.0, 0.0, 1.0 }; GLfloat light_diffuse [] = { 1.0, 1.0, 1.0, 1.0 }; GLfloat light_specular[] = { 1.0, 1.0, 1.0, 1.0 }; glLightfv(GL_LIGHT0, GL_AMBIENT , light_ambient ); glLightfv(GL_LIGHT0, GL_DIFFUSE , light_diffuse ); glLightfv(GL_LIGHT0, GL_SPECULAR, light_specular);
在OpenGL中,必須明確指出光照是否有效或無效。若是光照無效,則只是簡單地將當前顏色映射到當前頂點上去,不進行法向、光源、材質等複雜計算,那麼顯示的圖形就沒有真實感,如前幾章例程運行結果顯示。要使光照有效,首先得啓動光照,啓動光照須要用到以下函數。
glEnable(GL_LIGHTING);
若使光照無效,則調用gDisable(GL_LIGHTING)可關閉當前光照。而後,必須使所定義的每一個光源有效,若是隻用了一個光源。
glEnable(GL_LIGHT0);
其它光源相似,只是光源號不一樣而已。
OpenGL用材料對光的紅、綠、藍三原色的反射率來近似定義材料的顏色。像光源同樣,材料顏色也分紅環境、漫反射和鏡面反射成分,它們決定了材料對環境光、漫反射光和鏡面反射光的反射程度。在進行光照計算時,材料對環境光的反射率與每一個進入光源的環境光結合,對漫反射光的反射率與每一個進入光源的漫反射光結合,對鏡面光的反射率與每一個進入光源的鏡面反射光結合。對環境光與漫反射光的反射程度決定了材料的顏色,而且它們很類似。對鏡面反射光的反射率一般是白色或灰色(即對鏡面反射光中紅、綠、藍的反射率相同)。鏡面反射高光最亮的地方將變成具備光源鏡面光強度的顏色。例如一個光亮的紅色塑料球,球的大部分表現爲紅色,光亮的高光將是白色的。材質的定義與光源的定義相似:
void glMaterial{if}[v](GLenum face,GLenum pname,TYPE param);
定義光照計算中用到的當前材質。face能夠是GL_FRONT、GL_BACK、GL_FRONT_AND_BACK,它代表當前材質應該應用到物體的哪個面上;pname說明一個特定的材質;param是材質的具體數值,若函數爲向量形式,則param是一組值的指針,反之爲參數值自己。非向量形式僅用於設置GL_SHINESS。另外,參數GL_AMBIENT_AND_DIFFUSE表示能夠用相同的RGB值設置環境光顏色和漫反射光顏色。
參數名 | 默認值 | 說明 |
---|---|---|
GL_AMBIENT | (0.2, 0.2, 0.2, 1.0) | 材料的環境光顏色 |
GL_DIFFUSE | (0.8, 0.8, 0.8, 1.0) | 材料的漫反射光顏色 |
GL_AMBIENT_AND_DIFFUSE | 材料的環境光和漫反射光顏色 | |
GL_SPECULAR | (0.0, 0.0, 0.0, 1.0) | 材料的鏡面反射光顏色 |
GL_SHINESS | 0.0 | 鏡面指數(光亮度) |
GL_EMISSION | (0.0, 0.0, 0.0, 1.0) | 材料的輻射光顏色 |
GL_COLOR_INDEXES | (0, 1, 1) | 材料的環境光、漫反射光和鏡面光顏色 |
材質的顏色與光源的顏色有些不一樣。對於光源,R、G、B值等於R、G、B對其最大強度的百分比。若光源顏色的R、G、B值都是1.0,則是最強的白光;若值變爲0.5,顏色仍爲白色,但強度爲原來的一半,因而表現爲灰色;若R=G=1.0,B=0.0,則光源爲黃色。對於材質,R、G、B值爲材質對光的R、G、B成分的反射率。好比,一種材質的R=1.0、G=0.五、B=0.0,則材質反射所有的紅色成分,一半的綠色成分,不反射藍色成分。也就是說,若OpenGL的光源顏色爲(LR、LG、LB),材質顏色爲(MR、MG、MB),那麼,在忽略全部其餘反射效果的狀況下,最終到達眼睛的光的顏色爲(LRMR、LGMG、LB*MB)。
一樣,若是有兩束光,相應的值分別爲(R一、G一、B1)和(R二、G二、B2),則OpenGL將各個顏色成分相加,獲得(R1+R二、G1+G二、B1+B2),若任一成分的和值大於1(超出了設備所能顯示的亮度)則約簡到1.0。
下面的示例將演示光照和材質在OpenGL上的應用。
#include <GLUT/GLUT.h> #include <OpenGL/OpenGL.h> // 初始化參數 void init() { GLfloat ambient[] = { 0.0, 0.0, 0.0, 1.0 }; GLfloat diffuse[] = { 1.0, 1.0, 1.0, 1.0 }; // GLfloat specular[] = { 1.0, 1.0, 1.0, 1.0 }; GLfloat position[] = { 0.0, 0, -1.0, 0.0 }; glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LESS); glLightfv(GL_LIGHT0, GL_AMBIENT, ambient); glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse); // glLightfv(GL_LIGHT0, GL_SPECULAR, specular); glLightfv(GL_LIGHT0, GL_POSITION, position); glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); glClearColor(0.0, 0.1, 0.1, 0.0) ; } // 繪圖回調函數 void display() { GLfloat no_mat[] = { 0.0, 0.0, 0.0, 1.0 }; GLfloat mat_ambient[] = { 0.7, 0.7, 0.7, 1.0 }; GLfloat mat_ambient_color[] = { 0.8, 0.8, 0.2, 1.0 }; GLfloat mat_diffuse[] = { 0.1, 0.5, 0.8, 1.0 }; GLfloat mat_specular[] = { 1.0, 1.0, 1.0, 1.0 }; GLfloat no_shininess[] = { 0.0 }; GLfloat low_shininess[] = { 5.0 }; GLfloat high_shininess[] = { 100.0 }; GLfloat mat_emission[] = {0.3, 0.2, 0.2, 0.0}; glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); /* 第一行第一列繪製的球僅有漫反射光而無環境光和鏡面光。*/ glPushMatrix(); glTranslatef (-3.75, 3.0, 0.0); glMaterialfv(GL_FRONT, GL_AMBIENT, no_mat); glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse); glMaterialfv(GL_FRONT, GL_SPECULAR, no_mat); glMaterialfv(GL_FRONT, GL_SHININESS, no_shininess); glMaterialfv(GL_FRONT, GL_EMISSION, no_mat); glutSolidSphere(1.0, 20, 20); glPopMatrix(); /* 第一行第二列繪製的球有漫反射光和鏡面光,並有低高光,而無環境光 。*/ glPushMatrix(); glTranslatef (-1.25, 3.0, 0.0); glMaterialfv(GL_FRONT, GL_AMBIENT, no_mat); glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse); glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular); glMaterialfv(GL_FRONT, GL_SHININESS, low_shininess); glMaterialfv(GL_FRONT, GL_EMISSION, no_mat); glutSolidSphere(1.0, 20, 20); glPopMatrix(); /* 第一行第三列繪製的球有漫反射光和鏡面光,並有很亮的高光,而無環境光 。*/ glPushMatrix(); glTranslatef (1.25, 3.0, 0.0); glMaterialfv(GL_FRONT, GL_AMBIENT, no_mat); glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse); glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular); glMaterialfv(GL_FRONT, GL_SHININESS, high_shininess); glMaterialfv(GL_FRONT, GL_EMISSION, no_mat); glutSolidSphere(1.0, 20, 20); glPopMatrix(); /* 第一行第四列繪製的球有漫反射光和輻射光,而無環境和鏡面反射光。*/ glPushMatrix(); glTranslatef (3.75, 3.0, 0.0); glMaterialfv(GL_FRONT, GL_AMBIENT, no_mat); glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse); glMaterialfv(GL_FRONT, GL_SPECULAR, no_mat); glMaterialfv(GL_FRONT, GL_SHININESS, no_shininess); glMaterialfv(GL_FRONT, GL_EMISSION, mat_emission); glutSolidSphere(1.0, 20, 20); glPopMatrix(); /* 第二行第一列繪製的球有漫反射光和環境光,而鏡面反射光。*/ glPushMatrix(); glTranslatef (-3.75, 0.0, 0.0); glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient); glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse); glMaterialfv(GL_FRONT, GL_SPECULAR, no_mat); glMaterialfv(GL_FRONT, GL_SHININESS, no_shininess); glMaterialfv(GL_FRONT, GL_EMISSION, no_mat); glutSolidSphere(1.0, 20, 20); glPopMatrix(); /* 第二行第二列繪製的球有漫反射光、環境光和鏡面光,且有低高光。*/ glPushMatrix(); glTranslatef (-1.25, 0.0, 0.0); glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient); glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse); glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular); glMaterialfv(GL_FRONT, GL_SHININESS, low_shininess); glMaterialfv(GL_FRONT, GL_EMISSION, no_mat); glutSolidSphere(1.0, 20, 20); glPopMatrix(); /* 第二行第三列繪製的球有漫反射光、環境光和鏡面光,且有很亮的高光。*/ glPushMatrix(); glTranslatef (1.25, 0.0, 0.0); glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient); glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse); glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular); glMaterialfv(GL_FRONT, GL_SHININESS, high_shininess); glMaterialfv(GL_FRONT, GL_EMISSION, no_mat); glutSolidSphere(1.0, 20, 20); glPopMatrix(); /* 第二行第四列繪製的球有漫反射光、環境光和輻射光,而無鏡面光。*/ glPushMatrix(); glTranslatef (3.75, 0.0, 0.0); glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient); glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse); glMaterialfv(GL_FRONT, GL_SPECULAR, no_mat); glMaterialfv(GL_FRONT, GL_SHININESS, no_shininess); glMaterialfv(GL_FRONT, GL_EMISSION, mat_emission); glutSolidSphere(1.0, 20, 20); glPopMatrix(); /* 第三行第一列繪製的球有漫反射光和有顏色的環境光,而無鏡面光。*/ glPushMatrix(); glTranslatef (-3.75, -3.0, 0.0); glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient_color); glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse); glMaterialfv(GL_FRONT, GL_SPECULAR, no_mat); glMaterialfv(GL_FRONT, GL_SHININESS, no_shininess); glMaterialfv(GL_FRONT, GL_EMISSION, no_mat); glutSolidSphere(1.0, 20, 20); glPopMatrix(); /* 第三行第二列繪製的球有漫反射光和有顏色的環境光以及鏡面光,且有低高光。*/ glPushMatrix(); glTranslatef (-1.25, -3.0, 0.0); glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient_color); glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse); glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular); glMaterialfv(GL_FRONT, GL_SHININESS, low_shininess); glMaterialfv(GL_FRONT, GL_EMISSION, no_mat); glutSolidSphere(1.0, 20, 20); glPopMatrix(); /* 第三行第三列繪製的球有漫反射光和有顏色的環境光以及鏡面光,且有很亮的高光。*/ glPushMatrix(); glTranslatef (1.25, -3.0, 0.0); glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient_color); glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse); glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular); glMaterialfv(GL_FRONT, GL_SHININESS, high_shininess); glMaterialfv(GL_FRONT, GL_EMISSION, no_mat); glutSolidSphere(1.0, 20, 20); glPopMatrix(); /* 第三行第四列繪製的球有漫反射光和有顏色的環境光以及輻射光,而無鏡面光。*/ glPushMatrix(); glTranslatef (3.75, -3.0, 0.0); glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient_color); glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse); glMaterialfv(GL_FRONT, GL_SPECULAR, no_mat); glMaterialfv(GL_FRONT, GL_SHININESS, no_shininess); glMaterialfv(GL_FRONT, GL_EMISSION, mat_emission); glutSolidSphere(1.0, 20, 20); glPopMatrix(); // 執行繪圖命令 glFlush(); } // 窗口大小變化回調函數 void reshape(int w, int h) { glViewport(0, 0, w, h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(60.0, (GLfloat)w/(GLfloat)h, 0.1, 100000.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); gluLookAt(0, 0, 10, 0, 0, -1, 0, 1, 0); } int main(int argc, const char * argv[]) { // 初始化顯示模式 glutInit(&argc, const_cast<char **>(argv)); glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA); // 初始化窗口 glutInitWindowSize(500, 500); glutInitWindowPosition(100, 100); glutCreateWindow(argv[0]); init(); glutReshapeFunc(reshape); glutDisplayFunc(display); // 開始主循環繪製 glutMainLoop(); return 0; }
運行效果以下圖: