OpenGL基礎圖形編程

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正是提供一種直觀的編程環境,它提供的一系列函數大大地簡化了三維圖形程序。例如:程序員

  • OpenGL提供一系列的三維圖形單元供開發者調用。
  • OpenGL提供一系列的圖形變換函數。
  • OpenGL提供一系列的外部設備訪問函數,使開發者能夠方便地訪問鼠標、鍵盤、空間球、數據手套等這種直觀的三維圖形開發環境體現了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概念創建
算法

 

<>function StorePage(){d=document;t=d.selection?(d.selection.type!='None'?d.selection.createRange().text:''):(d.getSelection?d.getSelection():'');void(keyit=window.open('http://www.365key.com/storeit.aspx?t='+escape(d.title)+'&u='+escape(d.location.href)+'&c='+escape(t),'keyit','scrollbars=no,width=475,height=575,left=75,top=20,status=no,resizable=yes'));keyit.focus(); 2.一、OpenGL基本理解   OpenGL是一個與硬件圖形發生器的軟件接口,它包括了100多個圖形操做函數,開發者能夠利用這些函數來構造景物模型、進行三維圖形交互軟件的開發。正如上一章所述,OpenGL是一個高性能的圖形開發軟件包。OpenGL支持網絡,在網絡系統中用戶能夠在不一樣的圖形終端上運行程序顯示圖形。 OpenGL做爲一個與硬件獨立的圖形接口,它不提供與硬件密切相關的設備操做函數,同時,它也不提供描述相似於飛機、汽車、分子形狀等複雜形體的圖形操做函數。用戶必須從點、線、面等最基本的圖形單元開始構造本身的三維模型。固然,象OpenInventor那樣更高一級的基於OpenGL的三維圖形建模開發軟件包將提供方便的工具。所以OpenGL的圖形操做函數十分基本、靈活。例如OpenGL中的模型繪製過程就多種多樣,內容十分豐富,OpenGL提供瞭如下的對三維物體的繪製方式:
  • 網格線繪圖方式wireframe) 這種方式僅繪製三維物體的網格輪廓線。
  • 深度優先網格線繪圖方式depth_cued) 用網格線方式繪圖,增長模擬人眼看物體同樣,遠處的物體比近處的物體要暗些。
  • 反走樣網格線繪圖方式antialiased) 用網格線方式繪圖,繪圖時採用反走樣技術以減小圖形線條的良莠不齊。
  • 平面消隱繪圖方式flat_shade) 對模型的隱藏面進行消隱,對模型的平面單元按光照程度進行着色但不進行光滑處理。
  • 光滑消隱繪圖方式smooth_shade) 對模型進行消隱按光照渲染着色的過程當中再進行光滑處理,這種方式更接近於現實。
  • 加陰影和紋理的繪圖方式shadows、textures) 在模型表面貼上紋理甚至於加上光照陰影,使得三維景觀象照片同樣。
  • 運動模糊的繪圖方式motion-blured) 模擬物體運動時人眼觀察所感受的動感現象。
  • 大氣環境效果atmosphere-effects) 在三維景觀中加入如霧等大氣環境效果,令人身臨其境。
  • 深度域效果depth-of-effects) 相似於照相機鏡頭效果,模型在聚焦點處清晰,反之則模糊。
  這些三維物體繪圖和特殊效果處理方式,說明OpenGL已經可以模擬比較複雜的三維物體或天然景觀,這就是咱們所面對的OpenGL。 2.二、OpenGL工做流程   整個OpenGL的基本工做流程以下圖:   其中幾何頂點數據包括模型的頂點集、線集、多邊形集,這些數據通過流程圖的上部,包括運算器、逐個頂點操做等;圖像數據包括象素集、影像集、位圖集等,圖像象素數據的處理方式與幾何頂點數據的處理方式是不一樣的,但它們都通過光柵化、逐個片元( Fragment)處理直至把最後的光柵數據寫入幀緩衝器。在OpenGL中的全部數據包括幾何頂點數據和象素數據均可以被存儲在顯示列表中或者當即能夠獲得處理。OpenGL中,顯示列表技術是一項重要的技術。   OpenGL要求把全部的幾何圖形單元都用頂點來描述,這樣運算器和逐個頂點計算操做均可以針對每一個頂點進行計算和操做,而後進行光柵化造成圖形碎片;對於象素數據,象素操做結果被存儲在紋理組裝用的內存中,再象幾何頂點操做同樣光柵化造成圖形片元。   整個流程操做的最後,圖形片元都要進行一系列的逐個片元操做,這樣最後的象素值BZ送入幀緩衝器實現圖形的顯示。 2.三、OpenGL圖形操做步驟   在上一節中說明了OpenGL的基本工做流程,根據這個流程能夠概括出在OpenGL中進行主要的圖形操做直至在計算機屏幕上渲染繪製出三維圖形景觀的基本步驟:   1)根據基本圖形單元創建景物模型,而且對所創建的模型進行數學描述(OpenGL中把:點、線、多邊形、圖像和位圖都做爲基本圖形單元)。   2)把景物模型放在三維空間中的合適的位置,而且設置視點(viewpoint)以觀察所感興趣的景觀。   3)計算模型中全部物體的色彩,其中的色彩根據應用要求來肯定,同時肯定光照條件、紋理粘貼方式等。   4)把景物模型的數學描述及其色彩信息轉換至計算機屏幕上的象素,這個過程也就是光柵化(rasterization)。   在這些步驟的執行過程當中,OpenGL可能執行其餘的一些操做,例如自動消隱處理等。另外,景物光柵化以後被送入幀緩衝器以前還能夠根據須要對象素數據進行操做。

