<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    posts - 189,comments - 115,trackbacks - 0

    OpenGL基礎圖形編程

    一、OpenGL與3D圖形世界

    1.1、OpenGL使人們進入三維圖形世界

    我們生活在一個充滿三維物體的三維世界中,為了使計算機能精確地再現(xiàn)這些物體,我們必須能在三維空間描繪這些物體。我們又生活在一個充滿信息的世界中,能否盡快地理解并運用這些信息將直接影響事業(yè)的成敗,所以我們需要用一種最直接的形式來表示這些信息。
    最近幾年計算機圖形學的發(fā)展使得三維表現(xiàn)技術得以形成,這些三維表現(xiàn)技術使我們能夠再現(xiàn)三維世界中的物體,能夠用三維形體來表示復雜的信息,這種技術就是可視化(Visualization)技術。可視化技術使人能夠在三維圖形世界中直接對具有形體的信息進行操作,和計算機直接交流。這種技術已經(jīng)把人和機器的力量以一種直覺而自然的方式加以統(tǒng)一,這種革命性的變化無疑將極大地提高人們的工作效率。可視化技術賦予人們一種仿真的、三維的并且具有實時交互的能力,這樣人們可以在三維圖形世界中用以前不可想象的手段來獲取信息或發(fā)揮自己創(chuàng)造性的思維。機械工程師可以從二維平面圖中得以解放直接進入三維世界,從而很快得到自己設計的三維機械零件模型。醫(yī)生可以從病人的三維掃描圖象分析病人的病灶。軍事指揮員可以面對用三維圖形技術生成的戰(zhàn)場地形,指揮具有真實感的三維飛機、軍艦、坦克向目標開進并分析戰(zhàn)斗方案的效果。
    更令人驚奇的是目前正在發(fā)展的虛擬現(xiàn)實技術,它能使人們進入一個三維的、多媒體的虛擬世界,人們可以游歷遠古時代的城堡,也可以遨游浩翰的太空。所有這些都依賴于計算機圖形學、計算機可視化技術的發(fā)展。人們對計算機可視化技術的研究已經(jīng)歷了一個很長的歷程,而且形成了許多可視化工具,其中SGI公司推出的GL三維圖形庫表現(xiàn)突出,易于使用而且功能強大。利用GL開發(fā)出來的三維應用軟件頗受許多專業(yè)技術人員的喜愛,這些三維應用軟件已涉及建筑、產(chǎn)品設計、醫(yī)學、地球科學、流體力學等領域。隨著計算機技術的繼續(xù)發(fā)展,GL已經(jīng)進一步發(fā)展成為OpenGL,OpenGL已被認為是高性能圖形和交互式視景處理的標準,目前包括ATT公司UNIX軟件實驗室、IBM公司、DEC公司、SUN公司、HP公司、Microsoft公司和 SGI公司在內(nèi)的幾家在計算機市場占領導地位的大公司都采用了OpenGL圖形標準。
    值得一提的是,由于Microsoft公司在 Windows NT中提供OpenGL圖形標準,OpenGL將在微機中廣泛應用,尤其是OpenGL三維圖形加速卡和微機圖形工作站的推出,人們可以在微機上實現(xiàn)三維圖形應用,如CAD設計、仿真模擬、三維游戲等,從而更有機會、更方便地使用OpenGL及其應用軟件來建立自己的三維圖形世界。

    1.2、OpenGL提供直觀的三維圖形開發(fā)環(huán)境
    OpenGL實際上是一種圖形與硬件的接口。它包括了120個圖形函數(shù),開發(fā)者可以用這些函數(shù)來建立三維模型和進行三維實時交互。與其他圖形程序設計接口不同,OpenGL提供了十分清晰明了的圖形函數(shù),因此初學的程序設計員也能利用OpenGL的圖形處理能力和1670萬種色彩的調(diào)色板很快地設計出三維圖形以及三維交互軟件。
    OpenGL強有力的圖形函數(shù)不要求開發(fā)者把三維物體模型的數(shù)據(jù)寫成固定的數(shù)據(jù)格式,這樣開發(fā)者不但可以直接使用自己的數(shù)據(jù),而且可以利用其他不同格式的數(shù)據(jù)源。這種靈活性極大地節(jié)省了開發(fā)者的時間,提高了軟件開發(fā)效益。
    長期以來,從事三維圖形開發(fā)的技術人員都不得不在自己的程序中編寫矩陣變換、外部設備訪問等函數(shù),這樣為調(diào)制這些與自己的軟件開發(fā)目標關系并不十分密切的函數(shù)費腦筋,而OpenGL正是提供一種直觀的編程環(huán)境,它提供的一系列函數(shù)大大地簡化了三維圖形程序。例如:

    • OpenGL提供一系列的三維圖形單元供開發(fā)者調(diào)用。
    • OpenGL提供一系列的圖形變換函數(shù)。
    • OpenGL提供一系列的外部設備訪問函數(shù),使開發(fā)者可以方便地訪問鼠標、鍵盤、空間球、數(shù)據(jù)手套等這種直觀的三維圖形開發(fā)環(huán)境體現(xiàn)了OpenGL的技術優(yōu)勢,這也是許多三維圖形開發(fā)者熱衷于OpenGL的緣由所在。
    1.3、OpenGL成為目前三維圖形開發(fā)標準
    OpenGL成為目前三維圖形開發(fā)標準在計算機發(fā)展初期,人們就開始從事計算機圖形的開發(fā)。直到計算機硬軟件和計算機圖形學高度發(fā)達的九十年代,人們發(fā)現(xiàn)復雜的數(shù)據(jù)以視覺的形式表現(xiàn)時是最易理解的,因而三維圖形得以迅猛發(fā)展,于是各種三維圖形工具軟件包相繼推出,如PHIGS、PEX、 RenderMan等。這些三維圖形工具軟件包有些側重于使用方便,有些側重于渲染效果或與應用軟件的連接,但沒有一種三維工具軟件包在交互式三維圖形建模能力、外部設備管理以及編程方便程度上能夠OpenGL相比擬。
    OpenGL經(jīng)過對GL的進一步發(fā)展,實現(xiàn)二維和三維的高級圖形技術,在性能上表現(xiàn)得異常優(yōu)越,它包括建模、變換、光線處理、色彩處理、動畫以及更先進的能力,如紋理影射、物體運動模糊等。OpenGL的這些能力為實現(xiàn)逼真的三維渲染效果、建立交互的三維景觀提供了優(yōu)秀的軟件工具。OpenGL在硬件、窗口、操作系統(tǒng)方面是相互獨立的。
    許多計算機公司已經(jīng)把 OpenGL集成到各種窗口和操作系統(tǒng)中,其中操作系統(tǒng)包括UNIX、Windows NT、DOS等,窗口系統(tǒng)有X窗口、Windows等。為了實現(xiàn)一個完整功能的圖形處理系統(tǒng),設計一個與OpenGL相關的系統(tǒng)結構為:其最底層是圖形硬件,第二層為操作系統(tǒng),第三層為窗口系統(tǒng),第四層為OpenGL,第五層為應用軟件。OpenGL是網(wǎng)絡透明的,在客戶 — 服務器(Client-Server)體系結構中,OpenGL允許本地和遠程繪圖。所以在網(wǎng)絡系統(tǒng)中,OpenGL在X窗口、Windows或其它窗口系統(tǒng)下都可以以一個獨立的圖形窗口出現(xiàn)。
    OpenGL作為一個性能優(yōu)越的圖形應用程序設計界面(API)而適合于廣泛的計算環(huán)境,從個人計算機到工作站和超級計算機,OpenGL都能實現(xiàn)高性能的三維圖形功能。由于許多在計算機界具有領導地位的計算機公司紛紛采用OpenGL作為三維圖形應用程序設計界面,OpenGL應用程序具有廣泛的移植性。因此,OpenGL已成為目前的三維圖形開發(fā)標準,是從事三維圖形開發(fā)工作的技術人員所必須掌握的開發(fā)工具。




    二、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.1、OpenGL基本理解
    OpenGL是一個與硬件圖形發(fā)生器的軟件接口,它包括了100多個圖形操作函數(shù),開發(fā)者可以利用這些函數(shù)來構造景物模型、進行三維圖形交互軟件的開發(fā)。正如上一章所述,OpenGL是一個高性能的圖形開發(fā)軟件包。OpenGL支持網(wǎng)絡,在網(wǎng)絡系統(tǒng)中用戶可以在不同的圖形終端上運行程序顯示圖形。 OpenGL作為一個與硬件獨立的圖形接口,它不提供與硬件密切相關的設備操作函數(shù),同時,它也不提供描述類似于飛機、汽車、分子形狀等復雜形體的圖形操作函數(shù)。用戶必須從點、線、面等最基本的圖形單元開始構造自己的三維模型。當然,象OpenInventor那樣更高一級的基于OpenGL的三維圖形建模開發(fā)軟件包將提供方便的工具。因此OpenGL的圖形操作函數(shù)十分基本、靈活。例如OpenGL中的模型繪制過程就多種多樣,內(nèi)容十分豐富,OpenGL提供了以下的對三維物體的繪制方式:
    • 網(wǎng)格線繪圖方式wireframe)
      這種方式僅繪制三維物體的網(wǎng)格輪廓線。
    • 深度優(yōu)先網(wǎng)格線繪圖方式depth_cued
      用網(wǎng)格線方式繪圖,增加模擬人眼看物體一樣,遠處的物體比近處的物體要暗些。
    • 反走樣網(wǎng)格線繪圖方式antialiased
      用網(wǎng)格線方式繪圖,繪圖時采用反走樣技術以減少圖形線條的參差不齊。
    • 平面消隱繪圖方式flat_shade
      對模型的隱藏面進行消隱,對模型的平面單元按光照程度進行著色但不進行光滑處理。
    • 光滑消隱繪圖方式smooth_shade
      對模型進行消隱按光照渲染著色的過程中再進行光滑處理,這種方式更接近于現(xiàn)實。
    • 加陰影和紋理的繪圖方式shadows、textures
      在模型表面貼上紋理甚至于加上光照陰影,使得三維景觀象照片一樣。
    • 運動模糊的繪圖方式motion-blured
      模擬物體運動時人眼觀察所感覺的動感現(xiàn)象。
    • 大氣環(huán)境效果atmosphere-effects
      在三維景觀中加入如霧等大氣環(huán)境效果,使人身臨其境。
    • 深度域效果depth-of-effects)
      類似于照相機鏡頭效果,模型在聚焦點處清晰,反之則模糊。
    這些三維物體繪圖和特殊效果處理方式,說明OpenGL已經(jīng)能夠模擬比較復雜的三維物體或自然景觀,這就是我們所面對的OpenGL。

    2.2、OpenGL工作流程
    整個OpenGL的基本工作流程如下圖:

    其中幾何頂點數(shù)據(jù)包括模型的頂點集、線集、多邊形集,這些數(shù)據(jù)經(jīng)過流程圖的上部,包括運算器、逐個頂點操作等;圖像數(shù)據(jù)包括象素集、影像集、位圖集等,圖像象素數(shù)據(jù)的處理方式與幾何頂點數(shù)據(jù)的處理方式是不同的,但它們都經(jīng)過光柵化、逐個片元(Fragment)處理直至把最后的光柵數(shù)據(jù)寫入幀緩沖器。在OpenGL中的所有數(shù)據(jù)包括幾何頂點數(shù)據(jù)和象素數(shù)據(jù)都可以被存儲在顯示列表中或者立即可以得到處理。OpenGL中,顯示列表技術是一項重要的技術。
    OpenGL要求把所有的幾何圖形單元都用頂點來描述,這樣運算器和逐個頂點計算操作都可以針對每個頂點進行計算和操作,然后進行光柵化形成圖形碎片;對于象素數(shù)據(jù),象素操作結果被存儲在紋理組裝用的內(nèi)存中,再象幾何頂點操作一樣光柵化形成圖形片元。
    整個流程操作的最后,圖形片元都要進行一系列的逐個片元操作,這樣最后的象素值BZ送入幀緩沖器實現(xiàn)圖形的顯示。

    2.3、OpenGL圖形操作步驟
    在上一節(jié)中說明了OpenGL的基本工作流程,根據(jù)這個流程可以歸納出在OpenGL中進行主要的圖形操作直至在計算機屏幕上渲染繪制出三維圖形景觀的基本步驟:
    1)根據(jù)基本圖形單元建立景物模型,并且對所建立的模型進行數(shù)學描述(OpenGL中把:點、線、多邊形、圖像和位圖都作為基本圖形單元)。
    2)把景物模型放在三維空間中的合適的位置,并且設置視點(viewpoint)以觀察所感興趣的景觀。
    3)計算模型中所有物體的色彩,其中的色彩根據(jù)應用要求來確定,同時確定光照條件、紋理粘貼方式等。
    4)把景物模型的數(shù)學描述及其色彩信息轉換至計算機屏幕上的象素,這個過程也就是光柵化(rasterization)。
    在這些步驟的執(zhí)行過程中,OpenGL可能執(zhí)行其他的一些操作,例如自動消隱處理等。另外,景物光柵化之后被送入幀緩沖器之前還可以根據(jù)需要對象素數(shù)據(jù)進行操作。



    三、WindowsNT下的OpenGL

    3.1、Windows NT下的OpenGL函數(shù)
    如前面的章節(jié)所述,Windows NT下的OpenGL同樣包含100多個庫函數(shù),這些函數(shù)都按一定的格式來命名,即每個函數(shù)都以gl開頭。Windows NT下的OpenGL除了具有基本的OpenGL函數(shù)外,還支持其他四類函數(shù):
    相應函數(shù) 具體說明
    OpenGL實用庫 43個函數(shù),每個函數(shù)以glu開頭。
    OpenGL輔助庫 31個函數(shù),每個函數(shù)以aux開頭。
    Windows專用庫函數(shù)(WGL 6個函數(shù),每個函數(shù)以wgl開頭。
    Win32 API函數(shù) 5個函數(shù),函數(shù)前面沒有專用前綴。

    在OpenGL中有115個核心函數(shù),這些函數(shù)是最基本的,它們可以在任何OpenGL的工作平臺上應用。這些函數(shù)用于建立各種各樣的形體,產(chǎn)生光照效果,進行反走樣以及進行紋理映射,進行投影變換等等。由于這些核心函數(shù)有許多種形式并能夠接受不同類型的參數(shù),實際上這些函數(shù)可以派生出300 多個函數(shù)。
    OpenGL的實用函數(shù)是比OpenGL核心函數(shù)更高一層的函數(shù),這些函數(shù)是通過調(diào)用核心函數(shù)來起作用的。這些函數(shù)提供了十分簡單的用法,從而減輕了開發(fā)者的編程負擔。OpenGL的實用函數(shù)包括紋理映射、坐標變換、多邊形分化、繪制一些如橢球、圓柱、茶壺等簡單多邊形實體(本指南將詳細講述這些函數(shù)的具體用法)等。這部分函數(shù)象核心函數(shù)一樣在任何OpenGL平臺都可以應用。
    OpenGL的輔助庫是一些特殊的函數(shù),這些函數(shù)本來是用于初學者做簡單的練習之用,因此這些函數(shù)不能在所有的OpenGL平臺上使用,在Windows NT環(huán)境下可以使用這些函數(shù)。這些函數(shù)使用簡單,它們可以用于窗口管理、輸入輸出處理以及繪制一些簡單的三維形體。為了使OpenGL的應用程序具有良好的移植性,在使用OpenGL輔助庫的時候應謹慎。
    6個WGL函數(shù)是用于連接OpenGL與Windows NT的,這些函數(shù)用于在Windows NT環(huán)境下的OpenGL窗口能夠進行渲染著色,在窗口內(nèi)繪制位圖字體以及把文本放在窗口的某一位置等。這些函數(shù)把Windows與OpenGL揉合在一起。最后的5個Win32函數(shù)用于處理象素存儲格式和雙緩沖區(qū),顯然這些函數(shù)僅僅能夠用于Win32系統(tǒng)而不能用于其它OpenGL平臺。

    3.2、OpenGL基本功能
    OpenGL能夠對整個三維模型進行渲染著色,從而繪制出與客觀世界十分類似的三維景象。另外OpenGL還可以進行三維交互、動作模擬等。具體的功能主要有以下這些內(nèi)容。
    • 模型繪制
      OpenGL能夠繪制點、線和多邊形。應用這些基本的形體,我們可以構造出幾乎所有的三維模型。OpenGL通常用模型的多邊形的頂點來描述三維模型。如何通過多邊形及其頂點來描述三維模型,在指南的在后續(xù)章節(jié)會有詳細的介紹。
    • 模型觀察
      在建立了三維景物模型后,就需要用OpenGL描述如何觀察所建立的三維模型。觀察三維模型是通過一系列的坐標變換進行的。模型的坐標變換在使觀察者能夠在視點位置觀察與視點相適應的三維模型景觀。在整個三維模型的觀察過程中,投影變換的類型決定觀察三維模型的觀察方式,不同的投影變換得到的三維模型的景象也是不同的。最后的視窗變換則對模型的景象進行裁剪縮放,即決定整個三維模型在屏幕上的圖象。

    • 顏色模式的指定
      OpenGL 應用了一些專門的函數(shù)來指定三維模型的顏色。程序員可以選擇二個顏色模式,即RGBA模式和顏色表模式。在RGBA模式中,顏色直接由RGB值來指定;在顏色表模式中,顏色值則由顏色表中的一個顏色索引值來指定。程序員還可以選擇平面著色和光滑著色二種著色方式對整個三維景觀進行著色。
    • 光照應用
      用OpenGL繪制的三維模型必須加上光照才能更加與客觀物體相似。OpenGL提供了管理四種光(輻射光、環(huán)境光、鏡面光和漫反射光)的方法,另外還可以指定模型表面的反射特性。
    • 圖象效果增強
      OpenGL提供了一系列的增強三維景觀的圖象效果的函數(shù),這些函數(shù)通過反走樣、混合和霧化來增強圖象的效果。反走樣用于改善圖象中線段圖形的鋸齒而更平滑,混合用于處理模型的半透明效果,霧使得影像從視點到遠處逐漸褪色,更接近于真實。
    • 位圖和圖象處理
      OpenGL還提供了專門對位圖和圖象進行操作的函數(shù)。
    • 紋理映射
      三維景物因缺少景物的具體細節(jié)而顯得不夠真實,為了更加逼真地表現(xiàn)三維景物,OpenGL提供了紋理映射的功能。OpenGL提供的一系列紋理映射函數(shù)使得開發(fā)者可以十分方便地把真實圖象貼到景物的多邊形上,從而可以在視窗內(nèi)繪制逼真的三維景觀。
    • 實時動畫
      為了獲得平滑的動畫效果,需要先在內(nèi)存中生成下一幅圖象,然后把已經(jīng)生成的圖象從內(nèi)存拷貝到屏幕上,這就是OpenGL的雙緩存技術(double buffer)。OpenGL提供了雙緩存技術的一系列函數(shù)。
    • 交互技術
      目前有許多圖形應用需要人機交互,OpenGL提供了方便的三維圖形人機交互接口,用戶可以選擇修改三維景觀中的物體。
    3.3、Windows NT下OpenGL的結構
    OpenGL的作用機制是客戶(client)/服務器(sever)機制,即客戶(用OpenGL繪制景物的應用程序)向服務器(即OpenGL內(nèi)核)發(fā)布OpenGL命令,服務器則解釋這些命令。大多數(shù)情況下,客戶和服務器在同一機器上運行。正是OpenGL的這種客戶/服務器機制,OpenGL可以十分方便地在網(wǎng)絡環(huán)境下使用。因此Windows NT下的OpenGL是網(wǎng)絡透明的。正象Windows的圖形設備接口(GDI)把圖形函數(shù)庫封裝在一個動態(tài)鏈接庫(Windows NT下的GDI32.DLL)內(nèi)一樣,OpenGL圖形庫也被封裝在一個動態(tài)鏈接庫內(nèi)(OPENGL32.DLL)。受客戶應用程序調(diào)用的OpenGL函數(shù)都先在OPENGL32.DLL中處理,然后傳給服務器WINSRV.DLL。OpenGL的命令再次得到處理并且直接傳給Win32的設備驅動接口(Device Drive Interface,DDI),這樣就把經(jīng)過處理的圖形命令送給視頻顯示驅動程序。下圖簡要說明這個過程:
     
    圖3-1 OpenGL在Windows NT下運行機制

      在三維圖形加速卡的GLINT圖形加速芯片的加速支持下,二個附加的驅動程序被加入這個過程中。一個OpenGL可安裝客戶驅動程序(Installable Client Driver,ICD)被加在客戶這一邊,一個硬件指定DDI(Hardware-specific DDI)被加在服務器這邊,這個驅動程序與Wind32 DDI是同一級別的。


    圖3-2 在三維圖形加速下OpenGL運行機制




    四、OpenGL基礎程序結構

    用OpenGL編寫的程序結構類似于用其他語言編寫的程序。實際上,OpenGL是一個豐富的三維圖形函數(shù)庫,編寫OpenGL程序并非難事,只需在基本C語言中調(diào)用這些函數(shù),用法同Turbo C、Microsoft C等類似,但也有許多不同之處。
    本指南所有的程序都是在Windows NT的Microsoft Visual C++集成環(huán)境下編譯連接的,其中有部分頭文件和函數(shù)是為這個環(huán)境所用的,例如判別操作系統(tǒng)的頭文件“glos.h”。此外,為便于各類讀者同時快速入門,在短時間內(nèi)掌握OpenGL編程的基本方法和技巧,指南中例子盡量采用標準ANSI C調(diào)用OpenGL函數(shù)來編寫,而且所有例程都只采用OpenGL附帶的輔助庫中的窗口系統(tǒng)。此外,這樣也便于程序在各平臺間移植,尤其往工作站UNIX 操作系統(tǒng)移植時,也只需改動頭文件等很少很少的部分。下面列出一個簡單的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);
    }

    這個程序運行結果是在屏幕窗口內(nèi)畫一個紅色的方塊。
    下面具體分析整個程序結構:首先,在程序最開始處是OpenGL頭文件:<GL/gl.h>、<GL/glaux.h>。前一個是gl庫的頭文件,后一個是輔助庫的頭文件。此外,在以后的幾章中還將說明OpenGL的另外兩個頭文件,一個是<GL/glu.h>實用庫的頭文件,另一個是<GL/glx.h>X窗口擴充庫的頭文件(這個常用在工作站上)。接下來是主函數(shù)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)定義窗口的初始位置,前兩個參數(shù)(0, 0)為窗口的左上角點的屏幕坐標,后兩個參數(shù)(500,500)為窗口的寬度和高度。auxInitWindow("simple")是窗口初始化,字符參數(shù)是窗口名稱。
    然后是窗口內(nèi)清屏:

    glClearColor(0.0,0.0,0.0,0.0); glClear(GL_COLOR_BUFFER_BIT);

    第一句將窗口清為黑色,第二句將顏色緩沖區(qū)清為glClearColor(0.0, 0.0, 0.0, 0.0)命令所設置的顏色,即同窗口背景顏色一致。
    再接著是在窗口內(nèi)畫一個物體:

    glColor3f(1.0,0.0,0.0);
    glRectf(-0.5,-0.5,0.5,0.5);

    很明顯,第一句設置物體顏色,函數(shù)中前三個參數(shù)分別為R、G、B值,最后一個參數(shù)是Alpha值,范圍都從0至1;第二句繪制一個二維矩形。注意:OpenGL是針對三維圖形而言,因此用作OpenGL編程繪制物體必須意識到任何一個物體都是三維的,具有空間性,而顯示于屏幕上的物體都是三維物體在二維平面上的投影。
    從表面上看,上述程序代碼很簡單,實際上已經(jīng)用到了缺省的投影形式(正射投影)。再看glFlush()函數(shù),表示強制繪圖完成。最后一句_sleep(1000),參數(shù)單位為毫秒,整句意思是保持現(xiàn)有狀況一秒鐘,然后結束程序運行。這個函數(shù)是VC++的庫函數(shù)。
    總而言之,OpenGL程序基本結構為定義窗口、清理窗口、繪制物體、結束運行。 



    五、OpenGL的數(shù)據(jù)類型和函數(shù)名

    OpenGL的數(shù)據(jù)類型定義可以與其它語言一致,但建議在ANSI C下最好使用以下定義的數(shù)據(jù)類型,例如GLint、GLfloat等。具體類型見表5-1。
    前綴    數(shù)據(jù)類型                    相應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 命令前綴和參數(shù)數(shù)據(jù)類型

      OpenGL的庫函數(shù)命名方式很有規(guī)律,了解這種規(guī)律后閱讀和編寫程序都比較容易方便。
    首先,每個庫函數(shù)有前綴gl、glu、glx或aux,表示此函數(shù)分屬于基本庫、實用庫、X窗口擴充庫或輔助庫,其后的函數(shù)名頭字母大寫,后綴是參數(shù)類型的簡寫,取i、f,參見表5-1。例:

    glVertex2i(2,4);
    glVertex3f(2.0,4.0,5.0);


      注意:有的函數(shù)參數(shù)類型后綴前帶有數(shù)字2、3、4。2代表二維,3代表三維,4代表alpha值(以后介紹)。
    有些OpenGL函數(shù)最后帶一個字母v,表示函數(shù)參數(shù)可用一個指針指向一個向量(或數(shù)組)來替代一系列單個參數(shù)值。下面兩種格式都表示設置當前顏色為紅色,二者等價。

      
    glColor3f(1.0,0.0,0.0);
    float color_array[]={1.0,0.0,0.0};
    glColor3fv(color_array);


      除了以上基本命名方式外,還有一種帶“*”星號的表示方法,例如glColor*(),它表示可以用函數(shù)的各種方式來設置當前顏色。同理,glVertex*v()表示用一個指針指向所有類型的向量來定義一系列頂點坐標值。
    最后,OpenGL也定義GLvoid類型,如果用C語言編寫,可以用它替代void類型。



    六、OpenGL輔組庫的基本使用

    OpenGL是一個開放的系統(tǒng),它是獨立于任何窗口系統(tǒng)或操作系統(tǒng)的。盡管它包含了許多圖形函數(shù),但它卻沒有窗口函數(shù),也沒有從鍵盤和鼠標讀取事件的函數(shù),所以要初學者寫出一個完整的圖形程序是相當困難的。另外,OpenGL圖形函數(shù)中只提供基本的幾何原形:點、線、多邊形,因此要創(chuàng)建基本的三維幾何體如球、錐體等,也很不容易。而OpenGL輔助庫就是為解決這些基本問題專門設計的,它提供了一些基本的窗口管理函數(shù)和三維圖形繪制函數(shù),能幫助初學者盡快進入OpenGL世界,掌握關鍵的三維圖形技術,體會其中奇妙的樂趣。但是,對于復雜的應用,這些函數(shù)遠遠不夠,只能作為參考。

    6.1、輔助庫函數(shù)分類
    這一節(jié)內(nèi)容可以作為手冊查閱,初學者不必深究。
    輔助庫函數(shù)大致分為六類:

    6.1.1 窗口初始化和退出
      相關函數(shù)有三個,它們在第一章已提到,這里將詳細介紹:

    void auxInitWindow(GLbyte *titleString)

    打開一個由auxInitDisplayMode()和auxInitPosition()指定的窗口。函數(shù)參數(shù)是窗口標題,窗口背景缺省顏色是RGBA下的黑色或顏色表(color_index)下的0號調(diào)色板的顏色。按下Escape鍵可以完成關掉窗口、結束程序、全部清屏三項功能。

    void auxInitDisplayMode(GLbitfield mask)

    設置窗口顯示模式。基本模式有RGBA或顏色表、單或雙緩存,也可指定其他附加模式:深度、模板或累積緩存(depth,stencil,and/or accumulation buffer)。參數(shù)mask是一組位標志的聯(lián)合(取或),AUX_RGBA或AUX_INDEX、AUX_SINGLE或AUX_DOUBLE,以及其它有效標志AUX_DEPTH、AUX_STENCIL或AUX_ACCUM。

    void auxInitPosition(GLint x,GLint y,GLsizei width,GLsizei height)

    設置窗口位置及大小。參數(shù)(x, y)為窗口的左上角點的屏幕坐標,參數(shù)(width, height)為窗口的寬度和高度,單位為象素,缺省值為(0, 0, 100, 100)。

    6.1.2 窗口處理和事件輸入
    當窗口創(chuàng)建后,且在進入主函數(shù)循環(huán)之前,應當?shù)怯浺韵铝谐龅幕卣{(diào)函數(shù)(callback function):

    void auxReshapeFunc(void(*function)(GLsizei,GLsizei))

    定義窗口改變時形狀重定函數(shù)。參數(shù)function是一個函數(shù)指針,這個函數(shù)帶有兩個參數(shù),即窗口改變后的新寬度和新高度。通常,function是 glViewport(),顯示裁減后的新尺寸,重定義投影矩陣,以便使投影后圖像的比例與視點匹配,避免比例失調(diào)。若不調(diào)用 auxReshapeFunc(),缺省重定物體形狀的函數(shù)功能是調(diào)用一個二維的正射投影矩陣。運用輔助庫,窗口將在每個事件改變后自動重新繪制。

    void auxKeyFunction(GLint key,void(*function)(void))

    定義鍵盤響應函數(shù)。參數(shù)function就是當按下key鍵時所調(diào)用的函數(shù)指針,輔助庫為參數(shù)key定義了幾個常量:AUX_0至AUX_9、 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 *))

    定義鼠標響應函數(shù)。參數(shù)function就是當鼠標以mode方式作用于button時所調(diào)用的函數(shù)。參數(shù)button有 AUX_LEFTBUTTON、AUX_MIDDLEBUTTON或AUX_RIGHTBUTTON(以右手為標準)。參數(shù)mode代表鼠標觸擊狀態(tài),擊中時為AUX_MOUSEDOWN,釋放時為AUX_MOUSEUP。參數(shù)function必須帶一個參數(shù),它是指向結構AUX_EVENNTREC的指針。當函數(shù)auxMouseFunc()被調(diào)用時將為這個結構分配相應的內(nèi)存。通常用法類似如下:

    void function(AUX_EVENTREC *event)
    {
    GLint x,y;
    x=event->data[AUX_MOUSEX];
    y=event->data[AUX_MOUSEY];
    ...
    }

    6.1.3 顏色表裝入
    因為OpenGL本身沒有窗口系統(tǒng),所以依賴于窗口系統(tǒng)的顏色映射就沒法裝入顏色查找表。如果采用顏色表模式,就要用到輔助庫提供的用RGB值定義的單個顏色索引函數(shù):

    void auxSetOneColor(GLint index,GLfloat red,GLfloat green,GLfloat blue)

      設置自定義顏色的索引。參數(shù)index即索引號,參數(shù)red、green、blue分別為紅、綠、藍值,范圍在(0~1)內(nèi)。

    6.1.4 三維物體繪制
    每組三維物體包括兩種形式:網(wǎng)狀體(wire)和實心體(solid)。網(wǎng)狀體沒有平面法向,而實心體有,能進行光影計算,有光照時采用實心體模型。下面這些函數(shù)的 參數(shù)都是定義物體大小的,可以改變。

    功能
    函數(shù)
    繪制球
    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)
    繪制環(huán)形圓紋面
    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)

      定義空閑狀態(tài)執(zhí)行函數(shù)。參數(shù)func是一個指針,指向所要執(zhí)行的函數(shù)功能。當它為零時,func執(zhí)行無效。

    6.1.6 程序運行

    void auxMainLoop(void(*displayFunc)(void))

      定義場景繪制循環(huán)函數(shù)。displayFunc指針指向場景繪制函數(shù)。當窗口需要更新或場景發(fā)生改變時,程序便調(diào)用它所指的函數(shù),重新繪制場景。

    6.2、輔助庫應用示例
    下面舉一個輔助庫的應用例子,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 網(wǎng)狀球體

    以上程序運行結果是在屏幕窗口內(nèi)繪制一個黃色的網(wǎng)狀球體,這個程序充分體現(xiàn)了輔助庫的基本應用方法。
    首先,在主函數(shù)中用輔助庫函數(shù)定義一個窗口auxInitWindow(),然后初始化顏色myinit(),這些在第一章中已說明。接下來是兩個十分重要的函數(shù) auxReshapeFunc()和auxMainLoop(),參數(shù)都是一個函數(shù)指針,指向的都是回調(diào)函數(shù)(回調(diào)函數(shù)定義用CALLBACK說明)。
    前者是窗口形狀重定函數(shù),參數(shù)指針指向函數(shù)myReshape(),它的兩個參數(shù)就是窗口的新寬度和新高度。然后用glViewport(0, 0, w, h)重定視口,并且在新視口內(nèi)重新定義投影矩陣,

    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()將矩陣清為單位矩陣,避免受其它矩陣操作的干擾;然后調(diào)用glOrtho()對物體進行正射投影,并且用判斷語句給出了兩種情況,使投影后圖像的比例與視點匹配,避免比例失調(diào)。
    再下來調(diào)用glMatrixMode()將矩陣操作改為對觀察物體有關的方式GL_MODELVIEW,同樣用 glLoadIdentity()清矩陣。后者是主函數(shù)循環(huán)函數(shù),參數(shù)指針指向函數(shù)display(),即繪制物體。當窗口需要更新或物體發(fā)生改變時,程序便調(diào)用它重新繪制。以上例子是輔助庫的最基本應用,復雜的應用將在后續(xù)的章節(jié)中詳細介紹。 



    七、OpenGL建模

      OpenGL基本庫提供了大量繪制各種類型圖元的方法,輔助庫也提供了不少描述復雜三維圖形的函數(shù)。這一章主要介紹基本圖元,如點、線、多邊形,有了這些圖元,就可以建立比較復雜的模型了。

    7.1、描述圖元
    OpenGL是三維圖形的函數(shù)庫,它所定義的點、線、多邊形等圖元與一般的定義不太一樣,存在一定的差別。對編程者來說,能否理解二者之間的差別十分重要。一種差別源于基于計算機計算的限制。OpenGL中所有浮點計算精度有限,故點、線、多邊形的坐標值存在一定的誤差。另一種差別源于位圖顯示的限制。以這種方式顯示圖形,最小的顯示圖元是一個象素,盡管每個象素寬度很小,但它們?nèi)匀槐葦?shù)學上所定義的點或線寬要大得多。當用OpenGL 進行計算時,雖然是用一系列浮點值定義點串,但每個點仍然是用單個象素顯示,只是近似擬合。
    OpenGL圖元是抽象的幾何概念,不是真實世界中的物體,因此須用相關的數(shù)學模型來描述。

    7.1.1 齊次坐標Homogeneous Coordinate
      在空間直角坐標系中,任意一點可用一個三維坐標矩陣[x y z]表示。如果將該點用一個四維坐標的矩陣[Hx Hy Hz H]表示時,則稱為齊次坐標表示方法。在齊次坐標中,最后一維坐標H稱為比例因子。
    在OpenGL中,二維坐標點全看作三維坐標點,所有的點都用齊次坐標來描述,統(tǒng)一作為三維齊次點來處理。每個齊次點用一個向量(x, y, z, w)表示,其中四個元素全不為零。齊次點具有下列幾個性質(zhì):
    1)如果實數(shù)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內(nèi)部計算時都作為三維點處理,用二維坐標(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),不是數(shù)學意義上的那種沿軸兩個方向無限延伸的線。這里的線由一系列頂點順次連結而成,有閉合和不閉合兩種。見圖7-1所示。

    圖7-1 線段的兩種連結方式

    7.1.4 多邊形(Polygon)
    OpenGL中定義的多邊形是由一系列線段依次連結而成的封閉區(qū)域。這些線段不能交叉,區(qū)域內(nèi)不能有空洞,多邊形必須在凸多邊形,否則不能被OpenGL函數(shù)接受。合法和非法多邊形圖示見圖7-2。

    圖7-2 合法和非法多邊形

    OpenGL多邊形可以是平面多邊形,即所有頂點在一個平面上,也可以是空間多邊形。更復雜的多邊形將在提高篇中介紹。

    7.2、繪制圖元

    7.2.1 定義頂點
    在OpenGL中,所有幾何物體最終都由有一定順序的頂點集來描述。
    函數(shù)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),第二個例子表示用雙精度浮點數(shù)定義一個頂點,第三個例子表示用齊次坐標定義一個頂點,其真實坐標為(1.2, 0.5, -1.1),最后一個例子表示用一個指針(或數(shù)組)定義頂點。

    7.2.2 構造幾何圖元
    在實際應用中,通常用一組相關的頂點序列以一定的方式組織起來定義某個幾何圖元,而不采用單獨定義多個頂點來構造幾何圖元。在OpenGL中,所有被定義的頂點必須放在glBegain()和glEnd()兩個函數(shù)之間才能正確表達一個幾何圖元或物體,否則,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()中的參數(shù)GL_POLYGON改為GL_POINTS,則圖形變?yōu)橐唤M頂點(5個),見圖7-3所示。
    圖7-3 繪制多邊形或一組頂點

    點函數(shù)glBegin(GLenum mode)標志描述一個幾何圖元的頂點列表的開始,其參數(shù)mode表示幾何圖元的描述類型。所有類型及說明見表7-1所示,相應的圖示見圖7-4。

    類型 說明
    GL_POINTS 單個頂點集
    GL_LINES 多組雙頂點線段
    GL_POLYGON 單個簡單填充凸多邊形
    GL_TRAINGLES 多組獨立填充三角形
    GL_QUADS 多組獨立填充四邊形
    GL_LINE_STRIP 不閉合折線
    GL_LINE_LOOP 閉合折線
    GL_TRAINGLE_STRIP 線型連續(xù)填充三角形串
    GL_TRAINGLE_FAN 扇形連續(xù)填充三角形串
    GL_QUAD_STRIP 連續(xù)填充四邊形串
    表7-1 幾何圖元類型和說明

    圖7-4 幾何圖元類型

    函數(shù)glEnd()標志頂點列表的結束。
    從圖7-4中可看出,可以采用許多方法構造幾何圖元,這些方法僅僅依賴于所給的頂點數(shù)據(jù)。
    在glBegin()和glEnd()之間最重要的信息就是由函數(shù)glVertex*()定義的頂點,必要時也可為每個頂點指定顏色、法向、紋理坐標或其他,即調(diào)用相關的函數(shù),見表7-2所示,具體用法以后會逐步介紹。

    函數(shù) 函數(shù)意義
    glVertex*() 設置頂點坐標
    glColor*() 設置當前顏色
    glIndex*() 設置當前顏色表
    glNormal*() 設置法向坐標
    glEvalCoord*() 產(chǎn)生坐標
    glCallList(),glCallLists() 執(zhí)行顯示列表
    glTexCoord*() 設置紋理坐標
    glEdgeFlag*() 控制邊界繪制
    glMaterial*() 設置材質(zhì)
    表7-2 在glBegin()和glEnd()之間可調(diào)用的函數(shù)

    看如下幾句:

    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();

    顏色等的設置只對當前點或后續(xù)點有效。上一例中第一個點是紅色,第二個點和第三個點都是藍色。其中設置綠色時,之后沒有頂點操作,而是設置藍色,故只有當前藍色對緊跟其后的兩個頂點有效。
    為了更好地理解構造幾何圖元函數(shù)的用法,下面舉一個簡單的例子:

    例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所示的內(nèi)容,這個例子很好地說明了幾何圖元的類型及顏色等函數(shù)的用法。希望讀者自己仔細分析每個物體的繪制方法,體會其中的關鍵之處,達到舉一反三的效果。當然,還可利用上一章輔助庫中提供的基本三維圖元構造比較復雜的物體,你不妨也試一試。



    八、OpenGL變換

      OpenGL變換是本篇的重點內(nèi)容,它包括計算機圖形學中最基本的三維變換,即幾何變換、投影變換、裁剪變換、視口變換,以及針對OpenGL的特殊變換概念理解和用法,如相機模擬、矩陣堆棧等。學好了這章,才開始真正走進三維世界。

    8.1、從三維空間到二維平面

    8.1.1 相機模擬
    在真實世界里,所有的物體都是三維的。但是,這些三維物體在計算機世界中卻必須以二維平面物體的形式表現(xiàn)出來。那么,這些物體是怎樣從三維變換到二維的呢?下面我們采用相機(Camera)模擬的方式來講述這個概念,如圖8-1所示。

    圖8-1 相機模擬

    實際上,從三維空間到二維平面,就如同用相機拍照一樣,通常都要經(jīng)歷以下幾個步驟 (括號內(nèi)表示的是相應的圖形學概念):
    第一步,將相機置于三角架上,讓它對準三維景物(視點變換,Viewing Transformation)。
    第二步,將三維物體放在適當?shù)奈恢茫P妥儞Q,Modeling Transformation)。
    第三步,選擇相機鏡頭并調(diào)焦,使三維物體投影在二維膠片上(投影變換,Projection Transformation)。
    第四步,決定二維像片的大小(視口變換,Viewport Transformation)。
    這樣,一個三維空間里的物體就可以用相應的二維平面物體表示了,也就能在二維的電腦屏幕上正確顯示了。

    8.1.2 三維圖形顯示流程
    運用相機模擬的方式比較通俗地講解了三維圖形顯示的基本過程,但在具體應用OpenGL函數(shù)庫編程時,還必須了解三維圖形世界中的幾個特殊坐標系的概念,以及用這些概念表達的三維圖形顯示流程。
    計算機本身只能處理數(shù)字,圖形在計算機內(nèi)也是以數(shù)字的形式進行加工和處理的。大家都知道,坐標建立了圖形和數(shù)字之間的聯(lián)系。為了使被顯示的物體數(shù)字化,要在被顯示的物體所在的空間中定義一個坐標系。這個坐標系的長度單位和坐標軸的方向要適合對被顯示物體的描述,這個坐標系稱為世界坐標系。
    計算機對數(shù)字化的顯示物體作了加工處理后,要在圖形顯示器上顯示,這就要在圖形顯示器屏幕上定義一個二維直角坐標系,這個坐標系稱為屏幕坐標系。這個坐標系坐標軸的方向通常取成平行于屏幕的邊緣,坐標原點取在左下角,長度單位常取成一個象素的長度,大小可以是整型數(shù)。
    為了使顯示的物體能以合適的位置、大小和方向顯示出來,必須要通過投影。投影的方法有兩種,即正射投影和透視投影。
    有時為了突出圖形的一部分,只把圖形的某一部分顯示出來,這時可以定義一個三維視景體(Viewing Volume)。正射投影時一般是一個長方體的視景體,透視投影時一般是一個棱臺似的視景體。只有視景體內(nèi)的物體能被投影在顯示平面上,其他部分則不能。在屏幕窗口內(nèi)可以定義一個矩形,稱為視口(Viewport),視景體投影后的圖形就在視口內(nèi)顯示。
    為了適應物理設備坐標和視口所在坐標的差別,還要作一適應物理坐標的變換。這個坐標系稱為物理設備坐標系。根據(jù)上面所述,三維圖形的顯示流程應如圖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);
    }

    以上程序運行結果就是繪制一個三維的正面透視立方體。其中已經(jīng)用到了相機模擬中提到的四種基本變換,即視點變換、模型變換、投影變換和視口變換。

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

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

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

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

    8.2、幾何變換
    實際上,上述所說的視點變換和模型變換本質(zhì)上都是一回事,即圖形學中的幾何變換。
    只是視點變換一般只有平移和旋轉,沒有比例變換。當視點進行平移或旋轉時,視點坐標系中的物體就相當于在世界坐標系中作反方向的平移或旋轉。因此,從某種意義上講,二者可以統(tǒng)一,只是各自出發(fā)點不一樣而已。讀者可以根據(jù)具體情況,選擇其中一個角度去考慮,這樣便于理解。

    8.2.1 兩個矩陣函數(shù)解釋
    這里先解釋兩個基本OpenGL矩陣操作函數(shù),便于以后章節(jié)的講述。函數(shù)解釋如下:

    void glLoadMatrix{fd}(const TYPE *m)

      設置當前矩陣中的元素值。函數(shù)參數(shù)*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() 指定的矩陣,也可以是其它矩陣變換函數(shù)的綜合結果。
    當幾何變換時,調(diào)用OpenGL的三個變換函數(shù)glTranslate*()、glRotate*()和glScale*(),實質(zhì)上相當于產(chǎn)生了一個近似的平移、旋轉和比例矩陣,然后調(diào)用glMultMatrix()與當前矩陣相乘。但是直接調(diào)用這三個函數(shù)程序運行得快一些,因OpenGL自動能計算矩陣。

    8.2.2 平移
    平移變換函數(shù)如下:

    void glTranslate{fd}(TYPE x,TYPE y,TYPE z)

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

    圖8-5 平移示意圖

    8.2.3 旋轉
    旋轉變換函數(shù)如下:

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

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

    圖8-6 旋轉示意圖

    8.2.3 縮放和反射
    縮放和反射變換函數(shù)如下:

    void glScale{fd}(TYPE x,TYPE y,TYPE z)

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

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

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

    例 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.3、投影變換
    投影變換是一種很關鍵的圖形變換,OpenGL中只提供了兩種投影方式,一種是正射投影,另一種是透視投影。不管是調(diào)用哪種投影函數(shù),為了避免不必要的變換,其前面必須加上以下兩句:

    glMAtrixMode(GL_PROJECTION);
    glLoadIdentity();

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

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

    圖8-9 正射投影視景體

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

    void glOrtho(GLdouble left,GLdouble right,GLdouble bottom,GLdouble top,
    GLdouble near,GLdouble far)

      它創(chuàng)建一個平行視景體。實際上這個函數(shù)的操作是創(chuàng)建一個正射投影矩陣,并且用這個矩陣乘以當前矩陣。其中近裁剪平面是一個矩形,矩形左下角點三維空間坐標是(left,bottom,-near),右上角點是(right,top,-near);遠裁剪平面也是一個矩形,左下角點空間坐標是(left,bottom,-far),右上角點是(right,top,-far)。所有的near和far值同時為正或同時為負。如果沒有其他變換,正射投影的方向平行于Z軸,且視點朝向Z負軸。
    這意味著物體在視點前面時far和near都為負值,物體在視點后面時far和near都為正值。另一個函數(shù)是:

    void gluOrtho2D(GLdouble left,GLdouble right,GLdouble bottom,GLdouble top)

    它是一個特殊的正射投影函數(shù),主要用于二維圖像到二維屏幕上的投影。它的near和far缺省值分別為-1.0和1.0,所有二維物體的Z坐標都為0.0。因此它的裁剪面是一個左下角點為(left,bottom)、右上角點為(right,top)的矩形。

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

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

    這個函數(shù)原型為:

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


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

    void gluPerspective(GLdouble fovy,GLdouble aspect,GLdouble zNear, GLdouble zFar);

    它也創(chuàng)建一個對稱透視視景體,但它的參數(shù)定義于前面的不同,如圖8-11所示。其操作是創(chuàng)建一個對稱的透視投影矩陣,并且用這個矩陣乘以當前矩陣。參數(shù) fovy定義視野在X-Z平面的角度,范圍是[0.0, 180.0];參數(shù)aspect是投影平面寬度與高度的比率;參數(shù)zNear和Far分別是遠近裁剪面沿Z負軸到視點的距離,它們總為正值。

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

    以上兩個函數(shù)缺省時,視點都在原點,視線沿Z軸指向負方向。二者的應用實例將在后續(xù)章節(jié)中介紹。

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

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

    附加平面裁剪函數(shù)為:

    void glClipPlane(GLenum plane,Const GLdouble *equation);

      函數(shù)定義一個附加的裁剪平面。其中參數(shù)equation指向一個擁有四個系數(shù)值的數(shù)組,這四個系數(shù)分別是裁剪平面Ax+By+Cz+D=0的A、B、 C、D值。因此,由這四個系數(shù)就能確定一個裁剪平面。參數(shù)plane是GL_CLIP_PLANEi(i=0,1,...),指定裁剪面號。
    在調(diào)用附加裁剪函數(shù)之前,必須先啟動glEnable(GL_CLIP_PLANEi),使得當前所定義的裁剪平面有效;當不再調(diào)用某個附加裁剪平面時,可用glDisable(GL_CLIP_PLANEi)關閉相應的附加裁剪功能。
    下面這個例子不僅說明了附加裁剪函數(shù)的用法,而且調(diào)用了gluPerspective()透視投影函數(shù),讀者可以細細體會其中的用法。例程如下:

    例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 剪取后的網(wǎng)狀半球體

    8.5、視口變換
    在前面幾節(jié)內(nèi)容中已相繼提到過視口變換,這一節(jié)將針對OpenGL來講述視口變換的原理及其相關函數(shù)的用法。運用相機模擬方式,我們很容易理解視口變換就是類似于照片的放大與縮小。在計算機圖形學中,它的定義是將經(jīng)過幾何變換、投影變換和裁剪變換后的物體顯示于屏幕窗口內(nèi)指定的區(qū)域內(nèi),這個區(qū)域通常為矩形,稱為視口。OpenGL中相關函數(shù)是:

    glViewport(GLint x,GLint y,GLsizei width, GLsizei height);

      這個函數(shù)定義一個視口。函數(shù)參數(shù)(x, y)是視口在屏幕窗口坐標系中的左下角點坐標,參數(shù)width和height分別是視口的寬度和高度。缺省時,參數(shù)值即(0, 0, winWidth, winHeight) 指的是屏幕窗口的實際尺寸大小。所有這些值都是以象素為單位,全為整型數(shù)。
    注意:在實際應用中,視口的長寬比率總是等于視景體裁剪面的長寬比率。如果兩個比率不相等,那么投影后的圖像顯示于視口內(nèi)時會發(fā)生變形,如圖8-14所示。另外,屏幕窗口的改變一般不明顯影響視口的大小。因此,在調(diào)用這個函數(shù)時,最好實時檢測窗口尺寸,及時修正視口的大小,保證視口內(nèi)的圖像能隨窗口的變化而變化,且不變形。

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

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

    void glPushMatrix(void);
    void glPopMatrix(void);

    第一個函數(shù)表示將所有矩陣依次壓入堆棧中,頂部矩陣是第二個矩陣的備份;壓入的矩陣數(shù)不能太多,否則出錯。第二個函數(shù)表示彈出堆棧頂部的矩陣,令原第二個矩陣成為頂部矩陣,接受當前操作,故原頂部矩陣被破壞;當堆棧中僅存一個矩陣時,不能進行彈出操作,否則出錯。由此看出,矩陣堆棧操作與壓入矩陣的順序剛好相反,編程時要特別注意矩陣操作的順序。
    為了更好地理解這兩個函數(shù),我們可以形象地認為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);
    }

    從以上例程可以看出,復雜的機械手臂是由兩個簡單的長方體依據(jù)一定的繼承關系構成的,而這個繼承關系是由矩陣堆棧的順序決定的。

    圖8-15 簡單機械手臂的符合運動



    九、OpenGL顏色

      幾乎所有OpenGL應用目的都是在屏幕窗口內(nèi)繪制彩色圖形,所以顏色在OpenGL編程中占有很重要的地位。這里的顏色與繪畫中的顏色概念不一樣,它屬于RGB顏色空間,只在監(jiān)視器屏幕上顯示。另外,屏幕窗口坐標是以象素為單位,因此組成圖形的每個象素都有自己的顏色,而這種顏色值是通過對一系列OpenGL函數(shù)命令的處理最終計算出來的。本章將講述計算機顏色的概念以及OpenGL的顏色模式、顏色定義和兩種模式應用場合等內(nèi)容,若掌握好顏色的應用,你就能走進繽紛絢麗的色彩世界,從中享受無窮的樂趣。

    9.1、計算機顏色

    9.1.1 顏色生成原理
    計算機顏色不同于繪畫或印刷中的顏色,顯示于計算機屏幕上每一個點的顏色都是由監(jiān)視器內(nèi)部的電子槍激發(fā)的三束不同顏色的光(紅、綠、藍)混合而成,因此,計算機顏色通 常用R(Red)、G(Green)、B(Blue)三個值來表示,這三個值又稱為顏色分量。顏色生成原理 示意圖見圖9-1所示。

    圖9-1 計算機顏色生成原理

    9.1.2 RGB色立體RGB Color Cube
    所有監(jiān)視器屏幕的顏色都屬于RGB顏色空間,如果用一個立方體形象地表示RGB顏色組成關系,那么就稱這個立方體為RGB色立體,如圖9-2所示。

    圖9-2 RGB色立體

    在圖中,R、G、B三值的范圍都是從0.0到1.0。如果某顏色分量越大,則表示對應的顏色分量越亮,也就是它在此點所貢獻的顏色成分越多;反之,則越暗或越少。當R、G、B三個值都為0.0時,此點顏色為黑色(Black);當三者都為1.0時,此點顏色為白色(White);當三個顏色分量值相等時,表示三者貢獻一樣,因此呈現(xiàn)灰色(Grey),在圖中表現(xiàn)為從黑色頂點到白色頂點的那條對角線;當R=1.0、G=1.0、B=0.0時,此點顏色為黃色(Yellow);同理,R=1.0、G=0.0、B=1.0時為洋紅色,也叫品色(Magenta);R=0.0、G=1.0、B=1.0時為青色(Cyan)。

    9.2、顏色模式
    OpenGL顏色模式一共有兩個:RGB(RGBA)模式和顏色表模式。在RGB模式下,所有的顏色定義全用R、G、B三個值來表示,有時也加上 Alpha值(與透明度有關),即RGBA模式。在顏色表模式下,每一個象素的顏色是用顏色表中的某個顏色索引值表示,而這個索引值指向了相應的R、G、 B值。這樣的一個表成為顏色映射(Color Map)。

    9.2.1 RGBA模式RGBA Mode
    在RGBA模式下,可以用glColor*()來定義當前顏色。其函數(shù)形式為:

    void glColor3{b s i f d ub us ui}(TYPE r,TYPE g,TYPE b);
    void glColor4{b s i f d ub us ui}(TYPE r,TYPE g,TYPE b,TYPE a);
    void glColor3{b s i f d ub us ui}v(TYPE *v);
    void glColor4{b s i f d ub us ui}v(TYPE *v);


    設置當前R、G、B和A值。這個函數(shù)有3和4兩種方式,在前一種方式下,a值缺省為1.0,后一種Alpha值由用戶自己設定,范圍從0.0到1.0。同樣,它也可用指針傳遞參數(shù)。另外,函數(shù)的第二個后綴的不同使用,其相應的參數(shù)值及范圍不同,見下表9-1所示。雖然這些參數(shù)值不同,但實際上 OpenGL已自動將它們映射在0.0到1.0或-1.0或范圍之內(nèi)。因此,靈活使用這些后綴,會給你編程帶來很大的方便。

    后綴 數(shù)據(jù)類型 最小值 最小值映射 最大值 最大值映射
    b 1字節(jié)整型數(shù) -128 -1.0 127 1.0
    s 2字節(jié)整型數(shù) -32,768 -1.0 32,767 1.0
    i 4字節(jié)整型數(shù) -2,147,483,648 -1.0 2,147,483,647 1.0
    ub 1字節(jié)無符號整型數(shù) 0 0.0 255 1.0
    us 2字節(jié)無符號整型數(shù) 0 0.0 65,535 1.0
    ui 4字節(jié)無符號整型數(shù) 0 0.0 4,294,967,295 1.0
    表9-1 整型顏色值到浮點數(shù)的轉換

    9.2.2 顏色表模式Color_Index Mode
    在顏色表方式下,可以調(diào)用glIndex*()函數(shù)從顏色表中選取當前顏色。其函數(shù)形式為:

    void glIndex{sifd}(TYPE c);
    void glIndex{sifd}v(TYPE *c);


    設置當前顏色索引值,即調(diào)色板號。若值大于顏色位面數(shù)時則取模。

    9.2.3 兩種模式應用場合
    在大多情況下,采用RGBA模式比顏色表模式的要多,尤其許多效果處理,如陰影、光照、霧、反走樣、混合等,采用RGBA模式效果會更好些;另外,紋理映射只能在RGBA模式下進行。下面提供幾種運用顏色表模式的情況(僅供參考):
    1)若原來應用程序采用的是顏色表模式則轉到OpenGL上來時最好仍保持這種模式,便于移植。
    2)若所用顏色不在缺省提供的顏色許可范圍之內(nèi),則采用顏色表模式。
    3)在其它許多特殊處理,如顏色動畫,采用這種模式會出現(xiàn)奇異的效果。

    9.3、顏色應用舉例
    顏色是一個極具吸引力的應用,在前面幾章中已經(jīng)逐步介紹了RGBA模式的應用方式,這里就不再多述。下面著重說一下顏色表模式的應用方法,請看例程:

    例9-1 顏色表應用例程cindex.c

    #include "glos.h"
    #include <GL/gl.h>
    #include <GL/glaux.h>

    void myinit(void);
    void InitPalette(void);
    void DrawColorFans(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 InitPalette(void)
    {
    GLint j;
    static GLfloat rgb[][3]={
    {1.0,0.0,0.0},{1.0,0.0,0.5},{1.0,0.0,1.0},{0.0,0.0,1.0},
    {0.0,1.0,1.0},{0.0,1.0,0.0},{1.0,1.0,0.0},{1.0,0.5,0.0}};

    for(j=0;j<8;j++)
    auxSetOneColor(j+1,rgb[j][0],rgb[j][1],rgb[j][2]);
    }

    void CALLBACK myReshape(GLsizei w,GLsizei h)
    {
    glViewport(0,0,w,h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    if(w<=h)
    glOrtho(-12.0,12.0,-12.0*(GLfloat)h/(GLfloat)w, 12.0*(GLfloat)h/(GLfloat)w,-30.0,30.0);
    else
    glOrtho(-12.0*(GLfloat)h/(GLfloat)w, 12.0*(GLfloat)h/(GLfloat)w,-12.0,12.0,-30.0,30.0); 
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    }

    void CALLBACK display(void)
    {
    InitPalette();
    DrawColorFans();
    glFlush();
    }

    void DrawColorFans(void)
    {
    GLint n;
    GLfloat pp[8][2]={
    {7.0,-7.0},{0.0,-10.0},{-7.0,-7.0},{-10.0,0.0},
    {-7.0,7.0}, {0.0,10.0},{7.0,7.0},{10.0,0.0}};


    /* draw some filled_fan_triangles */
        glBegin(GL_TRIANGLE_FAN);
    glVertex2f(0.0,0.0);
    glVertex2f(10.0,0.0);
    for(n=0;n<8;n++)
    {
    glIndexi(n+1);
    glVertex2fv(pp[n]);
    }
    glEnd();
    }

    void main(void)
    {
    auxInitDisplayMode(AUX_SINGLE|AUX_INDEX);
    auxInitPosition(0,0,500,500);
    auxInitWindow("Color Index");
    myinit();
    auxReshapeFunc(myReshape);
    auxMainLoop(display);
    }

    這個程序運行結果是在屏幕上顯示八個連成扇形的不同顏色的三角形,每個三角形的顏色定義采用顏色表模式。其中,調(diào)用了輔助庫函數(shù)auxSetOneColor()來裝載顏色映射表,即調(diào)色板。因為將某個顏色裝載到顏色查找表(color lookup table)中的過程必須依賴窗口系統(tǒng),而OpenGL函數(shù)與窗口系統(tǒng)無關,所以這里就調(diào)用輔助庫的函數(shù)來完成這個過程,然后才調(diào)用OpenGL自己的函數(shù)glIndex()設置當前的顏色號。

    圖9-3 自定義調(diào)色板



    十、OpenGL光照


    10.1、真實感圖形基本概念
      真實感圖形繪制是計算機圖形學的一個重要組成部分,它綜合利用數(shù)學、物理學、計算機科學和其它科學知識在計算機圖形設備上生成象彩色照片那樣的具有真實感的圖形。一般說來,用計算機在圖形設備上生成真實感圖形必須完成以下四個步驟:一是用建模,即用一定的數(shù)學方法建立所需三維場景的幾何描述,場景的幾何描述直接影響圖形的復雜性和圖形繪制的計算耗費;二是將三維幾何模型經(jīng)過一定變換轉為二維平面透視投影圖;三是確定場景中所有可見面,運用隱藏面消隱算法將視域外或被遮擋住的不可見面消去;四是計算場景中可見面的顏色,即根據(jù)基于光學物理的光照模型計算可見面投射到觀察者眼中的光亮度大小和顏色分量,并將它轉換成適合圖形設備的顏色值,從而確定投影畫面上每一象素的顏色,最終生成圖形。
    由于真實感圖形是通過景物表面的顏色和明暗色調(diào)來表現(xiàn)景物的幾何形狀、空間位置以及表面材料的,而一個物體表面所呈現(xiàn)的顏色是由表面向視線方向輻射的光能決定的。在計算機圖形學中,常采用一個既能表示光能大小又能表示其顏色組成的物理量即光亮度(luminance)或光強(intensity of light)來描述物體表面朝某方向輻射光能的顏色。采用這個物理量可以正確描述光在物體表面的反射、透射和吸收現(xiàn)象,因而可以正確計算處物體表面在空間給定方向上的光能顏色。
    物體表面向空間給定方向輻射的光強可應用光照模型進行計算。簡單的光照模型通常假定物體表面是光滑的且由理想材料構成,因此只考慮光源照射在物體表面產(chǎn)生的反射光,所生成的圖形可以模擬處不透明物體表面的明暗過渡,具有一定的真實感效果。復雜的光照模型除了考慮上述因素外,還要考慮周圍環(huán)境的光對物體表面的影響。如光亮平滑的物體表面會將環(huán)境中其它物體映像在表面上,而通過透明物體也可看到其后的環(huán)境景象。這類光照模型稱為整體光照模型,它能模擬出鏡面映像、透明等較精致的光照效果。為了更真實的繪制圖形,還要考慮物體表面的細節(jié)紋理,這通常使用一種稱為“紋理映射”(texture mapping)的技術把已有的平面花紋圖案映射到物體表面上,并在應用光照模型時將這些花紋的顏色考慮進去,物體表面細節(jié)的模擬使繪制的圖形更接近自然景物。
    以上內(nèi)容中,真實感圖形繪制的四大步驟前兩步在前面的章節(jié)已經(jīng)詳細介紹過,這里不再重復,第三步OpenGL將自動完成所有消隱過程,第四步下面幾節(jié)詳述。另外,部分復雜光照模型應用將在后續(xù)章節(jié)里介紹。

    10.2、光照模型

    10.2.1 簡單光照模型
    當光照射到一個物體表面上時,會出現(xiàn)三種情形。首先,光可以通過物體表面向空間反射,產(chǎn)生反射光。其次,對于透明體,光可以穿透該物體并從另一端射出,產(chǎn)生透射光。最后,部分光將被物體表面吸收而轉換成熱。在上述三部分光中,僅僅是透射光和反射光能夠進入人眼產(chǎn)生視覺效果。這里介紹的簡單光照模型只考慮被照明物體表面的反射光影響,假定物體表面光滑不透明且由理想材料構成,環(huán)境假設為由白光照明。
    一般來說,反射光可以分成三個分量,即環(huán)境反射、漫反射和鏡面反射。環(huán)境反射分量假定入射光均勻地從周圍環(huán)境入射至景物表面并等量地向各個方向反射出去,通常物體表面還會受到從周圍環(huán)境來的反射光(如來自地面、天空、墻壁等的反射光)的照射,這些光常統(tǒng)稱為環(huán)境光(Ambient Light);漫反射分量表示特定光源在景物表面的反射光中那些向空間各方向均勻反射出去的光,這些光常稱為漫射光(Diffuse Light);鏡面反射光為朝一定方向的反射光,如一個點光源照射一個金屬球時會在球面上形成一塊特別亮的區(qū)域,呈現(xiàn)所謂“高光(Highlight)”,它是光源在金屬球面上產(chǎn)生的鏡面反射光(Specular Light)。對于較光滑物體,其鏡面反射光的高光區(qū)域小而亮;相反,粗糙表面的鏡面反射光呈發(fā)散狀態(tài),其高光區(qū)域大而不亮。下面先看一個簡單的光照例程。

    例10-1 簡單光照例程light0.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 myinit(void)
    {
    GLfloat light_position[] = { 1.0, 1.0, 1.0, 0.0 };

    glLightfv(GL_LIGHT0, GL_POSITION, light_position);

    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);
    glDepthFunc(GL_LESS);
    glEnable(GL_DEPTH_TEST);
    }

    void CALLBACK display(void)
    {
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    auxSolidSphere(1.0);
    glFlush();
    }

    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)w/(GLfloat)h, 1.5*(GLfloat)w/(GLfloat)h, -1.5, 1.5, -10.0, 10.0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    }

    void main(void)
    {
    auxInitDisplayMode (AUX_SINGLE | AUX_RGBA);
    auxInitPosition (0, 0, 500, 500);
    auxInitWindow ("Simple Lighting");
    myinit();
    auxReshapeFunc (myReshape);
    auxMainLoop(display);
    }


    以上程序運行結果是顯示一個具有灰色光影的球。其中函數(shù)myinit()中包含了關鍵的設定光源位置、啟動光照等幾句,而其它程序語言幾乎與以前的沒有多大區(qū)別,但效果卻完全不一樣。下面幾個小節(jié)將詳細介紹有關函數(shù)的用法。

    圖10-1 帶光影的灰色球體

    10.2.2 OpenGL光組成
    在OpenGL簡單光照模型中的幾種光分為:輻射光(Emitted Light)、環(huán)境光(Ambient Light)、漫射光(Diffuse Light)、鏡面光(Specular Light)。
    輻射光是最簡單的一種光,它直接從物體發(fā)出并且不受任何光源影響。
    環(huán)境光是由光源發(fā)出經(jīng)環(huán)境多次散射而無法確定其方向的光,即似乎來自所有方向。一般說來,房間里的環(huán)境光成分要多些,戶外的相反要少得多,因為大部分光按相同方向照射,而且在戶外很少有其他物體反射的光。當環(huán)境光照到曲面上時,它在各個方向上均等地發(fā)散(類似于無影燈光)。
    漫射光來自一個方向,它垂直于物體時比傾斜時更明亮。一旦它照射到物體上,則在各個方向上均勻地發(fā)散出去。于是,無論視點在哪里它都一樣亮。來自特定位置和特定方向的任何光,都可能有散射成分。
    鏡面光來自特定方向并沿另一方向反射出去,一個平行激光束在高質(zhì)量的鏡面上產(chǎn)生100%的鏡面反射。光亮的金屬和塑料具有很高非反射成分,而象粉筆和地毯等幾乎沒有反射成分。因此,三某種意義上講,物體的反射程度等同于其上的光強(或光亮度)。

    10.2.3 創(chuàng)建光源Light Source
    光源有許多特性,如顏色、位置、方向等。選擇不同的特性值,則對應的光源作用在物體上的效果也不一樣,這在以后的章節(jié)中會逐步介紹的。下面詳細講述定義光源特性的函數(shù)glLight*():

    void glLight{if}[v](GLenum light , GLenum pname, TYPE param)

    創(chuàng)建具有某種特性的光源。其中第一個參數(shù)light指定所創(chuàng)建的光源號,如GL_LIGHT0、GL_LIGHT1、...、GL_LIGHT7。第二個參數(shù)pname指定光源特性,這個參數(shù)的輔助信息見表10-1所示。最后一個參數(shù)設置相應的光源特性值。

    pname 參數(shù)名 缺省值 說明
    GL_AMBIENT (0.0, 0.0, 0.0, 1.0) RGBA模式下環(huán)境光
    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 點光源聚光指數(shù)
    GL_SPOT_CUTOFF 180.0 點光源聚光截止角
    GL_CONSTANT_ATTENUATION 1.0 常數(shù)衰減因子
    GL_LINER_ATTENUATION 0.0 線性衰減因子
    GL_QUADRATIC_ATTENUATION 0.0 平方衰減因子
    表10-1 函數(shù)glLight*()參數(shù)pname說明

     注意:以上列出的GL_DIFFUSE和GL_SPECULAR的缺省值只能用于GL_LIGHT0,其他幾個光源的GL_DIFFUSE和GL_SPECULAR缺省值為(0.0,0.0,0.0,1.0)。另外,表中后六個參數(shù)的應用放在下一篇中介紹。在上面例程中,光源的創(chuàng)建為:

    GLfloat light_position[] = { 1.0, 1.0, 1.0, 0.0 };
    glLightfv(GL_LIGHT0, GL_POSITION, light_position);


    其中l(wèi)ight_position是一個指針,指向定義的光源位置齊次坐標數(shù)組。其它幾個光源特性都為缺省值。同樣,我們也可用類似的方式定義光源的其他幾個特性值,例如:

    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);


    10.2.4 啟動光照
    在OpenGL中,必須明確指出光照是否有效或無效。如果光照無效,則只是簡單地將當前顏色映射到當前頂點上去,不進行法向、光源、材質(zhì)等復雜計算,那么顯示的圖形就沒有真實感,如前幾章例程運行結果顯示。要使光照有效,首先得啟動光照,即:

    glEnable(GL_LIGHTING);

    若使光照無效,則調(diào)用gDisable(GL_LIGHTING)可關閉當前光照。然后,必須使所定義的每個光源有效,例light0.c中只用了一個光源,即:

    glEnable(GL_LIGHT0);

    其它光源類似,只是光源號不同而已。

    10.3、明暗處理
    在計算機圖形學中,光滑的曲面表面常用多邊形予以逼近和表示,而每個小多邊形輪廓(或內(nèi)部)就用單一的顏色或許多不同的顏色來勾畫(或填充),這種處理方式就稱為明暗處理。在OpenGL中,用單一顏色處理的稱為平面明暗處理(Flat Shading),用許多不同顏色處理的稱為光滑明暗處理(Smooth Shading),也稱為Gourand明暗處理(Gourand Shading)。設置明暗處理模式的函數(shù)為:

    void glShadeModel(GLenum mode);

    函數(shù)參數(shù)為GL_FLAT或GL_SMOOTH,分別表示平面明暗處理和光滑明暗處理。
    應用平面明暗處理模式時,多邊形內(nèi)每個點的法向一致,且顏色也一致;應用光滑明暗處理模式時,多邊形所有點的法向是由內(nèi)插生成的,具有一定的連續(xù)性,因此每個點的顏色也相應內(nèi)插,故呈現(xiàn)不同色。這種模式下,插值方法采用的是雙線性插值法,如圖10-2所示。

    圖10-2 Gouraud明暗處理

    Gouraud明暗處理通常算法為:先用多邊形頂點的光強線性插值出當前掃描線與多邊形邊交點處的光強,然后再用交點的光強線插值處掃描線位于多邊形內(nèi)區(qū)段上每一象素處的光強值。圖中顯示出一條掃描線與多邊形相交,交線的端點是A點和B點,P點是掃描線上位于多邊形內(nèi)的任一點,多邊形三個頂點的光強分別為I1、I2和I3.取A點的光強Ia為I1和I2的線性插值,B點的光強Ib為I1和I3的線性插值,P點的光強Ip則為Ia和Ib的線性插值。采用Gouraud明暗處理不但可以使用多邊形表示的曲面光強連續(xù),而且計算量很小。這種算法還可以以增量的形式改進,且能用硬件直接實現(xiàn)算法,從而廣泛用于計算機實時圖形生成。請看下面光滑明暗處理的例程:

    例10-2 明暗處理例程Shading.c

    #include "glos.h"
    #include <GL/gl.h>
    #include <GL/glu.h>
    #include <GL/glaux.h>

    void myinit(void);
    void object(void);
    void CALLBACK display(void);
    void CALLBACK myReshape(GLsizei w, GLsizei h);

    /* GL_SMOOTH is actually the default shading model. */
    void myinit (void)
    {
    glShadeModel (GL_SMOOTH);
    }

    void object(void)
    {
    glBegin (GL_POLYGON);
    glColor3f (1.0, 0.0, 0.0);
    glVertex2f (4.0, 4.0);
    glColor3f(1.0,1.0,1.0);
    glVertex2f (12.0, 4.0);
    glColor3f(0.0,0.0,1.0);
    glVertex2f (12.0, 12.0);
    glColor3f(0.0,1.0,0.0);
    glVertex2f (4.0, 12.0);
    glEnd ();
    }

    void CALLBACK display(void)
    {
    glClear (GL_COLOR_BUFFER_BIT);
    object ();
    glFlush ();
    }

    void CALLBACK myReshape(GLsizei w, GLsizei h)
    {
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    if (w <= h)
    gluOrtho2D (0.0, 16.0, 0.0, 16.0 * (GLfloat) h/(GLfloat) w);
    else
    gluOrtho2D (0.0, 16.0 * (GLfloat) w/(GLfloat) h, 0.0, 16.0);
    glMatrixMode(GL_MODELVIEW);
    }

    void main(void)
    {
    auxInitDisplayMode (AUX_SINGLE | AUX_RGBA);
    auxInitPosition (0, 0, 500, 500);
    auxInitWindow ("Smooth Shading");
    myinit();
    auxReshapeFunc (myReshape);
    auxMainLoop(display);
    }


    以上程序運行結果是在屏幕上顯示一個色彩連續(xù)變化的三角形。這個程序是用的RGBA顯示模式,若改用顏色表模式,則顏色內(nèi)插實際上是顏色表的內(nèi)插,因此呈現(xiàn)的顏色可能不連續(xù)。網(wǎng)友不妨自己試試。
    另外,若在light0.c程序中加上一句定義GL_FLAT明暗處理模式,則又會出現(xiàn)怎樣的情形呢?讀者可以仔細比較一下。

    圖10-3 高氏明暗處理的正方形

    10.4、材質(zhì)

    10.4.1 材質(zhì)顏色
    OpenGL用材料對光的紅、綠、藍三原色的反射率來近似定義材料的顏色。象光源一樣,材料顏色也分成環(huán)境、漫反射和鏡面反射成分,它們決定了材料對環(huán)境光、漫反射光和鏡面反射光的反射程度。在進行光照計算時,材料對環(huán)境光的反射率與每個進入光源的環(huán)境光結合,對漫反射光的反射率與每個進入光源的漫反射光結合,對鏡面光的反射率與每個進入光源的鏡面反射光結合。對環(huán)境光與漫反射光的反射程度決定了材料的顏色,并且它們很相似。對鏡面反射光的反射率通常是白色或灰色(即對鏡面反射光中紅、綠、藍的反射率相同)。鏡面反射高光最亮的地方將變成具有光源鏡面光強度的顏色。例如一個光亮的紅色塑料球,球的大部分表現(xiàn)為紅色,光亮的高光將是白色的。

    10.4.2 材質(zhì)定義
    材質(zhì)的定義與光源的定義類似。其函數(shù)為:

    void glMaterial{if}[v](GLenum face,GLenum pname,TYPE param);

    定義光照計算中用到的當前材質(zhì)。face可以是GL_FRONT、GL_BACK、GL_FRONT_AND_BACK,它表明當前材質(zhì)應該應用到物體的哪一個面上;pname說明一個特定的材質(zhì);param是材質(zhì)的具體數(shù)值,若函數(shù)為向量形式,則param是一組值的指針,反之為參數(shù)值本身。非向量形式僅用于設置GL_SHINESS。pname參數(shù)值具體內(nèi)容見表10-1。另外,參數(shù)GL_AMBIENT_AND_DIFFUSE表示可以用相同的 RGB值設置環(huán)境光顏色和漫反射光顏色。

    參數(shù)名 缺省值 說明
    GL_AMBIENT (0.2, 0.2, 0.2, 1.0) 材料的環(huán)境光顏色
    GL_DIFFUSE (0.8, 0.8, 0.8, 1.0) 材料的漫反射光顏色
    GL_AMBIENT_AND_DIFFUSE   材料的環(huán)境光和漫反射光顏色
    GL_SPECULAR (0.0, 0.0, 0.0, 1.0) 材料的鏡面反射光顏色
    GL_SHINESS 0.0 鏡面指數(shù)(光亮度)
    GL_EMISSION (0.0, 0.0, 0.0, 1.0) 材料的輻射光顏色
    GL_COLOR_INDEXES (0, 1, 1) 材料的環(huán)境光、漫反射光和鏡面光顏色
    表10-2 函數(shù)glMaterial*()參數(shù)pname的缺省值

    例10-3 材質(zhì)定義例程light1.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)
    {
        /* 設置材質(zhì)的各種光的顏色成分反射比率 */
        GLfloat mat_ambient[]={0.8,0.8,0.8,1.0};
    GLfloat mat_diffuse[]={0.8,0.0,0.8,1.0};
         /* 紫色 */
        GLfloat mat_specular[] = { 1.0, 0.0, 1.0, 1.0 }; /* 亮紫色 */
        GLfloat mat_shininess[] = { 50.0 };

    GLfloat light_position[] = { 1.0, 1.0, 1.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, mat_shininess);

    glLightfv(GL_LIGHT0, GL_POSITION, light_position);

    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);
    glDepthFunc(GL_LESS);
    glEnable(GL_DEPTH_TEST);
    }

    void CALLBACK display(void)
    {
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    auxSolidSphere(1.0);
    glFlush();
    }

    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)w/(GLfloat)h, 1.5*(GLfloat)w/(GLfloat)h, -1.5, 1.5, -10.0, 10.0);
    glMatrixMode(GL_MODELVIEW); 
    glLoadIdentity();
    }

    void main(void)
    {
    auxInitDisplayMode (AUX_SINGLE | AUX_RGBA | AUX_DEPTH16);
    auxInitPosition (0, 0, 500, 500);
    auxInitWindow ("Lighting_1 ");
    myinit();
    auxReshapeFunc (myReshape);
    auxMainLoop(display);
    }


    以上程序運行結果是一個紫色的球。在函數(shù)myinit()中定義了球的材質(zhì)顏色,光源的定義仍延用light0.c中的,而light.c物體的光源定義為缺省形式。從例子中明顯地看出,物體的材質(zhì)顏色定義與光源顏色定義幾乎一樣,物體反射到眼中的顏色與二者都有關系,具體關系請看下一小節(jié)。

    10.4.3 材質(zhì)RGB值和光源RGB值的關系
    材質(zhì)的顏色與光源的顏色有些不同。對于光源,R、G、B值等于R、G、B對其最大強度的百分比。若光源顏色的R、G、B值都是1.0,則是最強的白光;若值變?yōu)?.5,顏色仍為白色,但強度為原來的一半,于是表現(xiàn)為灰色;若R=G=1.0,B=0.0,則光源為黃色。對于材質(zhì),R、G、B值為材質(zhì)對光的 R、G、B成分的反射率。比如,一種材質(zhì)的R=1.0、G=0.5、B=0.0,則材質(zhì)反射全部的紅色成分,一半的綠色成分,不反射藍色成分。也就是說,若OpenGL的光源顏色為(LR、LG、LB),材質(zhì)顏色為(MR、MG、MB),那么,在忽略所有其他反射效果的情況下,最終到達眼睛的光的顏色為(LR*MR、LG*MG、LB*MB)。
    同樣,如果有兩束光,相應的值分別為(R1、G1、B1)和(R2、G2、B2),則OpenGL 將各個顏色成分相加,得到(R1+R2、G1+G2、B1+B2),若任一成分的和值大于1(超出了設備所能顯示的亮度)則約簡到1.0。下面一例程就說明了二者之間的關系。

    例10-4 材質(zhì)與光源的RGB關系例程light2.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)
    {
    GLfloat mat_ambient[]= { 0.8, 0.8, 0.8, 1.0 };
    GLfloat mat_diffuse[]= { 0.8, 0.0, 0.8, 1.0 }; /* 紫色 */
    GLfloat mat_specular[] = { 1.0, 0.0, 1.0, 1.0 };
    GLfloat mat_shininess[] = { 50.0 };

    GLfloat light_diffuse[]= { 0.0, 0.0, 1.0, 1.0}; /* 藍色 */
    GLfloat light_position[] = { 1.0, 1.0, 1.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, mat_shininess);

    glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse);
    glLightfv(GL_LIGHT0, GL_POSITION, light_position);

    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);
    glDepthFunc(GL_LESS);
    glEnable(GL_DEPTH_TEST);
    }

    void CALLBACK display(void)
    {
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    auxSolidSphere(1.0);
    glFlush();
    }

    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)w/(GLfloat)h, 1.5*(GLfloat)w/(GLfloat)h, -1.5, 1.5, -10.0, 10.0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    }

    void main(void)
    {
    auxInitDisplayMode (AUX_SINGLE | AUX_RGBA | AUX_DEPTH16);
    auxInitPosition (0, 0, 500, 500);
    auxInitWindow ("Lighting_2 ");
    myinit();
    auxReshapeFunc (myReshape);
    auxMainLoop(display);
    }


    以上程序運行結果是一個藍色的球,其中高光部分仍為上一例的亮紫色。從上可看出,球漫反射光的結果是mat_diffuse[]與 light_diffuse[]中的三個顏色分量值相乘,即 (0.0*1.0,0.0*1.0,0.8*1.0,1.0*1.0)=(0.0,0.0,0.8,1.0),所以球大部分呈現(xiàn)藍色。

    圖10-4 光照藍色球高光為紅色

    10.4.4 材質(zhì)改變
    在實際應用的許多情況下,不同的物體或同一物體的不同部分都有可能設置不同的材質(zhì),OpenGL函數(shù)庫提供了兩種方式實現(xiàn)這種要求。下面一例程采用的是設置矩陣堆棧來保存不同物體的材質(zhì)信息:

    例10-5 矩陣堆棧改變材質(zhì)例程chgmat1.c

    #include "glos.h"
    #include <GL/gl.h>
    #include <GL/glu.h>
    #include <GL/glaux.h>

    void myinit(void);
    void CALLBACK display(void);
    void CALLBACK myReshape(GLsizei w, GLsizei h);


    /* 初始化z-buffer、光源和光照模型,在此不具體定義材質(zhì)。*/
      void myinit(void)
    {
    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, 3.0, 2.0, 0.0 };
    GLfloat lmodel_ambient[] = { 0.4, 0.4, 0.4, 1.0 };

    glEnable(GL_DEPTH_TEST);
    glDepthFunc(GL_LESS);

    glLightfv(GL_LIGHT0, GL_AMBIENT, ambient);
    glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse);
    glLightfv(GL_LIGHT0, GL_POSITION, position);

    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);

    glClearColor(0.0, 0.1, 0.1, 0.0);
    }

    void CALLBACK display(void)
    {
    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);


    /* 第一行第一列繪制的球僅有漫反射光而無環(huán)境光和鏡面光。*/
        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);
    auxSolidSphere(1.0);
    glPopMatrix();

        /* 第一行第二列繪制的球有漫反射光和鏡面光,并有低高光,而無環(huán)境光 。*/
        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);
    auxSolidSphere(1.0);
    glPopMatrix();


    /* 第一行第三列繪制的球有漫反射光和鏡面光,并有很亮的高光,而無環(huán)境光 。*/
        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);
    auxSolidSphere(1.0);
    glPopMatrix();

    /* 第一行第四列繪制的球有漫反射光和輻射光,而無環(huán)境和鏡面反射光。*/
        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);
    auxSolidSphere(1.0);
    glPopMatrix();

    /* 第二行第一列繪制的球有漫反射光和環(huán)境光,而鏡面反射光。*/
        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);
    auxSolidSphere(1.0);
    glPopMatrix();

    /* 第二行第二列繪制的球有漫反射光、環(huán)境光和鏡面光,且有低高光。*/
        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);
    auxSolidSphere(1.0);
    glPopMatrix();


    /* 第二行第三列繪制的球有漫反射光、環(huán)境光和鏡面光,且有很亮的高光。*/
        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);
    auxSolidSphere(1.0);
    glPopMatrix();

    /* 第二行第四列繪制的球有漫反射光、環(huán)境光和輻射光,而無鏡面光。*/
        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);
    auxSolidSphere(1.0); glPopMatrix();


    /* 第三行第一列繪制的球有漫反射光和有顏色的環(huán)境光,而無鏡面光。*/
        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);
    auxSolidSphere(1.0);
    glPopMatrix();


    /* 第三行第二列繪制的球有漫反射光和有顏色的環(huán)境光以及鏡面光,且有低高光。*/
        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);
    auxSolidSphere(1.0);
    glPopMatrix();


    /* 第三行第三列繪制的球有漫反射光和有顏色的環(huán)境光以及鏡面光,且有很亮的高光。*/
        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);
    auxSolidSphere(1.0);
    glPopMatrix();


    /* 第三行第四列繪制的球有漫反射光和有顏色的環(huán)境光以及輻射光,而無鏡面光。*/
        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);
    auxSolidSphere(1.0);
    glPopMatrix();

    glFlush();
    }

    void CALLBACK myReshape(GLsizei w, GLsizei h)
    {
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    if (w <= (h * 2))
    glOrtho (-6.0, 6.0, -3.0*((GLfloat)h*2)/(GLfloat)w,
    3.0*((GLfloat)h*2)/(GLfloat)w, -10.0, 10.0);
    else
    glOrtho (-6.0*(GLfloat)w/((GLfloat)h*2),
    6.0*(GLfloat)w/((GLfloat)h*2), -3.0, 3.0, -10.0, 10.0);
    glMatrixMode(GL_MODELVIEW);
    }

    void main(void)
    {
    auxInitDisplayMode (AUX_SINGLE | AUX_RGBA);
    auxInitPosition (0, 0, 600, 450);
    auxInitWindow ("Material");
    myinit();
    auxReshapeFunc (myReshape);
    auxMainLoop(display);
    }


    圖10-5 多種光和材質(zhì)的變化效果

    以上程序運行結果是繪制12個球(3行4列)。第一行的球材質(zhì)都沒有環(huán)境反射光,第二行的都有一定的環(huán)境反射光,第三行的都有某種顏色的環(huán)境光。而第一列的球材質(zhì)僅有藍色的漫反射光;第二列的不僅有藍漫反射光,而且還有鏡面反射光,較低的高光;第三列的不僅有藍漫反射光,而且還有鏡面反射光,很亮的高光;第四列的還包括輻射光,但無鏡面光。
    這個程序運用矩陣堆棧多次調(diào)用glMaterialfv()來設置每個球的材質(zhì),也就是改變同一場景中的不同物體的顏色。但由于這個函數(shù)的應用有個性能開銷,因此建議最好盡可能少的改變材質(zhì),以減少改變材質(zhì)時所帶來的性能開銷,可采用另一種方式即改變材質(zhì)顏色,相應函數(shù)為glColorMaterial(),說明如下:

    void glColorMaterial(GLenum face,GLenum mode);

    函數(shù)參數(shù)face指定面,值有GL_FRONT、GL_BACK或GL_FRONT_AND_BACK(缺省值)。mode指定材質(zhì)成分,值有 GL_AMBIENT、GL_DIFFUSE、GL_AMBIENT_AND_DIFFUSE(缺省值)、GL_SPECULAR或 GLEMISSION。
    注意:這個函數(shù)說明了兩個獨立的值,第一個參數(shù)說明哪一個面和哪些面被修改,而第二個參數(shù)說明這些面的哪一個或哪些材質(zhì)成分要被修改。OpenGL并不為每一種face保持獨立的mode變量。在調(diào)用glColorMterial() 以后,首先需要用GL_COLOR_MATERIAL作為參數(shù)調(diào)用glEnable()來啟動顏色材質(zhì),然后在繪圖時調(diào)用glColor*()來改變當前顏色,或用glMaterial()來改變材質(zhì)成分。當不用這種方式來改變材質(zhì)時,可調(diào)用glDisable(GL_COLOR_MATERIAL)來關閉取消。如下面一段代碼:

    glColorMaterial(GL_FRONT,GL_DIFFUSE);
    glEnable(GL_COLOR_MATERIAL);
    glColor3f(0.3,0.5,0.7);
      /* draw some objects here. */
      glcolor3f(0.0,1.0,0.0);
      /* draw other objects here.*/
      glDisable(GL_COLOR_MATERIAL);

    當需要改變場景中大部分方面的單個材質(zhì)時,最好調(diào)用glColorMaterial();當需要修改不止一個材質(zhì)參數(shù)時,最好調(diào)用glMaterial*()。注意,當不需要顏色材質(zhì)時一定要關閉它,以避免相應的開銷。下面來看一個顏色材質(zhì)的具體應用例子:

    例10-6 顏色定義改變材質(zhì)例程chgmat2.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 myinit(void)
    {
    GLfloat light_position[] = { 1.0, 1.0, 1.0, 0.0 };

    glLightfv(GL_LIGHT0, GL_POSITION, light_position);

    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);
    glDepthFunc(GL_LESS);
    glEnable(GL_DEPTH_TEST);

    glColorMaterial(GL_FRONT, GL_DIFFUSE);
    glEnable(GL_COLOR_MATERIAL);
    }

    void CALLBACK display(void)
    {
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    /* draw one yellow ball */
        glLoadIdentity();
    glTranslatef(-0.7,0.0,0.0);
    glColor3f(1.0,1.0,0.0);
    auxSolidSphere(0.5);

    /* draw one red cone */
        glLoadIdentity();
    glRotatef(-65.0,1.0,0.0,0.0);
    glTranslatef(0.7,0.0,0.0);
    glColor3f(1.0,0.0,0.0);
    auxSolidCone(0.4,0.6);

    glFlush();
    }

    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)w/(GLfloat)h, 1.5*(GLfloat)w/(GLfloat)h, -1.5, 1.5, -10.0, 10.0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    }

    void main(void)
    {
    auxInitDisplayMode (AUX_SINGLE | AUX_RGB | AUX_DEPTH16);
    auxInitPosition (0, 0, 500, 500);
    auxInitWindow ("ColorMaterial Mode");
    myinit();
    auxReshapeFunc (myReshape);
    auxMainLoop(display);
    }


    以上程序改變的是漫反射顏色。場景中顯示了一個黃色的球和一個紅色的錐體。

    圖10-6 漫反射材質(zhì)改變
    posted on 2010-08-24 14:03 MEYE 閱讀(3586) 評論(0)  編輯  收藏 所屬分類: Android3D
    主站蜘蛛池模板: 亚洲日韩图片专区第1页| 免费国产污网站在线观看15| 亚洲XX00视频| 美美女高清毛片视频黄的一免费| 最近的中文字幕大全免费版| 亚洲最大的成人网| 成人免费无毒在线观看网站 | 精品免费AV一区二区三区| 免费看www视频| 九九精品国产亚洲AV日韩| 国产免费变态视频网址网站 | 久久久久亚洲精品无码蜜桃| 1000部羞羞禁止免费观看视频| 亚洲黄色在线观看视频| 久草视频免费在线观看| 亚洲av日韩av综合| 在线jyzzjyzz免费视频| 亚洲av永久中文无码精品| 最好免费观看韩国+日本 | 丁香五月亚洲综合深深爱| 中文字幕免费在线看线人动作大片 | 国产区在线免费观看| 国产aⅴ无码专区亚洲av| 三年片在线观看免费大全电影 | 亚洲成AV人片一区二区密柚| 久久99青青精品免费观看| 久久亚洲日韩看片无码| 丁香花免费高清视频完整版 | 久久免费看少妇高潮V片特黄| 337p欧洲亚洲大胆艺术| 少妇高潮太爽了在线观看免费| 亚洲日本天堂在线| 全亚洲最新黄色特级网站 | 国产午夜无码片免费| 久久久久亚洲AV片无码下载蜜桃| 美女视频黄的全免费视频| 亚洲日本一线产区和二线产区对比| 免费国产成人高清在线观看麻豆| 国产VA免费精品高清在线| 亚洲人成网站在线播放影院在线| 91在线视频免费91|