1、OpenGL與3D圖形世界
1.一、OpenGL令人們進入三維圖形世界 咱們生活在一個充滿三維物體的三維世界中,爲了使計算機能精確地再現這些物體,咱們必須能在三維空間描繪這些物體。咱們又生活在一個充滿信息的世界中,可否儘快地理解並運用這些信息將直接影響事業的成敗,因此咱們須要用一種最直接的形式來表示這些信息。 最近幾年計算機圖形學的發展使得三維表現技術得以造成,這些三維表現技術使咱們可以再現三維世界中的物體,可以用三維形體來表示複雜的信息,這種技術就是可視化(Visualization)技術。可視化技術令人可以在三維圖形世界中直接對具備形體的信息進行操做,和計算機直接交流。這種技術已經把人和機器的力量以一種直覺而天然的方式加以統一,這種革命性的變化無疑將極大地提升人們的工做效率。可視化技術賦予人們一種仿真的、三維的而且具備實時交互的能力,這樣人們能夠在三維圖形世界中用之前不可想象的手段來獲取信息或發揮本身創造性的思惟。機械工程師能夠從二維平面圖中得以解放直接進入三維世界,從而很快獲得本身設計的三維機械零件模型。醫生能夠從病人的三維掃描圖象分析病人的病竈。軍事指揮員能夠面對用三維圖形技術生成的戰場地形,指揮具備真實感的三維飛機、軍艦、坦克向目標開進並分析戰鬥方案的效果。 更使人驚奇的是目前正在發展的虛擬現實技術,它能令人們進入一個三維的、多媒體的虛擬世界,人們能夠遊歷遠古時代的城堡,也能夠遨遊浩翰的太空。全部這些都依賴於計算機圖形學、計算機可視化技術的發展。人們對計算機可視化技術的研究已經歷了一個很長的歷程,並且造成了許多可視化工具,其中SGI公司推出的GL三維圖形庫表現突出,易於使用並且功能強大。利用GL開發出來的三維應用軟件頗受許多專業技術人員的喜好,這些三維應用軟件已涉及建築、產品設計、醫學、地球科學、流體力學等領域。隨着計算機技術的繼續發展,GL已經進一步發展成爲OpenGL,OpenGL已被認爲是高性能圖形和交互式視景處理的標準,目前包括ATT公司UNIX軟件實驗室、IBM公司、DEC公司、SUN公司、HP公司、Microsoft公司和 SGI公司在內的幾家在計算機市場佔領導地位的大公司都採用了OpenGL圖形標準。 值得一提的是,因爲Microsoft公司在 Windows NT中提供OpenGL圖形標準,OpenGL將在微機中普遍應用,尤爲是OpenGL三維圖形加速卡和微機圖形工做站的推出,人們能夠在微機上實現三維圖形應用,如CAD設計、仿真模擬、三維遊戲等,從而更有機會、更方便地使用OpenGL及其應用軟件來創建本身的三維圖形世界。 1.二、OpenGL提供直觀的三維圖形開發環境 OpenGL其實是一種圖形與硬件的接口。它包括了120個圖形函數,開發者能夠用這些函數來創建三維模型和進行三維實時交互。與其餘圖形程序設計接口不一樣,OpenGL提供了十分清晰明瞭的圖形函數,所以初學的程序設計員也能利用OpenGL的圖形處理能力和1670萬種色彩的調色板很快地設計出三維圖形以及三維交互軟件。 OpenGL強有力的圖形函數不要求開發者把三維物體模型的數據寫成固定的數據格式,這樣開發者不但能夠直接使用本身的數據,並且能夠利用其餘不一樣格式的數據源。這種靈活性極大地節省了開發者的時間,提升了軟件開發效益。 長期以來,從事三維圖形開發的技術人員都不得不在本身的程序中編寫矩陣變換、外部設備訪問等函數,這樣爲調製這些與本身的軟件開發目標關係並不十分密切的函數費腦筋,而OpenGL正是提供一種直觀的編程環境,它提供的一系列函數大大地簡化了三維圖形程序。例如:程序員
1.三、OpenGL成爲目前三維圖形開發標準 OpenGL成爲目前三維圖形開發標準在計算機發展初期,人們就開始從事計算機圖形的開發。直到計算機硬軟件和計算機圖形學高度發達的九十年代,人們發現複雜的數據以視覺的形式表現時是最易理解的,於是三維圖形得以迅猛發展,因而各類三維圖形工具軟件包相繼推出,如PHIGS、PEX、 RenderMan等。這些三維圖形工具軟件包有些側重於使用方便,有些側重於渲染效果或與應用軟件的鏈接,但沒有一種三維工具軟件包在交互式三維圖形建模能力、外部設備管理以及編程方便程度上可以OpenGL相比擬。 OpenGL通過對GL的進一步發展,實現二維和三維的高級圖形技術,在性能上表現得異常優越,它包括建模、變換、光線處理、色彩處理、動畫以及更先進的能力,如紋理影射、物體運動模糊等。OpenGL的這些能力爲實現逼真的三維渲染效果、創建交互的三維景觀提供了優秀的軟件工具。OpenGL在硬件、窗口、操做系統方面是相互獨立的。 許多計算機公司已經把 OpenGL集成到各類窗口和操做系統中,其中操做系統包括UNIX、Windows NT、DOS等,窗口系統有X窗口、Windows等。爲了實現一個完整功能的圖形處理系統,設計一個與OpenGL相關的系統結構爲:其最底層是圖形硬件,第二層爲操做系統,第三層爲窗口系統,第四層爲OpenGL,第五層爲應用軟件。OpenGL是網絡透明的,在客戶 — 服務器(Client-Server)體系結構中,OpenGL容許本地和遠程繪圖。因此在網絡系統中,OpenGL在X窗口、Windows或其它窗口系統下均可以以一個獨立的圖形窗口出現。 OpenGL做爲一個性能優越的圖形應用程序設計界面(API)而適合於普遍的計算環境,從我的計算機到工做站和超級計算機,OpenGL都能實現高性能的三維圖形功能。因爲許多在計算機界具備領導地位的計算機公司紛紛採用OpenGL做爲三維圖形應用程序設計界面,OpenGL應用程序具備普遍的移植性。所以,OpenGL已成爲目前的三維圖形開發標準,是從事三維圖形開發工做的技術人員所必須掌握的開發工具。
2、OpenGL概念創建 算法
相應函數 | 具體說明 |
OpenGL實用庫 | 43個函數,每一個函數以glu開頭。 |
OpenGL輔助庫 | 31個函數,每一個函數以aux開頭。 |
Windows專用庫函數(WGL) | 6個函數,每一個函數以wgl開頭。 |
Win32 API函數 | 5個函數,函數前面沒有專用前綴。 |
4、OpenGL基礎程序結構
用OpenGL編寫的程序結構相似於用其餘語言編寫的程序。實際上,OpenGL是一個豐富的三維圖形函數庫,編寫OpenGL程序並不是難事,只需在基本C語言中調用這些函數,用法同Turbo C、Microsoft C等相似,但也有許多不一樣之處。
本指南全部的程序都是在Windows NT的Microsoft Visual C++集成環境下編譯鏈接的,其中有部分頭文件和函數是爲這個環境所用的,例如判別操做系統的頭文件「glos.h」。此外,爲便於各種讀者同時快速入門,在短期內掌握OpenGL編程的基本方法和技巧,指南中例子儘可能採用標準ANSI C調用OpenGL函數來編寫,並且全部例程都只採用OpenGL附帶的輔助庫中的窗口系統。此外,這樣也便於程序在各平臺間移植,尤爲往工做站UNIX 操做系統移植時,也只需改動頭文件等不多不多的部分。下面列出一個簡單的OpenGL程序:
例4-1 OpenGL簡單例程(Simple.c)
#include <GL/gl.h> #include <GL/glaux.h> #include "glos.h"
void main(void) { auxInitDisplayMode(AUX_SINGLE|AUX_RGBA); auxInitPosition(0,0,500,500); auxInitWindow("simple");
glClearColor(0.0,0.0,0.0,0.0); glClear(GL_COLOR_BUFFER_BIT);
glColor3f(1.0,0.0,0.0); glRectf(-0.5,-0.5,0.5,0.5);
glFlush(); _sleep(1000); }
這個程序運行結果是在屏幕窗口內畫一個紅色的方塊。
下面具體分析整個程序結構:首先,在程序最開始處是OpenGL頭文件:<GL/gl.h>、<GL/glaux.h>。前一個是gl庫的頭文件,後一個是輔助庫的頭文件。此外,在之後的幾章中還將說明OpenGL的另外兩個頭文件,一個是<GL/glu.h>實用庫的頭文件,另外一個是<GL/glx.h>X窗口擴充庫的頭文件(這個經常使用在工做站上)。接下來是主函數main()的定義:通常的程序結構是先定義一個窗口:
auxInitDisplayMode(AUX_SINGLE|AUX_RGBA); auxInitPosition(0,0,500,500); auxInitWindow("simple");
auxInitDisplayMode(AUX_SINGLE|AUX_RGBA)設置窗口顯示模式爲RGBA方式,即彩色方式,而且圖形緩存爲單緩存(SINGLE BUFFER)。 auxInitPosition(0, 0, 500, 500)定義窗口的初始位置,前兩個參數(0, 0)爲窗口的左上角點的屏幕座標,後兩個參數(500,500)爲窗口的寬度和高度。auxInitWindow("simple")是窗口初始化,字符參數是窗口名稱。
而後是窗口內清屏:
glClearColor(0.0,0.0,0.0,0.0); glClear(GL_COLOR_BUFFER_BIT);
第一句將窗口清爲黑色,第二句將顏色緩衝區清爲glClearColor(0.0, 0.0, 0.0, 0.0)命令所設置的顏色,即同學口背景顏色一致。
再接着是在窗口內畫一個物體:
glColor3f(1.0,0.0,0.0); glRectf(-0.5,-0.5,0.5,0.5);
很明顯,第一句設置物體顏色,函數中前三個參數分別爲R、G、B值,最後一個參數是Alpha值,範圍都從0至1;第二句繪製一個二維矩形。注意:OpenGL是針對三維圖形而言,所以用做OpenGL編程繪製物體必須意識到任何一個物體都是三維的,具備空間性,而顯示於屏幕上的物體都是三維物體在二維平面上的投影。
從表面上看,上述程序代碼很簡單,實際上已經用到了缺省的投影形式(正射投影)。再看glFlush()函數,表示強制繪圖完成。最後一句_sleep(1000),參數單位爲毫秒,整句意思是保持現有情況一秒鐘,而後結束程序運行。這個函數是VC++的庫函數。
總而言之,OpenGL程序基本結構爲定義窗口、清理窗口、繪製物體、結束運行。
5、OpenGL的數據類型和函數名
OpenGL的數據類型定義能夠與其它語言一致,但建議在ANSI C下最好使用如下定義的數據類型,例如GLint、GLfloat等。具體類型見表5-1。 前綴 數據類型 相應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 編程
6、OpenGL輔組庫的基本使用
OpenGL是一個開放的系統,它是獨立於任何窗口系統或操做系統的。儘管它包含了許多圖形函數,但它卻沒有窗口函數,也沒有從鍵盤和鼠標讀取事件的函數,因此要初學者寫出一個完整的圖形程序是至關困難的。另外,OpenGL圖形函數中只提供基本的幾何原形:點、線、多邊形,所以要建立基本的三維幾何體如球、錐體等,也很不容易。而OpenGL輔助庫就是爲解決這些基本問題專門設計的,它提供了一些基本的窗口管理函數和三維圖形繪製函數,能幫助初學者儘快進入OpenGL世界,掌握關鍵的三維圖形技術,體會其中奇妙的樂趣。可是,對於複雜的應用,這些函數遠遠不夠,只能做爲參考。
6.一、輔助庫函數分類
這一節內容能夠做爲手冊查閱,初學者沒必要深究。
輔助庫函數大體分爲六類:
6.1.1 窗口初始化和退出 相關函數有三個,它們在第一章已提到,這裏將詳細介紹:
void auxInitWindow(GLbyte *titleString)
打開一個由auxInitDisplayMode()和auxInitPosition()指定的窗口。函數參數是窗口標題,窗口背景缺省顏色是RGBA下的黑色或顏色表(color_index)下的0號調色板的顏色。按下Escape鍵能夠完成關掉窗口、結束程序、所有清屏三項功能。
void auxInitDisplayMode(GLbitfield mask)
設置窗口顯示模式。基本模式有RGBA或顏色表、單或雙緩存,也可指定其餘附加模式:深度、模板或累積緩存(depth,stencil,and/or accumulation buffer)。參數mask是一組位標誌的聯合(取或),AUX_RGBA或AUX_INDEX、AUX_SINGLE或AUX_DOUBLE,以及其它有效標誌AUX_DEPTH、AUX_STENCIL或AUX_ACCUM。
void auxInitPosition(GLint x,GLint y,GLsizei width,GLsizei height)
設置窗口位置及大小。參數(x, y)爲窗口的左上角點的屏幕座標,參數(width, height)爲窗口的寬度和高度,單位爲象素,缺省值爲(0, 0, 100, 100)。
6.1.2 窗口處理和事件輸入
當窗口建立後,且在進入主函數循環以前,應當登記如下列出的回調函數(callback function):
void auxReshapeFunc(void(*function)(GLsizei,GLsizei))
定義窗口改變時形狀重定函數。參數function是一個函數指針,這個函數帶有兩個參數,即窗口改變後的新寬度和新高度。一般,function是 glViewport(),顯示裁減後的新尺寸,重定義投影矩陣,以便使投影后圖像的比例與視點匹配,避免比例失調。若不調用 auxReshapeFunc(),缺省重定物體形狀的函數功能是調用一個二維的正射投影矩陣。運用輔助庫,窗口將在每一個事件改變後自動從新繪製。
void auxKeyFunction(GLint key,void(*function)(void))
定義鍵盤響應函數。參數function就是當按下key鍵時所調用的函數指針,輔助庫爲參數key定義了幾個常量:AUX_0至AUX_九、 AUX_A至AUX_Z、AUX_a至AUX_z、AUX_LEFT、AUX_RIGHT、AUX_UP、AUX_DOWN(方向鍵)、 AUX_ESCAPE、AUX_SPACE或AUX_RETURN。
void auxMouseFunc(GLint button,Glint mode,void(*function)(AUX_EVENTREC *))
定義鼠標響應函數。參數function就是當鼠標以mode方式做用於button時所調用的函數。參數button有 AUX_LEFTBUTTON、AUX_MIDDLEBUTTON或AUX_RIGHTBUTTON(以右手爲標準)。參數mode表明鼠標觸擊狀態,擊中時爲AUX_MOUSEDOWN,釋放時爲AUX_MOUSEUP。參數function必須帶一個參數,它是指向結構AUX_EVENNTREC的指針。當函數auxMouseFunc()被調用時將爲這個結構分配相應的內存。一般用法相似以下:
void function(AUX_EVENTREC *event) { GLint x,y; x=event->data[AUX_MOUSEX]; y=event->data[AUX_MOUSEY]; ... }
6.1.3 顏色表裝入
由於OpenGL自己沒有窗口系統,因此依賴於窗口系統的顏色映射就無法裝入顏色查找表。若是採用顏色表模式,就要用到輔助庫提供的用RGB值定義的單個顏色索引函數:
void auxSetOneColor(GLint index,GLfloat red,GLfloat green,GLfloat blue)
設置自定義顏色的索引。參數index即索引號,參數red、green、blue分別爲紅、綠、藍值,範圍在(0~1)內。
6.1.4 三維物體繪製
每組三維物體包括兩種形式:網狀體(wire)和實心體(solid)。網狀體沒有平面法向,而實心體有,能進行光影計算,有光照時採用實心體模型。下面這些函數的 參數都是定義物體大小的,能夠改變。
數組
功能
|
函數 |
繪製球
|
void auxWireSphere(GLdouble radius) void auxSolidSphere(GLdouble radius) |
繪製立方體
|
void auxWireCube(GLdouble size) void auxSolidCube(GLdouble size) |
繪製長方體
|
void auxWireBox(GLdouble width,GLdouble height,GLdouble depth) void auxSolidBox(GLdouble width,GLdouble height,GLdouble depth) |
繪製環形圓紋面
|
void auxWireTorus(GLdouble innerRadius,GLdouble outerRadius) void auxSolidTorus(GLdouble innerRadius,GLdouble outerRadius) |
繪製圓柱
|
void auxWireCylinder(GLdouble radius,GLdouble height) void auxSolidCylinder(GLdouble radius,GLdouble height) |
繪製二十面體
|
void auxWireIcosahedron(GLdouble radius) void auxSolidIcosahedron(GLdouble radius) |
繪製八面體
|
void auxWireOctahedron(GLdouble radius) void auxSolidOctahedron(GLdouble radius) |
繪製四面體
|
void auxWireTetrahedron(GLdouble radius) void auxSolidTetrahedron(GLdouble radius) |
繪製十二面體
|
void auxWireDodecahedron(GLdouble radius) void auxSolidDodecahedron(GLdouble radius) |
繪製圓錐
|
void auxWireCone(GLdouble radius,GLdouble height) void auxSolidCone(GLdouble radius,GLdouble height) |
繪製茶壺
|
void auxWireTeapot(GLdouble size) void aucSolidTeapot(GLdouble size) |
表6-1
|
以上物體均以各自中心爲原點繪製,全部座標都已單位化,能夠縮放。
6.1.5 背景過程管理
void auxIdleFunc(void *func)
定義空閒狀態執行函數。參數func是一個指針,指向所要執行的函數功能。當它爲零時,func執行無效。
6.1.6 程序運行
void auxMainLoop(void(*displayFunc)(void))
定義場景繪製循環函數。displayFunc指針指向場景繪製函數。當窗口須要更新或場景發生改變時,程序便調用它所指的函數,從新繪製場景。
6.二、輔助庫應用示例
下面舉一個輔助庫的應用例子,testaux.c:
例6-1 輔助庫應用例程 testaux.c
#include "glos.h" #include <GL/gl.h> #include <GL/glaux.h>
void myinit(void); void CALLBACK myReshape(GLsizei w,GLsizei h); void CALLBACK display(void);
void myinit(void) { glClearColor(0.0,0.0,0.0,0.0); glClear(GL_COLOR_BUFFER_BIT); }
void CALLBACK myReshape(GLsizei w,GLsizei h) { glViewport(0,0,w,h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); if(w<=h) glOrtho(-1.5,1.5,-1.5*(GLfloat)h/(GLfloat)w, 1.5*(GLfloat)h/(GLfloat)w,-10.0,10.0); else glOrtho(-1.5*(GLfloat)h/(GLfloat)w, 1.5*(GLfloat)h/(GLfloat)w,-1.5,1.5,-10.0,10.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); }
void CALLBACK display(void) { glColor3f(1.0,1.0,0.0); auxWireSphere(1.0); glFlush(); }
void main(void) { auxInitDisplayMode(AUX_SINGLE|AUX_RGBA); auxInitPosition(0,0,500,500); auxInitWindow("AUX_SAMPLE"); myinit(); auxReshapeFunc(myReshape); auxMainLoop(display); }
緩存
圖6-1 網狀球體 |
以上程序運行結果是在屏幕窗口內繪製一個黃色的網狀球體,這個程序充分體現了輔助庫的基本應用方法。
首先,在主函數中用輔助庫函數定義一個窗口auxInitWindow(),而後初始化顏色myinit(),這些在第一章中已說明。接下來是兩個十分重要的函數 auxReshapeFunc()和auxMainLoop(),參數都是一個函數指針,指向的都是回調函數(回調函數定義用CALLBACK說明)。
前者是窗口形狀重定函數,參數指針指向函數myReshape(),它的兩個參數就是窗口的新寬度和新高度。而後用glViewport(0, 0, w, h)重定視口,而且在新視口內從新定義投影矩陣,
glMatrixMode(GL_PROJECTION); glLoadIdentity(); if(w<=h) glOrtho(-1.5,1.5,-1.5*(GLfloat)h/(GLfloat)w, 1.5*(GLfloat)h/(GLfloat)w,-10.0,10.0); else glOrtho(-1.5*(GLfloat)h/(GLfloat)w, 1.5*(GLfloat)h/(GLfloat)w,-1.5,1.5,-10.0,10.0);
即先用glMatrixMode()說明當前矩陣操做與投影有關GL_PROJECTION,再用glLoadIdentity()將矩陣清爲單位矩陣,避免受其它矩陣操做的干擾;而後調用glOrtho()對物體進行正射投影,而且用判斷語句給出了兩種狀況,使投影后圖像的比例與視點匹配,避免比例失調。
再下來調用glMatrixMode()將矩陣操做改成對觀察物體有關的方式GL_MODELVIEW,一樣用 glLoadIdentity()清矩陣。後者是主函數循環函數,參數指針指向函數display(),即繪製物體。當窗口須要更新或物體發生改變時,程序便調用它從新繪製。以上例子是輔助庫的最基本應用,複雜的應用將在後續的章節中詳細介紹。
7、OpenGL建模
OpenGL基本庫提供了大量繪製各類類型圖元的方法,輔助庫也提供了很多描述複雜三維圖形的函數。這一章主要介紹基本圖元,如點、線、多邊形,有了這些圖元,就能夠創建比較複雜的模型了。
7.一、描述圖元
OpenGL是三維圖形的函數庫,它所定義的點、線、多邊形等圖元與通常的定義不太同樣,存在必定的差異。對編程者來講,可否理解兩者之間的差異十分重要。一種差異源於基於計算機計算的限制。OpenGL中全部浮點計算精度有限,故點、線、多邊形的座標值存在必定的偏差。另外一種差異源於位圖顯示的限制。以這種方式顯示圖形,最小的顯示圖元是一個象素,儘管每一個象素寬度很小,但它們仍然比數學上所定義的點或線寬要大得多。當用OpenGL 進行計算時,雖然是用一系列浮點值定義點串,但每一個點仍然是用單個象素顯示,只是近似擬合。
OpenGL圖元是抽象的幾何概念,不是真實世界中的物體,所以須用相關的數學模型來描述。
7.1.1 齊次座標(Homogeneous Coordinate)
在空間直角座標系中,任意一點可用一個三維座標矩陣[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。
7.1.2 點(Point)
用浮點值表示的點稱爲頂點(Vertex)。全部頂點在OpenGL內部計算時都做爲三維點處理,用二維座標(x, y)定義的點在OpenGL中默認z值爲0。全部頂點座標用齊次座標(x, y, z, w) 表示,若是w不爲0.0,這些齊次座標表示的頂點即爲三維空間點(x/w, y/w, z/w)。編程者能夠本身指定w值,但不多這樣作。通常來講,w缺省爲1.0。
7.1.3 線(Line)
在OpenGL中,線表明線段(Line Segment),不是數學意義上的那種沿軸兩個方向無限延伸的線。這裏的線由一系列頂點順次連結而成,有閉合和不閉合兩種。見圖7-1所示。
服務器
圖7-1 線段的兩種連結方式 |
7.1.4 多邊形(Polygon)
OpenGL中定義的多邊形是由一系列線段依次連結而成的封閉區域。這些線段不能交叉,區域內不能有空洞,多邊形必須在凸多邊形,不然不能被OpenGL函數接受。合法和非法多邊形圖示見圖7-2。
網絡
圖7-2 合法和非法多邊形 |
OpenGL多邊形能夠是平面多邊形,即全部頂點在一個平面上,也能夠是空間多邊形。更復雜的多邊形將在提升篇中介紹。
7.二、繪製圖元
7.2.1 定義頂點
在OpenGL中,全部幾何物體最終都由有必定順序的頂點集來描述。
函數glVertex{234}{sifd}[v](TYPE coords)能夠用二維、三維或齊次座標定義頂點。舉例以下:
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),最後一個例子表示用一個指針(或數組)定義頂點。
7.2.2 構造幾何圖元
在實際應用中,一般用一組相關的頂點序列以必定的方式組織起來定義某個幾何圖元,而不採用單獨定義多個頂點來構造幾何圖元。在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個),見圖7-3所示。
app
圖7-3 繪製多邊形或一組頂點 |
點函數glBegin(GLenum mode)標誌描述一個幾何圖元的頂點列表的開始,其參數mode表示幾何圖元的描述類型。全部類型及說明見表7-1所示,相應的圖示見圖7-4。
函數
類型 | 說明 |
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 | 連續填充四邊形串 |
表7-1 幾何圖元類型和說明
|
圖7-4 幾何圖元類型 |
函數glEnd()標誌頂點列表的結束。
從圖7-4中可看出,能夠採用許多方法構造幾何圖元,這些方法僅僅依賴於所給的頂點數據。
在glBegin()和glEnd()之間最重要的信息就是由函數glVertex*()定義的頂點,必要時也可爲每一個頂點指定顏色、法向、紋理座標或其餘,即調用相關的函數,見表7-2所示,具體用法之後會逐步介紹。
工具
函數 | 函數意義 |
glVertex*() | 設置頂點座標 |
glColor*() | 設置當前顏色 |
glIndex*() | 設置當前顏色表 |
glNormal*() | 設置法向座標 |
glEvalCoord*() | 產生座標 |
glCallList(),glCallLists() | 執行顯示列表 |
glTexCoord*() | 設置紋理座標 |
glEdgeFlag*() | 控制邊界繪製 |
glMaterial*() | 設置材質 |
表7-2 在glBegin()和glEnd()之間可調用的函數
|
看以下幾句:
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();
顏色等的設置只對當前點或後續點有效。上一例中第一個點是紅色,第二個點和第三個點都是藍色。其中設置綠色時,以後沒有頂點操做,而是設置藍色,故只有當前藍色對緊跟其後的兩個頂點有效。
爲了更好地理解構造幾何圖元函數的用法,下面舉一個簡單的例子:
例7-3 幾何圖元構造例程(drawgeom.c)
#include "glos.h" #include<GL/gl.h> #include<GL/glaux.h>
void myinit(void); void DrawMyObjects(void); void CALLBACK myReshape(GLsizei w,GLsizei h); void CALLBACK display(void);
void myinit(void) { glClearColor(0.0,0.0,0.0,0.0); glClear(GL_COLOR_BUFFER_BIT); glShadeModel(GL_FLAT); }
void CALLBACK myReshape(GLsizei w,GLsizei h) { glViewport(0,0,w,h); glMatrixMode(GL_PROJECTION); glLoadIdentity();
if(w<=h) glOrtho(-20.0,20.0,-20.0*(GLfloat)h/(GLfloat)w, 20.0*(GLfloat)h/(GLfloat)w,-50.0,50.0); else glOrtho(-20.0*(GLfloat)h/(GLfloat)w, 20.0*(GLfloat)h/(GLfloat)w,-20.0,20.0,-50.0,50.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); }
void CALLBACK display(void) { glColor3f(1.0,1.0,0.0); DrawMyObjects(); glFlush(); }
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 main(void) { auxInitDisplayMode(AUX_SINGLE|AUX_RGBA); auxInitPosition(0,0,500,500); auxInitWindow("Geometric Primitive Types"); myinit(); auxReshapeFunc(myReshape); auxMainLoop(display); }
以上程序運行結果就是圖7-4所示的內容,這個例子很好地說明了幾何圖元的類型及顏色等函數的用法。但願讀者本身仔細分析每一個物體的繪製方法,體會其中的關鍵之處,達到觸類旁通的效果。固然,還可利用上一章輔助庫中提供的基本三維圖元構造比較複雜的物體,你不妨也試一試。
8、OpenGL變換
OpenGL變換是本篇的重點內容,它包括計算機圖形學中最基本的三維變換,即幾何變換、投影變換、裁剪變換、視口變換,以及針對OpenGL的特殊變換概念理解和用法,如相機模擬、矩陣堆棧等。學好了這章,纔開始真正走進三維世界。
8.一、從三維空間到二維平面
8.1.1 相機模擬
在真實世界裏,全部的物體都是三維的。可是,這些三維物體在計算機世界中卻必須以二維平面物體的形式表現出來。那麼,這些物體是怎樣從三維變換到二維的呢?下面咱們採用相機(Camera)模擬的方式來說述這個概念,如圖8-1所示。