3、WindowsNT下的OpenGL
3.一、Windows NT下的OpenGL函數   如前面的章節所述,Windows NT下的OpenGL一樣包含100多個庫函數,這些函數都按必定的格式來命名,即每一個函數都以gl開頭。Windows NT下的OpenGL除了具備基本的OpenGL函數外,還支持其餘四類函數:
相應函數 具體說明
OpenGL實用庫 43個函數,每一個函數以glu開頭。
OpenGL輔助庫 31個函數,每一個函數以aux開頭。
Windows專用庫函數(WGL) 6個函數,每一個函數以wgl開頭。
Win32 API函數 5個函數,函數前面沒有專用前綴。
  在OpenGL中有115個核心函數,這些函數是最基本的,它們能夠在任何OpenGL的工做平臺上應用。這些函數用於創建各類各樣的形體,產生光照效果,進行反走樣以及進行紋理映射,進行投影變換等等。因爲這些核心函數有許多種形式並可以接受不一樣類型的參數,實際上這些函數能夠派生出300 多個函數。   OpenGL的實用函數是比OpenGL核心函數更高一層的函數,這些函數是經過調用核心函數來起做用的。這些函數提供了十分簡單的用法,從而減輕了開發者的編程負擔。OpenGL的實用函數包括紋理映射、座標變換、多邊形分化、繪製一些如橢球、圓柱、茶壺等簡單多邊形實體(本指南將詳細講述這些函數的具體用法)等。這部分函數象核心函數同樣在任何OpenGL平臺均可以應用。   OpenGL的輔助庫是一些特殊的函數,這些函數原本是用於初學者作簡單的練習之用,所以這些函數不能在全部的OpenGL平臺上使用,在Windows NT環境下能夠使用這些函數。這些函數使用簡單,它們能夠用於窗口管理、輸入輸出處理以及繪製一些簡單的三維形體。爲了使OpenGL的應用程序具備良好的移植性,在使用OpenGL輔助庫的時候應謹慎。   6個WGL函數是用於鏈接OpenGL與Windows NT的,這些函數用於在Windows NT環境下的OpenGL窗口可以進行渲染着色,在窗口內繪製位圖字體以及把文本放在窗口的某一位置等。這些函數把Windows與OpenGL揉合在一塊兒。最後的5個Win32函數用於處理象素存儲格式和雙緩衝區,顯然這些函數僅僅可以用於Win32系統而不能用於其它OpenGL平臺。 3.二、OpenGL基本功能   OpenGL可以對整個三維模型進行渲染着色,從而繪製出與客觀世界十分相似的三維景象。另外OpenGL還能夠進行三維交互、動做模擬等。具體的功能主要有如下這些內容。
  • 模型繪製 OpenGL可以繪製點、線和多邊形。應用這些基本的形體,咱們能夠構造出幾乎全部的三維模型。OpenGL一般用模型的多邊形的頂點來描述三維模型。如何經過多邊形及其頂點來描述三維模型,在指南的在後續章節會有詳細的介紹。
  • 模型觀察 在創建了三維景物模型後,就須要用OpenGL描述如何觀察所創建的三維模型。觀察三維模型是經過一系列的座標變換進行的。模型的座標變換在使觀察者可以在視點位置觀察與視點相適應的三維模型景觀。在整個三維模型的觀察過程當中,投影變換的類型決定觀察三維模型的觀察方式,不一樣的投影變換獲得的三維模型的景象也是不一樣的。最後的視窗變換則對模型的景象進行裁剪縮放,即決定整個三維模型在屏幕上的圖象。
  • 顏色模式的指定 OpenGL 應用了一些專門的函數來指定三維模型的顏色。程序員能夠選擇二個顏色模式,即RGBA模式和顏色表模式。在RGBA模式中,顏色直接由RGB值來指定;在顏色表模式中,顏色值則由顏色表中的一個顏色索引值來指定。程序員還能夠選擇平面着色和光滑着色二種着色方式對整個三維景觀進行着色。
  • 光照應用 用OpenGL繪製的三維模型必須加上光照才能更加與客觀物體類似。OpenGL提供了管理四種光(輻射光、環境光、鏡面光和漫反射光)的方法,另外還能夠指定模型表面的反射特性。
  • 圖象效果加強 OpenGL提供了一系列的加強三維景觀的圖象效果的函數,這些函數經過反走樣、混合和霧化來加強圖象的效果。反走樣用於改善圖象中線段圖形的鋸齒而更平滑,混合用於處理模型的半透明效果,霧使得影像從視點到遠處逐漸褪色,更接近於真實。
  • 位圖和圖象處理 OpenGL還提供了專門對位圖和圖象進行操做的函數。
  • 紋理映射 三維景物因缺乏景物的具體細節而顯得不夠真實,爲了更加逼真地表現三維景物,OpenGL提供了紋理映射的功能。OpenGL提供的一系列紋理映射函數使得開發者能夠十分方便地把真實圖象貼到景物的多邊形上,從而能夠在視窗內繪製逼真的三維景觀。
  • 實時動畫 爲了得到平滑的動畫效果,須要先在內存中生成下一幅圖象,而後把已經生成的圖象從內存拷貝到屏幕上,這就是OpenGL的雙緩存技術(double buffer)。OpenGL提供了雙緩存技術的一系列函數。
  • 交互技術 目前有許多圖形應用須要人機交互,OpenGL提供了方便的三維圖形人機交互接口,用戶能夠選擇修改三維景觀中的物體。
3.三、Windows NT下OpenGL的結構   OpenGL的做用機制是客戶(client)/服務器(sever)機制,即客戶(用OpenGL繪製景物的應用程序)向服務器(即OpenGL內核)發佈OpenGL命令,服務器則解釋這些命令。大多數狀況下,客戶和服務器在同一機器上運行。正是OpenGL的這種客戶/服務器機制,OpenGL能夠十分方便地在網絡環境下使用。所以Windows NT下的OpenGL是網絡透明的。正象Windows的圖形設備接口(GDI)把圖形函數庫封裝在一個動態連接庫(Windows NT下的GDI32.DLL)內同樣,OpenGL圖形庫也被封裝在一個動態連接庫內(OPENGL32.DLL)。受客戶應用程序調用的OpenGL函數都先在OPENGL32.DLL中處理,而後傳給服務器WINSRV.DLL。OpenGL的命令再次獲得處理而且直接傳給Win32的設備驅動接口(Device Drive Interface,DDI),這樣就把通過處理的圖形命令送給視頻顯示驅動程序。下圖簡要說明這個過程: 圖3-1 OpenGL在Windows NT下運行機制
  在三維圖形加速卡的GLINT圖形加速芯片的加速支持下,二個附加的驅動程序被加入這個過程當中。一個OpenGL可安裝客戶驅動程序(Installable Client Driver,ICD)被加在客戶這一邊,一個硬件指定DDI(Hardware-specific DDI)被加在服務器這邊,這個驅動程序與Wind32 DDI是同一級別的。
圖3-2 在三維圖形加速下OpenGL運行機制





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
編程

表5-1 命令前綴和參數數據類型
  OpenGL的庫函數命名方式頗有規律,瞭解這種規律後閱讀和編寫程序都比較容易方便。   首先,每一個庫函數有前綴gl、glu、glx或aux,表示此函數分屬於基本庫、實用庫、X窗口擴充庫或輔助庫,其後的函數名頭字母大寫,後綴是參數類型的簡寫,取i、f,參見表5-1。例:
  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()表示用一個指針指向全部類型的向量來定義一系列頂點座標值。   最後,OpenGL也定義GLvoid類型,若是用C語言編寫,能夠用它替代void類型。




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所示。

圖8-1 相機模擬


  實際上,從三維空間到二維平面,就如同用相機拍照同樣,一般都要經歷如下幾個步驟 (括號內表示的是相應的圖形學概念):
  第一步,將相機置於三角架上,讓它對準三維景物(視點變換,Viewing Transformation)。
  第二步,將三維物體放在適當的位置(模型變換,Modeling Transformation)。
  第三步,選擇相機鏡頭並調焦,使三維物體投影在二維膠片上(投影變換,Projection Transformation)。
  第四步,決定二維像片的大小(視口變換,Viewport Transformation)。
  這樣,一個三維空間裏的物體就能夠用相應的二維平面物體表示了,也就能在二維的電腦屏幕上正確顯示了。

  8.1.2 三維圖形顯示流程
  運用相機模擬的方式比較通俗地講解了三維圖形顯示的基本過程,但在具體應用OpenGL函數庫編程時,還必須瞭解三維圖形世界中的幾個特殊座標系的概念,以及用這些概念表達的三維圖形顯示流程。
  計算機自己只能處理數字,圖形在計算機內也是以數字的形式進行加工和處理的。你們都知道,座標創建了圖形和數字之間的聯繫。爲了使被顯示的物體數字化,要在被顯示的物體所在的空間中定義一個座標系。這個座標系的長度單位和座標軸的方向要適合對被顯示物體的描述,這個座標系稱爲世界座標系。
  計算機對數字化的顯示物體做了加工處理後,要在圖形顯示器上顯示,這就要在圖形顯示器屏幕上定義一個二維直角座標系,這個座標系稱爲屏幕座標系。這個座標系座標軸的方向一般取成平行於屏幕的邊緣,座標原點取在左下角,長度單位常取成一個象素的長度,大小能夠是整型數。
  爲了使顯示的物體能以合適的位置、大小和方向顯示出來,必需要經過投影。投影的方法有兩種,即正射投影和透視投影。
  有時爲了突出圖形的一部分,只把圖形的某一部分顯示出來,這時能夠定義一個三維視景體(Viewing Volume)。正射投影時通常是一個長方體的視景體,透視投影時通常是一個棱臺似的視景體。只有視景體內的物體能被投影在顯示平面上,其餘部分則不能。在屏幕窗口內能夠定義一個矩形,稱爲視口(Viewport),視景體投影后的圖形就在視口內顯示。
  爲了適應物理設備座標和視口所在座標的差異,還要做一適應物理座標的變換。這個座標系稱爲物理設備座標系。根據上面所述,三維圖形的顯示流程應如圖8-2所示。

圖8-2 三維圖形的顯示流程


  8.1.3 基本變換簡單分析
  下面舉一個簡單的變換例子,cube.c:

  例8-4 簡單變換例程cube.c

  #include "glos.h"   #include <GL/gl.h>   #include <GL/glu.h>   #include <GL/glaux.h>
  void myinit(void);   void CALLBACK myReshape(GLsizei w, GLsizei h);   void CALLBACK display(void);
  void CALLBACK display (void)   {     glClear(GL_COLOR_BUFFER_BIT);     glColor3f (1.0, 1.0, 1.0);     glLoadIdentity (); /* clear the matrix */     glTranslatef (0.0, 0.0, -5.0); /* viewing transformation */     glScalef (1.0, 2.0, 1.0); /* modeling transformation */     auxWireCube(1.0); /* draw the cube */     glFlush();   }
  void myinit (void)   {     glShadeModel (GL_FLAT);   }
  void CALLBACK myReshape(GLsizei w, GLsizei h)   {     glMatrixMode (GL_PROJECTION); /* prepare for and then */     glLoadIdentity (); /* define the projection */     glFrustum (-1.0, 1.0, -1.0, 1.0, 1.5, 20.0); /* transformation */     glMatrixMode (GL_MODELVIEW); /* back to modelview matrix */     glViewport (0, 0, w, h); /* define the viewport */   }
  void main(void)   {     auxInitDisplayMode (AUX_SINGLE | AUX_RGBA);     auxInitPosition (0, 0, 500, 500);     auxInitWindow ("Perspective 3-D Cube");     myinit ();     auxReshapeFunc (myReshape);     auxMainLoop(display);   }
  以上程序運行結果就是繪製一個三維的正面透視立方體。其中已經用到了相機模擬中提到的四種基本變換,即視點變換、模型變換、投影變換和視口變換。

圖8-3 三維的正面透視立方體


  下面簡單分析一下整個程序過程:
  1)視點變換。視點變換是在視點座標系中進行的。視點座標系於通常的物體所在的世界座標系不一樣,它遵循左手法則,即左手大拇指指向Z正軸,與之垂直的四個手指指向X正軸,四指彎曲90度的方向是Y正軸。而世界座標系遵循右手法則的。如圖8-4所示。當矩陣初始化glLoadIdentity()後,調用glTranslatef()做視點變換。函數參數(x, y, z)表示視點或相機在視點座標系中移動的位置,這裏z=-5.0,意思是將相機沿Z負軸移動5個單位。
  一般相機位置缺省值同場景中的物體同樣,都在原點處,並且相機初始方向都指向Z負軸。
  這裏相機移走後,仍然對準立方體。若是相機須要指向另外一方向,則調用glRotatef()能夠改變。

圖8-4 視點座標系與世界座標系


  2)模型變換。模型變換是在世界座標系中進行的。在這個座標系中,能夠對物體實施平移 glTranslatef()、旋轉glRotatef()和放大縮小glScalef()。例子裏只對物體進行比例變換,glScalef(sx, sy, sz)的三個參數分別是X、Y、Z軸向的比例變換因子。缺省時都爲1.0,即物體沒變化。程序中物體Y軸比例爲2.0,其他都爲1.0,就是說將立方體變成長方體。
  3)投影變換。投影變換相似於選擇相機的鏡頭。本例中調用了一個透視投影函數 glFrustum(),在調用它以前先要用glMatrixMode()說明當前矩陣方式是投影GL_PROJECTION。這個投影函數一共有六個參數,由它們能夠定義一個棱臺似的視景體。即視景體內的部分可見,視景體外的部分不可見,這也就包含了三維裁剪變換。
  4)視口變換。視口變換就是將視景體內投影的物體顯示在二維的視口平面上。一般,都調用函數glViewport()來定義一個視口,這個過程相似於將照片放大或縮小。
  總而言之,一旦全部必要的變換矩陣被指定後,場景中物體的每個頂點都要按照被指定的變換矩陣序列逐一進行變換。注意:OpenGL 中的物體座標一概採用齊次座標,即(x, y, z, w),故全部變換矩陣都採用4X4矩陣。通常說來,每一個頂點先要通過視點變換和模型變換,而後進行指定的投影,若是它位於視景體外,則被裁剪掉。最後,餘下的已經變換過的頂點x、y、z座標值都用比例因子w除,即x/w、y/w、z/w,再映射到視口區域內,這樣才能顯示在屏幕上。

8.二、幾何變換
  實際上,上述所說的視點變換和模型變換本質上都是一回事,即圖形學中的幾何變換。
  只是視點變換通常只有平移和旋轉,沒有比例變換。當視點進行平移或旋轉時,視點座標系中的物體就至關於在世界座標系中做反方向的平移或旋轉。所以,從某種意義上講,兩者能夠統一,只是各自出發點不同而已。讀者能夠根據具體狀況,選擇其中一個角度去考慮,這樣便於理解。

  8.2.1 兩個矩陣函數解釋
  這裏先解釋兩個基本OpenGL矩陣操做函數,便於之後章節的講述。函數解釋以下:

  void glLoadMatrix{fd}(const TYPE *m)
  設置當前矩陣中的元素值。函數參數*m是一個指向16個元素(m0, m1, ..., m15)的指針,這16個元素就是當前矩陣M中的元素,其排列方式以下:

M = | m0 m4 m8 m12 | | m1 m5 m9 m13 | | m2 m6 m10 m14 | | m3 m7 m11 M15 |


  void glMultMatrix{fd}(const TYPE *m)
  用當前矩陣去乘*m所指定的矩陣,並將結果存放於*m中。當前矩陣能夠是用glLoadMatrix() 指定的矩陣,也能夠是其它矩陣變換函數的綜合結果。
  當幾何變換時,調用OpenGL的三個變換函數glTranslate*()、glRotate*()和glScale*(),實質上至關於產生了一個近似的平移、旋轉和比例矩陣,而後調用glMultMatrix()與當前矩陣相乘。可是直接調用這三個函數程序運行得快一些,因OpenGL自動能計算矩陣。

  8.2.2 平移
  平移變換函數以下:

  void glTranslate{fd}(TYPE x,TYPE y,TYPE z)
  三個函數參數就是目標分別沿三個軸向平移的偏移量。這個函數表示用這三個偏移量生成的矩陣乘以當前矩陣。當參數是(0.0,0.0,0.0)時,表示對函數glTranslate*()的操做是單位矩陣,也就是對物體沒有影響。平移示意如圖8-5所示。

圖8-5 平移示意圖


  8.2.3 旋轉
  旋轉變換函數以下:

  void glRotate{fd}(TYPE angle,TYPE x,TYPE y,TYPE z)

  函數中第一個參數是表示目標沿從點(x, y, z)到原點的方向逆時針旋轉的角度,後三個參數是旋轉的方向點座標。這個函數表示用這四個參數生成的矩陣乘以當前矩陣。當角度參數是0.0時,表示對物體沒有影響。旋轉示意如圖8-6所示。

圖8-6 旋轉示意圖


  8.2.3 縮放和反射
  縮放和反射變換函數以下:

  void glScale{fd}(TYPE x,TYPE y,TYPE z)
  三個函數參數值就是目標分別沿三個軸向縮放的比例因子。這個函數表示用這三個比例因子生成的矩陣乘以當前矩陣。這個函數能完成沿相應的軸對目標進行拉伸、壓縮和反射三項功能。當參數是(1.0, 1.0, 1.0)時,表示對函數glScale*()操做是單位矩陣,也就是對物體沒有影響。當其中某個參數爲負值時,表示將對目標進行相應軸的反射變換,且這個參數不爲1.0,則還要進行相應軸的縮放變換。最好不要令三個參數值都爲零,這將致使目標沿三軸都縮爲零。縮放和反射示意如圖8-7所示。

圖8-7 縮放和反射示意圖


  8.2.5 幾何變換舉例
  以上介紹了三個基本幾何變換函數,下面舉一個簡單的例子進一步說明它們的用法。程序以下:

  例 8-5 幾何變換例程geomtrsf.c

  #include "glos.h"   #include <GL/gl.h>   #include <GL/glu.h>   #include <GL/glaux.h>
  void myinit(void);   void draw_triangle(void);   void CALLBACK display(void);   void CALLBACK myReshape(GLsizei w, GLsizei h);
  void draw_triangle(void)   {     glBegin(GL_LINE_LOOP);       glVertex2f(0.0, 25.0);       glVertex2f(25.0, -25.0);       glVertex2f(-25.0, -25.0);     glEnd();   }
  void CALLBACK display(void)   {     glClearColor (0.0, 0.0, 0.0, 1.0);     glClear (GL_COLOR_BUFFER_BIT);
    /* draw an original triangle */     glLoadIdentity ();     glColor3f (1.0, 1.0, 1.0);  /* white */     draw_triangle ();
    /* translating a triangle along X_axis */     glLoadIdentity ();     glTranslatef (-20.0, 0.0, 0.0);     glColor3f(1.0,0.0,0.0);   /* red */     draw_triangle ();
    /* scaling a triangle along X_axis by 1.5 and along Y_axis by 0.5 */     glLoadIdentity();     glScalef (1.5, 0.5, 1.0);     glColor3f(0.0,1.0,0.0);   /* green */     draw_triangle ();
    /* rotating a triangle in a counterclockwise direction about Z_axis */     glLoadIdentity ();     glRotatef (90.0, 0.0, 0.0, 1.0);     glColor3f(0.0,0.0,1.0);   /* blue */     draw_triangle ();
    /* scaling a triangle along Y_axis and reflecting it about Y_axis */     glLoadIdentity();     glScalef (1.0, -0.5, 1.0);     glColor3f(1.0,1.0,0.0);   /* yellow */     draw_triangle ();
    glFlush();   }
  void myinit (void)   {     glShadeModel (GL_FLAT);   }
  void CALLBACK myReshape(GLsizei w, GLsizei h)   {     glViewport(0, 0, w, h);     glMatrixMode(GL_PROJECTION);     glLoadIdentity();     if (w <= h)       glOrtho(-50.0, 50.0, -50.0*(GLfloat)h/(GLfloat)w, 50.0*(GLfloat)h/(GLfloat)w,-1.0,1.0);     else       glOrtho(-50.0*(GLfloat)w/(GLfloat)h, 50.0*(GLfloat)w/(GLfloat)h, -50.0, 50.0,-1.0,1.0);     glMatrixMode(GL_MODELVIEW);   }
  void main(void)   {     auxInitDisplayMode (AUX_SINGLE | AUX_RGBA);     auxInitPosition (0, 0, 500, 500);     auxInitWindow ("Geometric Transformations");     myinit ();     auxReshapeFunc (myReshape);     auxMainLoop(display);   }
  以上程序運行結果:第一個白色三角形是原始三角形,第二個紅色三角形是白三角沿X 負軸平移後的三角形,第三個綠色三角形是白三角分別沿X軸和Y軸比例變換後的三角形,第四個藍色三角形是白三角繞Z正軸逆時針轉90度後的三角形,第五個黃色三角形是白三角沿Y軸方向縮小一倍且相對於X軸做反射後造成的三角形。

圖8-8 三角形的幾何變換


8.三、投影變換
  投影變換是一種很關鍵的圖形變換,OpenGL中只提供了兩種投影方式,一種是正射投影,另外一種是透視投影。無論是調用哪一種投影函數,爲了不沒必要要的變換,其前面必須加上如下兩句:

  glMAtrixMode(GL_PROJECTION);   glLoadIdentity();
  事實上,投影變換的目的就是定義一個視景體,使得視景體外多餘的部分裁剪掉,最終圖像只是視景體內的有關部分。本指南將詳細講述投影變換的概念以及用法。

  8.3.1 正射投影Orthographic Projection
  正射投影,又叫平行投影。這種投影的視景體是一個矩形的平行管道,也就是一個長方體,如圖8-9所示。正射投影的最大一個特色是不管物體距離相機多遠,投影后的物體大小尺寸不變。這種投影一般用在建築藍圖繪製和計算機輔助設計等方面,這些行業要求投影后的物體尺寸及相互間的角度不變,以便施工或製造時物體比例大小正確。

圖8-9 正射投影視景體


  OpenGL正射投影函數共有兩個,這在前面幾個例子中已用過。一個函數是:

  void glOrtho(GLdouble left,GLdouble right,GLdouble bottom,GLdouble top,       GLdouble near,GLdouble far)
  它建立一個平行視景體。實際上這個函數的操做是建立一個正射投影矩陣,而且用這個矩陣乘以當前矩陣。其中近裁剪平面是一個矩形,矩形左下角點三維空間座標是(left,bottom,-near),右上角點是(right,top,-near);遠裁剪平面也是一個矩形,左下角點空間座標是(left,bottom,-far),右上角點是(right,top,-far)。全部的near和far值同時爲正或同時爲負。若是沒有其餘變換,正射投影的方向平行於Z軸,且視點朝向Z負軸。
  這意味着物體在視點前面時far和near都爲負值,物體在視點後面時far和near都爲正值。另外一個函數是:

  void gluOrtho2D(GLdouble left,GLdouble right,GLdouble bottom,GLdouble top)
  它是一個特殊的正射投影函數,主要用於二維圖像到二維屏幕上的投影。它的near和far缺省值分別爲-1.0和1.0,全部二維物體的Z座標都爲0.0。所以它的裁剪面是一個左下角點爲(left,bottom)、右上角點爲(right,top)的矩形。

  8.3.2 透視投影Perspective Projection
  透視投影符合人們心理習慣,即離視點近的物體大,離視點遠的物體小,遠到極點即爲消失,成爲滅點。它的視景體相似於一個頂部和底部都被切除掉的棱椎,也就是棱臺。這個投影一般用於動畫、視覺仿真以及其它許多具備真實性反映的方面。
  OpenGL透視投影函數也有兩個,其中函數glFrustum()在8.1.3節中提到過,它所造成的視景體如圖8-10所示。

圖8-10 函數glFrustum()透視投影視景體


  這個函數原型爲:

  void glFrustum(GLdouble left,GLdouble Right,GLdouble bottom,GLdouble top,       GLdouble near,GLdouble far);

  它建立一個透視視景體。其操做是建立一個透視投影矩陣,而且用這個矩陣乘以當前矩陣。這個函數的參數只定義近裁剪平面的左下角點和右上角點的三維空間座標,即(left,bottom,-near)和(right,top,-near);最後一個參數far是遠裁剪平面的Z負值,其左下角點和右上角點空間座標由函數根據透視投影原理自動生成。near和far表示離視點的遠近,它們總爲正值。
  另外一個函數是:

  void gluPerspective(GLdouble fovy,GLdouble aspect,GLdouble zNear, GLdouble zFar);
  它也建立一個對稱透視視景體,但它的參數定義於前面的不一樣,如圖8-11所示。其操做是建立一個對稱的透視投影矩陣,而且用這個矩陣乘以當前矩陣。參數 fovy定義視野在X-Z平面的角度,範圍是[0.0, 180.0];參數aspect是投影平面寬度與高度的比率;參數zNear和Far分別是遠近裁剪面沿Z負軸到視點的距離,它們總爲正值。

圖8-11 函數gluPerspective()透視投影視景體


  以上兩個函數缺省時,視點都在原點,視線沿Z軸指向負方向。兩者的應用實例將在後續章節中介紹。

8.四、裁剪變換
  在OpenGL中,空間物體的三維裁剪變換包括兩個部分:視景體裁剪和附加平面裁剪。視景體裁剪已經包含在投影變換裏,前面已述,這裏再也不重複。下面簡單講一下平面裁剪函數的用法。
  除了視景體定義的六個裁剪平面(上、下、左、右、前、後)外,用戶還可本身再定義一個或多個附加裁剪平面,以去掉場景中無關的目標,如圖8-12所示。

圖8-12 附加裁剪平面和視景體


  附加平面裁剪函數爲:

  void glClipPlane(GLenum plane,Const GLdouble *equation);
  函數定義一個附加的裁剪平面。其中參數equation指向一個擁有四個係數值的數組,這四個係數分別是裁剪平面Ax+By+Cz+D=0的A、B、 C、D值。所以,由這四個係數就能肯定一個裁剪平面。參數plane是GL_CLIP_PLANEi(i=0,1,...),指定裁剪面號。
  在調用附加裁剪函數以前,必須先啓動glEnable(GL_CLIP_PLANEi),使得當前所定義的裁剪平面有效;當再也不調用某個附加裁剪平面時,可用glDisable(GL_CLIP_PLANEi)關閉相應的附加裁剪功能。
  下面這個例子不只說明了附加裁剪函數的用法,並且調用了gluPerspective()透視投影函數,讀者能夠細細體會其中的用法。例程以下:

  例8-6 裁剪變換例程clipball.c)

  #include "glos.h"   #include <GL/gl.h>   #include <GL/glu.h>   #include <GL/glaux.h>
  void myinit(void);   void CALLBACK myReshape(GLsizei w, GLsizei h);   void CALLBACK display(void);
  void CALLBACK display(void)   {     GLdouble eqn[4] = {1.0, 0.0, 0.0, 0.0};
    glClear(GL_COLOR_BUFFER_BIT);
    glColor3f (1.0, 0.0, 1.0);     glPushMatrix();     glTranslatef (0.0, 0.0, -5.0);
    /* clip the left part of wire_sphere : x<0 */     glClipPlane (GL_CLIP_PLANE0, eqn);     glEnable (GL_CLIP_PLANE0);     glRotatef (-90.0, 1.0, 0.0, 0.0);     auxWireSphere(1.0);     glPopMatrix();     glFlush();   }
  void myinit (void)   {     glShadeModel (GL_FLAT);   }
  void CALLBACK myReshape(GLsizei w, GLsizei h)   {     glViewport(0, 0, w, h);     glMatrixMode(GL_PROJECTION);     glLoadIdentity();     gluPerspective(60.0, (GLfloat) w/(GLfloat) h, 1.0, 20.0);     glMatrixMode(GL_MODELVIEW);   }
  void main(void)   {     auxInitDisplayMode (AUX_SINGLE | AUX_RGB);     auxInitPosition (0, 0, 500, 500);     auxInitWindow ("Arbitrary Clipping Planes");     myinit ();     auxReshapeFunc (myReshape);     auxMainLoop(display);   }

圖8-13 剪取後的網狀半球體


8.五、視口變換
  在前面幾節內容中已相繼提到過視口變換,這一節將針對OpenGL來說述視口變換的原理及其相關函數的用法。運用相機模擬方式,咱們很容易理解視口變換就是相似於照片的放大與縮小。在計算機圖形學中,它的定義是將通過幾何變換、投影變換和裁剪變換後的物體顯示於屏幕窗口內指定的區域內,這個區域一般爲矩形,稱爲視口。OpenGL中相關函數是:

  glViewport(GLint x,GLint y,GLsizei width, GLsizei height);
  這個函數定義一個視口。函數參數(x, y)是視口在屏幕窗口座標系中的左下角點座標,參數width和height分別是視口的寬度和高度。缺省時,參數值即(0, 0, winWidth, winHeight) 指的是屏幕窗口的實際尺寸大小。全部這些值都是以象素爲單位,全爲整型數。
  注意:在實際應用中,視口的長寬比率老是等於視景體裁剪面的長寬比率。若是兩個比率不相等,那麼投影后的圖像顯示於視口內時會發生變形,如圖8-14所示。另外,屏幕窗口的改變通常不明顯影響視口的大小。所以,在調用這個函數時,最好實時檢測窗口尺寸,及時修正視口的大小,保證視口內的圖像能隨窗口的變化而變化,且不變形。

圖8-14 視景體到視口的映射


8.6 矩陣堆棧
  學過計算機的人也許都知道這個使用頻率極高的名詞 — 「堆棧」。顧名思義,堆棧指的是一個頂部打開底部封閉的柱狀物體,一般用來存放經常使用的東西。這些東西從頂部依次放入,但取出時也只能從頂部取出,即「先進後出,後進先出」。在計算機中,它常指在內存中開闢的一塊存放某些變量的連續區域。所以,OpenGL的矩陣堆棧指的就是內存中專門用來存放矩陣數據的某塊特殊區域。
  實際上,在建立、裝入、相乘模型變換和投影變換矩陣時,都已用到堆棧操做。通常說來,矩陣堆棧經常使用於構造具備繼承性的模型,即由一些簡單目標構成的複雜模型。例如,一輛自行車就是由兩個輪子、一個三角架及其它一些零部件構成的。它的繼承性表如今當自行車往前走時,首先是前輪旋轉,而後整個車身向前平移,接着是後輪旋轉,而後整個車身向前平移,如此進行下去,這樣自行車就往前走了。
  矩陣堆棧對複雜模型運動過程當中的多個變換操做之間的聯繫與獨立十分有利。由於全部矩陣操做函數如glLoadMatrix()、glMultMatrix()、 glLoadIdentity()等只處理當前矩陣或堆棧頂部矩陣,這樣堆棧中下面的其它矩陣就不受影響。堆棧操做函數有如下兩個:

  void glPushMatrix(void);   void glPopMatrix(void);
  第一個函數表示將全部矩陣依次壓入堆棧中,頂部矩陣是第二個矩陣的備份;壓入的矩陣數不能太多,不然出錯。第二個函數表示彈出堆棧頂部的矩陣,令原第二個矩陣成爲頂部矩陣,接受當前操做,故原頂部矩陣被破壞;當堆棧中僅存一個矩陣時,不能進行彈出操做,不然出錯。由此看出,矩陣堆棧操做與壓入矩陣的順序恰好相反,編程時要特別注意矩陣操做的順序。
  爲了更好地理解這兩個函數,咱們能夠形象地認爲glPushMatrix()就是「記住本身在哪」,glPopMatrix()就是「返回本身原來所在地」。請看下面一例:

  例8-7 堆棧操做例程arm.c

  #include "glos.h"   #include <GL/gl.h>   #include <GL/glu.h>   #include <GL/glaux.h>
  void myinit(void);   void drawPlane(void);   void CALLBACK elbowAdd (void);   void CALLBACK elbowSubtract (void);   void CALLBACK shoulderAdd (void);   void CALLBACK shoulderSubtract (void);   void CALLBACK display(void);   void CALLBACK myReshape(GLsizei w, GLsizei h);
  static int shoulder = 0, elbow = 0;
  void CALLBACK elbowAdd (void)   {     elbow = (elbow + 5) % 360;   }
  void CALLBACK elbowSubtract (void)   {     elbow = (elbow - 5) % 360;   }
  void CALLBACK shoulderAdd (void)   {     shoulder = (shoulder + 5) % 360;   }
  void CALLBACK shoulderSubtract (void)   {     shoulder = (shoulder - 5) % 360;   }
  void CALLBACK display(void)   {     glClear(GL_COLOR_BUFFER_BIT);     glColor3f(0.0, 1.0, 1.0);
    glPushMatrix();     glTranslatef (-0.5, 0.0, 0.0);     glRotatef ((GLfloat)     shoulder, 0.0, 0.0, 1.0);     glTranslatef (1.0, 0.0, 0.0);     auxWireBox(2.0, 0.2, 0.5);
    glTranslatef (1.0, 0.0, 0.0);     glRotatef ((GLfloat) elbow, 0.0, 0.0, 1.0);     glTranslatef (0.8, 0.0, 0.0);     auxWireBox(1.6, 0.2, 0.5);
    glPopMatrix();     glFlush();   }
  void myinit (void)   {     glShadeModel (GL_FLAT);   }
  void CALLBACK myReshape(GLsizei w, GLsizei h)   {     glViewport(0, 0, w, h);     glMatrixMode(GL_PROJECTION);     glLoadIdentity();     gluPerspective(65.0, (GLfloat) w/(GLfloat) h, 1.0, 20.0);     glMatrixMode(GL_MODELVIEW);     glLoadIdentity(); glTranslatef (0.0, 0.0, -5.0); /* viewing transform */   }
  void main(void)   {     auxInitDisplayMode (AUX_SINGLE | AUX_RGBA);     auxInitPosition (0, 0, 400, 400);     auxInitWindow ("Composite Modeling Transformations");
    myinit ();
    auxKeyFunc (AUX_LEFT, shoulderSubtract);     auxKeyFunc (AUX_RIGHT, shoulderAdd);     auxKeyFunc (AUX_UP, elbowAdd);     auxKeyFunc (AUX_DOWN, elbowSubtract);     auxReshapeFunc (myReshape);     auxMainLoop(display);   }
  從以上例程能夠看出,複雜的機械手臂是由兩個簡單的長方體依據必定的繼承關係構成的,而這個繼承關係是由矩陣堆棧的順序決定的。

圖8-15 簡單機械手臂的符合運動
相關文章
相關標籤/搜索