三維投影總結:數學原理、投影幾何、OpenGL教程、個人方法

        若是要獲得pose視圖,除非有精密的測量方法,不然進行大量的樣本採集時很耗時耗力的。能夠採起一些取巧的方法,正如A Survey on Partial of 3d shapes,描述的,能夠利用已獲得的3D模型,利用投影的方法 (page10-透視投影或者正射投影),自動獲得精確的3D單向視圖。html

        其中的遇到了好幾個難題:透視投影的視角問題;單側面的曲面補全問題(曲面插值問題);pose特徵的描述性問題git

        一篇文章看完視覺及相關通略。
程序員


先普及一下基礎知識:算法

一:圖像處理、計算機圖形學、計算機視覺和模式識別領域的區別和聯繫編程

        三者之間既有區別,又有聯繫。 原文連接:http://blog.csdn.net/qianmianyuan/article/details/9130805數組

        計算機圖形學是給定關於景象結構、表面反射特性、光源配置及相機模型的信息,生成圖像。 而計算機視覺是給定圖象,推斷景象特性實現的是從模型到圖像的變換,也就是說從圖象數據提取信息,包括景象的三維結構,運動檢測,識別物體等。 模式識別則是從特徵空間到類別空間的變換。研究內容包括特徵提取( PCA,LDA,LFA,Kernel,Mean Shift,SIFT,ISOMAP,LLE);特徵選擇;分類器設計(SVM,AdaBoost)等。 

       總之,計算機圖形學是計算機視覺的逆問題,二者從最初相互獨立的平行發展到最近的融合是一大趨勢。圖像模式的分類是計算機視覺中的一個重要問題,模式識別中的許多方法能夠應用於計算機視覺中。緩存

       圖形學講的是圖形,也就是圖形的構造方式,是一種從無到有的概念,從數據獲得圖像。數字圖像處理是對已有的圖像進行變換、分析、重構,獲得的還是圖像。PR本質就是分類,根據常識或樣本或兩者結合進行分類,能夠對圖像進行分類,從圖像獲得數據。服務器

       

       Computer Graphics和Computer Vision是同一過程的兩個方向。Computer Graphics將抽象的語義信息轉化成圖像,Computer Vision從圖像中提取抽象的語義信息。Image Processing探索的是從一個圖像或者一組圖像之間的互相轉化和關係,與語義信息無關。
網絡

先說區別
        Computer Graphics,簡稱 CG 。輸入的是對虛擬場景的描述,一般爲多邊形數組,而每一個多邊形由三個頂點組成,每一個頂點包括三維座標、貼圖座標、rgb顏色等。輸出的是圖像,即二維像素數組。
        Computer Vision,簡稱 CV。輸入的是圖像或圖像序列,一般來自相機或usb攝像頭。輸出的是對於圖像序列對應的真實世界的理解,好比檢測人臉、識別車牌。
       Digital Image Processing,簡稱 DIP。輸入的是圖像,輸出的也是圖像。Photoshop中對一副圖像應用濾鏡就是典型的一種圖像處理。常見操做有模糊、灰度化、加強對比度等。
併發

        能夠簡單地理解爲:圖像學更側重於圖像的字母表和語法部分;而計算機視覺更側重於圖像從語法到語義的部分;而數字圖像處理側重於圖像的狀態空間轉化過程及總結其通常規律。


再說聯繫:
         CG 中也會用到 DIP,現今的三維遊戲爲了增長表現力都會疊加全屏的後期特效,原理就是 DIP,只是將計算量放在了顯卡端。CV 更是大量依賴 DIP 來打雜活,好比對須要識別的照片進行預處理。最後還要提到近年來的熱點——加強現實(AR),它既須要 CG,又須要 CV,固然也不會漏掉 DIP。它用 DIP 進行預處理,用 CV 進行跟蹤物體的識別與姿態獲取,用 CG 進行虛擬三維物體的疊加。

          簡單點說吧,1 計算機視覺,裏面人工智能的東西更多一些,不只僅是圖像處理的知識,還涵蓋了人工智能,機器學習等領域知識;2,計算機圖形學,主要涉及圖造成像及遊戲類開發,如opengl等,還有就是視頻渲染等;3,圖像處理,這個主要針對圖像圖像的基本處理,如圖像檢索或則圖像識別,壓縮,復原等等操做。以上只是本人字面理解


         計算機圖形學和數字圖像處理是比較老的技術。計算機視覺要遲幾十年才提出。計算機圖形學和數字圖像處理的區別在於圖形和圖像。
         圖形是矢量的、純數字式的。圖像經常由來自現實世界的信號產生,有時也包括圖形。而圖像和圖形都是數據的簡單堆積,計算機視覺要從圖像中整理出一些信息或統計數據,也就是說要對計算機圖像做進一步的分析。
以上是它們的區別,下面來講聯繫:
       計算機圖形學的研究成果能夠用於產生數字圖像處理所須要的素材,計算機視覺須要以數字圖像處理做爲基礎。計算機視覺與數字圖像處理的這種關係相似於物理學和數學的關係。

       你能夠多看看浙大周昆的文章,周昆是計算機圖形學這個領域的領軍人物另外,你若是不是浙江大學的或者中科院計算所的,不建議作這個方向,難度太大(圖形比圖像雖然表面上只高一維,但實際上工做量大了好多倍;其次,圖像,國內外差距目前已經很小,好發重要期刊;圖形,除上面兩個單位和微軟外,國內外差距很大,很差發重要期刊)

        數字圖像處理主要是對已有的圖像,好比說可見光的圖像、紅外圖像、雷達成像進行噪聲濾除、邊緣檢測、圖像恢復等處理,就像用ps 處理照片同樣的。人臉識別啊、指紋識別啊、運動物體跟蹤啊,都屬於圖像處理。去噪有各類濾波算法;其餘的有各類時頻變化算法,如傅里葉變化,小波變換等,有不少這方面的書籍。
        圖形學主要研究如何生成圖形的,像用autoCAD做圖,就是圖形學中算法的應用。各類動漫軟件中圖形算法的生成等。



二:數學的三大核心領域數學的三大核心領域——代數學、幾何學、分析學

   原文連接:http://blog.sciencenet.cn/blog-81613-682181.html

    數學發展到如今,已經成爲科學世界中擁有100多個主要分支學科的龐大的共和國。大致說來,數學中研究數的部分屬於代數學的範疇;研究形的部分,屬於幾何學的範籌;溝通形與數且涉及極限運算的部分,屬於分析學的範圍。這三大類數學構成了整個數學的本體與核心。在這一核心的周圍,因爲數學經過數與形這兩個概念,與其它科學互相滲透,而出現了許多邊緣學科和交叉學科。本章簡要介紹數學三大核心領域中十幾門主要分支學科的有關歷史發展狀況。

1、代數學範疇


1、算術
  
算術有兩種含義,一種是從中國傳下來的,至關於通常所說的數學,如《九章算術》等。另外一種是從歐洲數學翻譯過來的,源自希臘語,有計算技術之意。如今通常所說的算術,每每指天然數的四則運算;若是是在高等數學中,則有數論的含義。做爲現代小學課程內容的算術,主要講的是天然數、正分數以及它們的四則運算,並經過由計數和度量而引發的一些最簡單的應用題加以鞏固。
  算術是數學中最古老的一個分支,它的一些結論是在長達數千年的時間裏,緩慢而逐漸地創建起來的。它們反映了在許多世紀中積累起來,並不斷凝固在人們意識中的經驗。
  天然數:是在對於對象的有限集合進行計算的過程當中,產生的抽象概念。平常生活中要求人們不只要計算單個的對象,還要計算各類量,例如長度、重量和時間。爲了知足這些簡單的量度須要,就要用到分數。
  現代初等算術運算方法的發展,起源於印度,時間可能在10世紀或11世紀。它後來被阿拉伯人採用,以後傳到西歐。15世紀,它被改形成如今的形式。在印度算術的後面,明顯地存在着我國古代的影響。
  19世紀中葉,格拉斯曼第一次成功地挑選出一個基本公理體系,來定義加法與乘法運算;而算術的其它命題,能夠做爲邏輯的結果,從這一體系中被推導出來。後來,皮亞諾進一步完善了格拉斯曼的體系。
  算術的基本概念和邏輯推論法則,以人類的實踐活動爲基礎,深入地反映了世界的客觀規律性。儘管它是高度抽象的,但因爲它歸納的原始材料是如此普遍,所以咱們幾乎離不開它。同時,它又構成了數學其它分支的最堅實的基礎。

2、初等代數
  做爲中學數學課程主要內容的初等代數,其中心內容是方程理論。代數一詞的拉丁文原意是歸位。代數方程理論在初等代數中是由一元一次方程向兩個方面擴展的:其一是增長未知數的個數,考察由有幾個未知數的若干個方程所構成的二元或三元方程組(主要是一次方程組);其二是增高未知量的次數,考察一元二次方程或準二次方程。初等代數的主要內容在16世紀便已基本上發展完備了
  古巴比倫(公元前19世紀~前17世紀)解決了一次和二次方程問題,歐幾里得的《本來》(公元前4世紀)中就有用幾何形式解二次方程的方法。我國的《九章算術》(公元1世紀)中有三次方程和一次聯立方程組的解法,並運用了負數。3世紀的丟番圖用有理數求一次、二次不定方程的解。13世紀我國出現的天元術(李冶《測圓海鏡》)是有關一元高次方程的數值解法。16世紀意大利數學家發現了三次和四次方程的解法。
  代數學符號發展的歷史,可分爲三個階段。第一個階段爲三世紀以前,對問題的解不用縮寫和符號,而是寫成一篇論文,稱爲文字敘述代數。第二個階段爲三世紀至16世紀,對某些較常出現的量和運算採用了縮寫的方法,稱爲簡化代數。三世紀的丟番圖的傑出貢獻之一,就是把希臘代數學簡化,開創了簡化代數。然而此後文字敘述代數,在除了印度之外的世界其它地方,還十分普通地存在了好幾百年,尤爲在西歐一直到15世紀。第三個階段爲16世紀之後,對問題的解多半表現爲由符號組成的數學速記,這些符號與所表現的內容沒有什麼明顯的聯繫,稱爲符號代數。16世紀韋達的名著《分析方法入門》,對符號代數的發展有很多貢獻。16世紀末,維葉特開創符號代數,經笛卡爾改進後成爲現代的形式。
  號第一次在數學書中出現,是1489年魏德曼的著做。不過正式爲你們所公認,做爲加、減法運算的符號,那是從1514年由荷伊克開始的。1540年,雷科德開始使用如今使用。到1591年,韋達在著做中大量使用後,才逐漸爲人們所接受。1600年哈里奧特創用大於號和小於號1631年,奧屈特給出「×」「÷」做爲乘除運算符。1637年,笛卡爾第一次使用了根號,並引進用字母表中頭前的字母表示已知數、後面的字母表示未知數的習慣作法。至於「≮」「≯」「≠」這三個符號的出現,那是近代的事了。
  數的概念的拓廣,在歷史上並不全是由解代數方程所引發的,但習慣上仍把它放在初等代數裏,以求與這門課程的安排相一致。公元前4世紀,古希臘人發現無理數。公元前2世紀(西漢時期),我國開始應用負數。1545年,意大利的卡爾達諾開始使用虛數。1614年,英國的耐普爾發明對數。17世紀末,通常的實數指數概念才逐步造成。

3、高等代數-多項式方程
   在高等代數中,一次方程組(即線性方程組)發展成爲線性代數理論;而、二次方程發展成爲多項式理論。前者是向量空間、線性變換、型論、不變量論和張量代數等內容的一門近世代數分支學科,然後者是研究只含有一個未知量的任意次方程的一門近世代數分支學科。做爲大學課程的高等代數,只研究它們的基礎。
  1683年關孝和(日本人)最先引入行列式概念。關於行列式理論最系統的論述,則是雅可比1841年的《論行列式的造成與性質》一書。在邏輯上,矩陣的概念先於行列式的概念;而在歷史上,次序正相反。凱雷在1855年引入了矩陣的概念,在1858年發表了關於這個課題的第一篇重要文章《矩陣論的研究報告》。
  19世紀,行列式和矩陣受到人們極大的關注,出現了千餘篇關於這兩個課題的文章。可是,它們在數學上並非大的改革,而是速記的一種表達式。不過已經證實它們是高度有用的工具。
  多項式代數的研究始於對34次方程求根公式的探索。1515年,菲洛解決了被簡化爲缺2次項的3次方程的求解問題。1540年,費爾拉里成功地發現了通常4次方程的代數解法。人們繼續尋求5次、6次或更高次方程的求根公式,但這些努力在200多年中付諸東流。
  1746年,達朗貝爾首先給出了代數學基本定理的證實(有不完善之處)。這個定理斷言:每個實係數或復係數的n次代數方程,至少有一個實根或復根。所以,通常地說,n次代數方程應當有n個根。1799年,22歲的高斯在寫博士論文中,給出了這個定理的第一個嚴格的證實。1824年,22歲的阿貝爾證實了:高於4次的通常方程的所有係數組成的根式,不多是它的根。1828年,年僅17歲的伽羅華創立了伽羅華理論,包含了方程能用根號解出的充分必要條件。

4、數論
  以正整數做爲研究對象的數論,能夠看做是算術的一部分,但它不是以運算的觀點,而是以數的結構的觀點,即一個數可用性質較簡單的其它數來表達的觀點來研究數的。所以能夠說,數論是研究由整數按必定形式構成的數系的科學。
  早在公元前3世紀,歐幾里得的《本來》討論了整數的一些性質。他證實素數的個數是無窮的,他還給出了求兩個數的公約數的展轉相除法。這與我國《九章算術》中的更相減損法是相同的。埃拉托色尼則給出了尋找不大於給定的天然數N的所有素數的篩法:在寫出從1N的所有整數的紙草上,依次挖去2357……的倍數(各自的2倍,3倍,……)以及1,在這篩子般的紙草上留下的便全是素數了。
  當兩個整數之差能被正整數m除盡時,便稱這兩個數對於」m同餘。我國《孫子算經》(公元4世紀)中計算一次同餘式組的求一術,有中國剩餘定理之稱。13世紀,秦九韶已創建了比較完整的同餘式理論——「大衍求一術,這是數論研究的內容之一。
  丟番圖的《算術》中給出了求x?y?z?全部整數解的方法。費爾馬指出x^ny^nz^nn3時無整數解,對於該問題的研究產生了19世紀的數論。以後高斯的《數論研究》(1801)造成了系統的數論。
   數論的古典內容基本上不借助於其它數學分支的方法,稱爲初等數論。17世紀中葉之後,曾受數論影響而發展起來的代數、幾何、分析、機率等數學分支,又反過來促進了數論的發展,出現了代數數論(研究整係數多項式的根—「代數數」)、幾何數論(研究直線座標系中座標均爲整數的所有整點」—「空間格網」)19世紀後半期出現瞭解析數論,用分析方法研究素數的分佈。二十世紀出現了完備的數論理論。

5、抽象代數-近世代數
   1843年,哈密頓發明了一種乘法交換律不成立的代數——四元數代數。第二年,格拉斯曼推演出更有通常性的幾類代數。1857年,凱雷設計出另外一種不可交換的代數——矩陣代數。他們的研究打開了抽象代數(也叫近世代數)的大門。實際上,減弱或刪去普通代數的某些假定,或將某些假定代之以別的假定(與其他假定是相容的),就能研究出許多種代數體系。
   1870年,克隆尼克給出了有限阿貝爾羣的抽象定義;狄德金開始使用的說法,並研究了代數體;1893年,韋伯定義了抽象的體;1910年,施坦尼茨展開了體的通常抽象理論;狄德金和克隆尼克創立了環論;1910年,施坦尼茨總結了包括羣、代數、域等在內的代數體系的研究,開創了抽象代數學。
   1926年,諾特完成了理想()理論;1930年,畢爾霍夫創建格論,它源於1847年的布爾代數;第二次世界大戰後,出現了各類代數系統的理論和布爾巴基學派;1955年,嘉當、格洛辛狄克和愛倫伯克創建了同調代數理論。
   到如今爲止,數學家們已經研究過200多種這樣的代數結構,其中最主要德若當代數和李代數是不服從結合律的代數的例子。這些工做的絕大部分屬於20世紀,它們使通常化和抽象化的思想在現代數學中獲得了充分的反映。
   抽象代數是研究各類抽象的公理化代數系統的數學學科。典型的代數系統有羣、環、域等,它們主要起源於19世紀的羣論,包含有羣論、環論、伽羅華理論、格論、線性代數等許多分支,並與數學其它分支相結合產生了代數幾何、代數數論、代數拓撲、拓撲羣等新的數學學科。抽象代數已經成了當代大部分數學的通用語言。(其中李羣機器學習方法稱爲機器學習方法的一個重要分支)
  如今,能夠籠統地把代數學解釋爲關於字母計算的學說,但字母的含義是在不斷地拓廣的。在初等代數中,字母表示數;而在高等代數和抽象代數中,字母則表示向量(n元有序數組)、矩陣、張量、旋量、超複數等各類形式的量。能夠說,代數已經發展成爲一門關於形式運算的通常學說了。


2、幾何學範疇
1、初等幾何-測量幾何
  在希臘語中,幾何學是由測量合併而來的,原本有測量土地的含義,意譯就是測地術幾何學這個名詞,系我國明代數學家根據讀音譯出的,沿用至今。
  如今的初等幾何主要是指歐幾里得幾何,它是討論圖形(點、線、面、角、圓等)在運動下的不變性質的科學。例如,歐氏幾何中的兩點之間的距離,兩條直線相交的交角大小,半徑是r的某一圓的面積等都是一些運動不變量。
  初等幾何做爲一門課程來說,安排在初等代數以後;然而在歷史上,幾何學的發展曾優先於代數學,它主要被認爲是古希臘人的貢獻。
  幾何學捨棄了物質全部的其它性質,只保留了空間形式和關係做爲本身研究的對象,所以它是抽象的。這種抽象決定了幾何的思惟方法,就是必須用推理的方法,從一些結論導出另外一些新結論。定理是用演繹的方式來證實的,這種論證幾何學的表明做,即是公元前三世紀歐幾里得的《本來》,它從定義與公理出發,演繹出各類幾何定理。
  如今中學《平面三角》中關於三角函數的理論是15世紀才發展完善起來的,可是它的一些最基本的概念,卻早在古代研究直角三角形時便己造成。所以,可把三角學劃在初等幾何這一標題下。
  古代埃及、巴比倫、中國、希臘都研究過有關球面三角的知識。公元前2世紀,希帕恰斯製做了弦表,能夠說是三角的創始人。後來印度人制做了正弦表;阿拉伯的阿爾·巴塔尼用計算sinθ值的方法來解方程,他還與阿布爾·沃法共同導出了正切、餘切、正割、餘割的概念;賴蒂庫斯做了較精確的正弦表,並把三角函數與圓弧聯繫起來。
  因爲直角三角形是最簡單的直線形,又具備很重要的實用價值,因此各文明古國都極重視它的研究。我國《周髀算經》一開始就記載了周朝初年(約公元前1100年左右)的周公與學者商高的對話,其中就談到勾三股四弦五,即勾股定理的特殊形式;還記載了在周公以後的陳子,曾用勾股定理和類似圖形的比例關係,推算過地球與太陽的距離和太陽的直徑,同時爲勾股定理做的圖注達幾十種之多。在國外,傳統稱勾股定理爲畢達哥拉斯定理,認爲它的第一個一致性的證實源於畢氏學派(公元前6世紀),雖然巴比倫人在此之前1000多年就發現了這個定理。到如今人們對勾股定理已經至少提供了370種證實。
  19世紀以來,人們對於關於三角形和圓的初等綜合幾何,又進行了深刻的研究。至今這一研究領域仍然沒有到頭,很多資料已引伸到四面體及伴隨的點、線、面、球。

2、射影幾何-變換幾何
  射影幾何學是一門討論在把點射影到直線或平面上的時候,圖形的不變性質的一門幾何學。幻燈片上的點、線,通過幻燈機的照射投影,在銀幕上的圖畫中都有相對應的點線,這樣一組圖形通過有限次透視之後,變成另外一組圖形,這在數學上就叫作射影對應。射影幾何學在航空、攝影和測量等方面都有普遍的應用。
  射影幾何是迪沙格和帕斯卡在1639年開闢的。迪沙格發表了本關於圓維曲線的頗有首創性的小冊子,從開普勒的連續性原理開始,導出了許多關於對合、調和變程、透射、極軸、極點以及透視的基本原理,這些課題是今天學習射影幾何這門課程的人所熟悉的。年僅16歲的帕斯卡得出了一些新的、深奧的定理,並於9年後寫了一分內容很豐富的手稿。18世紀後期,蒙日提出了二維平面上的適當投影表達三維對象的方法,於是從提供的數據能快速算出炮兵陣地的位置,避開了冗長的、麻煩的算術運算。
  射影幾何真正獨立的研究是由彭賽勒開創的。1822年,他發表了《論圖形的射影性質》一文,給該領域的研究以巨大的推進做用。他的許多概念被斯坦納進一步發展。1847年,斯陶特發表了《位置幾何學》一書,使射影幾何最終從測量基礎中解脫出來。
  後來證實,採用度量適當的射影定義,能在射影幾何的範圍內研究度量幾何學。將一個不變二次曲線添加到平面上的射影幾何中,就能獲得傳統的非歐幾何學。在19世紀晚期和20世紀初期,對射影幾何學做了多種公設處理,而且有限射影幾何也被發現。事實證實,逐漸地增添和改變公設,就能從射影幾何過渡到歐幾里得幾何,其間經歷了許多其它重要的幾何學。
 
3解析幾何-代數幾何化
   解析幾何即座標幾何,包括平面解析幾何和立體解析幾何兩部分。解析幾何經過平面直角座標系和空間直角座標系,創建點與實數對之間的一一對應關係,從而創建起曲線或曲面與方程之間的一一對應關係,於是就能用代數方法研究幾何問題,或用幾何方法研究代數問題。
   在初等數學中,幾何與代數是彼此獨立的兩個分支;在方法上,它們也基本是互不相關的。解析幾何的創建,不只因爲在內容上引入了變量的研究而開創了變量數學,並且在方法上也使幾何方法與代數方法結合起來。
   在迪沙格和帕斯卡開闢了射影幾何的同時,笛卡兒和費爾馬開始構思現代解析幾何的概念。這兩項研究之間存在一個根本區別:前者是幾何學的一個分支,後者是幾何學的一種方法。
   1637年,笛卡兒發表了《方法論》及其三個附錄,他對解析幾何的貢獻,就在第三個附錄《幾何學》中,他提出了幾種由機械運動生成的新曲線。在《平面和立體軌跡導論》中,費爾馬解析地定義了許多新的曲線。在很大程度上,笛卡兒從軌跡開始,而後求它的方程;費爾馬則從方程出發,而後來研究軌跡。這正是解析幾何基本原則的兩個相反的方面,解析幾何的名稱是之後才定下來的。
   這門課程達到如今課本中熟悉的形式,是100多年之後的事。象今天這樣使用座標、橫座標、縱座標這幾個術語,是萊布尼茲於1692年提出的。1733年,年僅18歲的克雷洛出版了《關於雙重曲率曲線的研究》一書,這是最先的一部空間解析幾何著做。1748年,歐拉寫的《無窮分析概要》,能夠說是符合現代意義的第一部解析幾何學教程。1788年,拉格朗日開始研究有向線段的理論。1844年,格拉斯曼提出了多維空間的概念,並引入向量的記號。因而多維解析幾何出現了。
   解析幾何在近代的發展,產生了無窮維解析幾何和代數幾何等一些分支。普通解析幾何只不過是代數幾何的一部分,而代數幾何的發展同抽象代數有着密切的聯繫。

4非歐幾何-廣義空間幾何
  非歐幾何有三種不一樣的含義:狹義的,單指羅氏(羅巴切夫斯基)幾何;廣義的,泛指一切和歐氏(歐幾里得)幾何不一樣的幾何;一般意義的,指羅氏幾何和黎曼幾何。
  歐幾里得的第5公設(平行公設)在數學史上佔有特殊的地位,它與前4條公設相比,性質顯得太複雜了。它在《本來》中第一次應用是在證實第29個定理時,並且此後彷佛老是儘可能避免使用它。所以人們懷疑第五公設的公理地位,並探索用其它公理來證實它,以使它變爲一條定理。在三千多年的時間中,進行這種探索並有案可查的就達兩千人以上,其中包括許多知名的數學家,但他們都失敗了。
  羅巴契夫斯基於1826年,鮑耶於1832年發表了劃時代的研究結果,開創了非歐幾何。在這種幾何中,他們假設過不在已知直線上的一點,能夠引至少兩條直線平行於已知直線,用以代替第五公設,同時保留了歐氏幾何的其它公設。
  1854年,黎曼推出了另外一種非歐幾何。在這種幾何中,他假設過已知直線外一點,沒有和已知直線平行的直線可引,用以代替第5公設,同時保留了歐氏幾何的其它公設。1871年,克萊因把這3種幾何:羅巴契夫斯基鮑耶的、歐幾里得的和黎曼的分別定名爲雙曲幾何、拋物幾何和橢圓幾何。
  非歐幾何的發現不只最終解決了平行公設的問題——平行公設被證實是獨立於歐氏幾何的其它公設的,並且把幾何學從其傳統模型中解放出來,創造了許多不一樣體系的幾何的道路被打開了。
  1854年,黎曼發表了關於做爲幾何學基礎的假設的講演。他指出:每種不一樣的(兩個無限靠近的點的)距離公式決定了最終產生的空間和幾何的性質。1872年,克萊因創建了各類幾何系統按照不一樣變換羣不變量的分類方法。
  19世紀之後,幾何空間概念發展的另外一方向,是按照所研究流形的微分幾何原則的分類,每一種幾何都對應着一種定理系統。1899年,希爾伯特發表了《幾何基礎》一書,提出了完備的幾何公理體系,創建了歐氏幾何的嚴密的基礎,並給出了證實一個公理體系的相容性(無矛盾性)、獨立性和完備性的廣泛原則。按照他的觀點,不一樣的幾何空間乃是從屬於不一樣幾何公理要求的元素集合。歐氏幾何和非歐幾何,在大量的幾何系統中,只不過是極其特殊的情形罷了。

5拓撲學-代數拓撲和幾何拓撲
  1736年,歐拉發表論文,討論哥尼斯堡七橋問題。他還提出球面三角形剖分圖形頂點、邊、面之間關係的歐拉公式,這能夠說是拓撲學的開端。
  龐加萊於18951904年創建了拓撲學,採用代數組合的方法研究拓撲性質。他把歐拉公式推廣爲歐拉龐加萊公式,與此有關的理論如今稱爲同調理論和同倫理論。之後的拓撲學主要按照龐加萊的設想發展。
  拓撲學開始是幾何學的一個分支,在二十世紀它獲得了極大的推廣。1906年,弗雷歇發表博士論文,把函數做爲一個來看,把函數收斂描繪成點的收斂,這就把康託的點集論和分析學的抽象化聯繫起來了。他在函數所構成的集合中引入距離的概念,構成距離空間,展開了線性距離空間的理論。在這個基礎上,產生了點集拓撲學。在豪斯道夫的《點集論綱要》一書中,出現了更通常的點集拓撲學的完整想法。第二次世界大戰後,把分析引進拓撲,發展了微分拓撲。
  如今的拓撲學能夠粗略地定義爲對於連續性的數學研究。任何事物的集合都能在某種意義上構成拓撲空間,拓撲學的概念和理論已基本完組成爲數學的基礎理論之一,滲入到各個分支,而且成功地應用於電磁學和物理學的研究。


3、分析學範疇


1、微積分
  微積分學是微分學和積分學的統稱,它是研究函數的導數、積分的性質和應用的一門數學分支學科。
  微積分的出現具備劃時代意義,時至今日,它不只成了學習高等數學各個分支必不可少的基礎,並且是學習近代任何一門天然科學和工程技術的必備工具。如今的微積分學的教程,一般的講授次序是先極限、再微分、後積分,這與歷史順序正好相反。
  在微積分歷史中,最初的問題是涉及計算面積、體積和弧長的。阿基米得(公元前3世紀)的方法最接近於現行的積分法。在17世紀探索微積分的至少有十幾位大數學家和幾十位小數學家。牛頓和萊布尼茨分別進行了創造性的工做,各自獨立地跑完了微積分這場接力賽的最後一棒
  1609年,開普勒爲了計算行星運動第二定律中包含的面積,和在他的論文中討論的酒桶的體積,而藉助了某種積分方法。1635年,卡瓦列利發表了一篇闡述不可分元法的論文,提出卡瓦列利原理,它是計算面積和體積的有價值的工具。1650年,沃利斯把卡瓦列利的方法系統化,並做了推廣。
  微分起源於做曲線的切線和求函數的極大值或極小值問題。雖然能夠追溯到古希臘,可是第一個真正值得注意的先驅工做,是費爾馬1629年陳述的概念。1669年,巴羅對微分理論做出了重要的貢獻,他用了微分三角形,很接近現代微分法。通常認爲,他是充分地認識到微分法爲積分法的逆運算的第一我的。

  至此,還有什麼要作的呢?首要的是,創造通常的符號和一整套形式的解析規則,造成能夠應用的微積分學,這項工做是由牛頓和萊布尼茲彼此獨立地作出的。接着的工做是在可接受的嚴格的基礎上,從新推導基本理論,這必須等到此課題想到多方面應用以後。柯西和他的後繼者們完成了這一工做。
  牛頓早在1665年才23歲時,就創造了流數法(微分學),並發展到能求曲線上任意一點的切線和曲率半徑。他的《流數法》寫於1671年,但直到死後9年的1736年才發表。牛頓考慮了兩種類型的問題,等價於如今的微分和解微分方程。他定義了流數(導數)、極大值、極小值、曲線的切線、曲率、拐點、凸性和凹性,並把它的理論應用於許多求積問題和曲線的求長問題。
  牛頓創立的微積分原理是同他的力學研究分不開的,他藉此發現、並研究了力學三大定律和萬有引力定律,1687年出版了名著《天然哲學的數學原理》。這本書是研究天體力學的,包括了微積分的一些基本概念和原理。
  萊布尼茨是在1673年到1676年之間,從幾何學觀點上獨立發現微積分的。1676年,他第一次用長寫字母表示積分符號,象今天這樣寫微分和微商。1684年~1686年,他發表了一系列微積分著做,力圖找到廣泛的方法來解決問題。今天課本中的許多微分的基本原則就是他推導出來的,如求兩個函數乘積的n階導數的法則,如今仍稱做菜布尼茲法則。萊布尼茲的另外一最大功績是創造了反映事物本質的數字符號,數學分析中的基本概念的記號,例如微分dx,二級微分dx?,積分∫ydx,導數dy/dx等都是他提出來的,而且沿用至今,很是方便。
  牛頓與萊布尼茨的創造性工做有很大的不一樣。主要差異是牛頓把xy的無窮小增量做爲求導數的手段,當增量愈來愈小的時候,導數實際上就是增量比的極限,而萊布尼茲卻直接用xy的無窮小增量(就是微分)求出它們之間的關係。
  這個差異反映了他們研究方向的不一樣,在牛頓的物理學方向中,速度之類是中心概念;而在萊布尼茲的幾何學方向中,卻着眼於面積體積的計算。其它差異是,牛頓自由地用級數表示函數,採用經驗的、具體和謹慎的工做方式,認爲用什麼記號可有可無;而萊布尼茲則寧願用有限的形式來表示函數,採用富於想象的、喜歡推廣的、大膽的工做方式,花費不少時間來選擇富有提示性的符號。
  到1700年,如今大學且學習的大部分微積份內容已經創建起來。第一部微積分課本出版於1696年,是洛比達寫的。1769年,歐拉論述了二重積分。1773年,拉格朗日考察了三重積分。1837年,波爾查諾給出了級數的現代定義。19世紀分析學的嚴謹化,是由柯西奠定的。如今課本中的極限、連續性定義、把導數看做差商的極限、把定積分看作和的權限等等,實質上都是柯西給出的。進一步完成這一工做的是威爾斯特拉斯,他給出瞭如今使用的精確的極限定義,並同狄德金、康託於19世紀70年代創建了嚴格的實數理論,使微積分有了堅固可靠的邏輯基礎。

2、微分方程
  凡是表示未知函數和未知函數的導數以及自變量之間的關係的方程,就叫作微分方程。若是未知函數是一元函數,則稱爲常微分方程,若是未知函數是多元函數,則稱爲偏微分方積。微分方程的基本問題是在必定條件下,從所給出的微分方程解出未知函數。
  微分方程幾乎是與微積分同時發展起來的,因爲它與力學、物理學的淵源很深,因此在13世紀便已自成一門獨立的學科了。兩個多世紀來,這一學科已發展得至關完善。
  1676年,萊布尼茲在致牛頓的信中,首先提出了微分方程這個名稱。在他們兩人的著做中,都包含了許多微分方程的實例。早期的研究側重於探討各種一階方程的解法,並由此致使了方程的分類。18世紀,歐拉解決了全微分方程和歐拉方程」(一類高階變係數線性微分方程),提出了通解和特解的概念,指出了n階線性方程通解的結構。其後,泰勒獲得了方程的奇解;拉格朗日推導了非齊次線性方程的常數交易法。
  對於微分方程組的研究,始於達朗貝爾。19世紀前半葉,柯西開始研究解的存在性和惟一性。19世紀後半葉,數學家們開始利用羣論來研究微分方程,由此創建連續羣和李羣的新理論。龐加萊引入了極限環的概念,李雅普諾夫引入了微分方程組解的穩定性概念。他們的方法都沒必要直接求解,稱爲定性理論。1927年,畢爾霍夫創建了動力系統的一段定性理論。
  一階偏微分方程的研究首先是從幾何學問題開始的。拉格朗日指出,解一階線性偏微分方程的技巧,在於把它們化爲常微分方程。一階非線性偏微分方程的研究,始於歐拉和拉格朗日,蒙日爲偏微分方程的幾何理論奠基了基礎。到18世紀末葉,在引入奇解、通解、全積分、通積分、特積分等概念以後,偏微分方程已造成一門獨立的學科。
  二階偏微分方程的研究,始於18世紀的弦振動理論。一般見的二階偏微分方程均來自物理或力學的實際問題,它們構成了這門學科中一個獨立的系統數學物理方程。
  積分方程源於阿貝爾1826年的工做,可是直到1888年杜·波阿·雷蒙的著做中,才正式提出了積分方程這個名詞。1896年開始,伏特拉給出了兩類積分方程的通常理論;不久,弗雷德荷姆大致上完成了一類重要的線性積分方程理論。因爲這類積分方程常出如今一些物理問題中,所以積分方程論常被包含在數學物理方程內。
  現代科學技術,如空間技術、現代物理學、力學等,都有許多問題須要用微分方程來求解,甚至在化學、生物學、醫藥學、經濟學等方面,微分方程的應用也愈來愈多。

3、微分幾何
  微分幾何這門分支學科主要研究三維歐氏空間中曲線和曲面的內在性質,所謂內在性質就是同幾何對象在空間中的位置無關的性質。它以微積分、微分方程這些分支學科的理論爲研究工具。或簡單地說,微分幾何就是用分析方法研究幾何性質。
  微分幾何的發端可見於1731年克萊洛的著做中。蒙日1809年的著做包含了這一學科的雛型;歐拉研究了曲面的通常理論;高斯1827年的《關於曲面的通常研究》一書,論述了曲面理論,創立了內蘊幾何學,奠基了曲面微分幾何的基礎。18871896年,達布的《曲面通常理論的講義》集曲線和曲面微分幾何之大成。
  變換理論對於微分幾何的影響,產生了射影微分幾何、仿射微分幾何等分支。二十世紀初,出現了對非充分光滑曲線和曲面以及曲線曲面的總體問題的研究,造成現代微分幾何。1923年,嘉當提出了通常聯絡的理論。1945年,陳省身創建了代數拓撲和微分幾何的聯繫,他又是纖維叢概念的建立人之一。

4、函數論
  函數論包括複變函數論和實變函數論,但有時也單指複變函數論(或複分析)而言。
  複數概念出現於16世紀,但對它的全面掌握和普遍運用,卻遲至18世紀。自變量是複數的函數,叫作複變函數。若是複變函數在某一區域內除了可能有有限個例外點以外,到處有導數,那麼這個伏辯函數叫作在這個區域內的解析函數;例外點叫作奇點。複變函數論主要研究解析函數的性質。
  複變函數的研究是從18世紀開始的。3040年代,歐拉利用冪級數詳細討論了初等複變函數的性質。達朗貝爾於1752年得出複變函數可微的必要條件(柯西黎曼條件」)。拉普拉斯也考慮過複變函數的積分。
  複變函數的全面發展是在19世紀。1825年,柯西討論了虛限定積分,1831年他實質上推出了柯西積分公式,並在此基礎上創建了一整套複變函數微分和積分的理論。黎曼1851年的博士論文《複變函數論的基礎》,奠基了複變函數論的基礎。他推廣了單位解析函數到多位解析函數;引入了黎曼曲面的重要概念,確立了復變因數的幾何理論基礎;證實了保角映射基本定理。威爾斯特拉斯徹底擺脫了幾何直觀,以冪級數爲工具,用嚴密的純解析推理展開了函數論。定義解析函數是能夠展開爲冪級數的函數,圍繞着奇點研究函數的性質。近幾十年來,複變函數論又有很大的推動。
  複變函數論是解決工程技術問題的有力工具,飛機飛行理論、熱運動理論、流體力學理論、電場和彈性理論等中的不少問題。
  實變函數的發展較晚,其中積分論是它的重要組成部分。容度和測度是線段長度概念的推廣,是爲了推廣積分的概念而創建起來的。1893年,約當給出了約當容度的概念,並用於討論積分。1894年,斯提捷首先推廣了積分概念,獲得了斯提捷積分1898年,波萊爾改進了容度的概念,他稱之爲測度。下一步決定性的進展是1902年勒貝格改進了測度理論,創建了勒貝格測度勒貝格積分等概念。1904年,他徹底解決了黎曼可積性的問題。後來,數學家們對積分的概念又做了種種推廣和探索。
  實變函數的另外一個領域是函數構造論。1885年,威爾斯特拉斯證實:連續函數必可表示爲一致收斂的多項式級數。這一結果和切比雪夫斯基最佳逼近論,是函數構造論的開端。近年來,這個方向的研究十分活躍。

5、泛函分析
  本世紀初,出現了一個廣闊的新領域——泛函分析,它是古典分析觀點的推廣。近幾十年來,因爲分析學中許多新分支的造成,從而發如今代數、幾何、分析中不一樣領域之間的某些方面的相似。其次,幾何與集合論的結合產生了抽象空間的理論,將函數當作函數空間中的點。再加上實變函數論以及近世代數的感念和方法的影響,就產生了泛畫分析。它綜合函數論,幾何和代數的觀點,研究無窮維向量空間上的函數、算子和極限理論。
  19世紀末,弗爾太拉和二十世紀初阿達瑪的著做中已出現泛函分析的萌芽。隨後希爾伯特、海令哲開創了希爾伯將空間的研究,黎斯、馮·諾伊曼等人在這方面都有重要的建樹。


  未完待續..............................




三:OpenGL圖像學基礎

1、OpenGL與3D圖形世界

          OpenGL是基於圖元建模的典範,粒子建模計算太複雜以致於如今還僅僅是停留在學術領域,做爲將來的方向繼續發展.

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所示。
  
圖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 簡單機械手臂的符合運動



9、OpenGL顏色

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

9.一、計算機顏色

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

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

  9.1.2 RGB色立體(RGB Color Cube
  全部監視器屏幕的顏色都屬於RGB顏色空間,若是用一個立方體形象地表示RGB顏色組成關係,那麼就稱這個立方體爲RGB色立體,如圖9-2所示。

圖9-2 RGB色立體

  在圖中,R、G、B三值的範圍都是從0.0到1.0。若是某顏色份量越大,則表示對應的顏色份量越亮,也就是它在此點所貢獻的顏色成分越多;反之,則越暗或越少。當R、G、B三個值都爲0.0時,此點顏色爲黑色(Black);當三者都爲1.0時,此點顏色爲白色(White);當三個顏色份量值相等時,表示三者貢獻同樣,所以呈現灰色(Grey),在圖中表現爲從黑色頂點到白色頂點的那條對角線;當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.二、顏色模式
  OpenGL顏色模式一共有兩個:RGB(RGBA)模式和顏色表模式。在RGB模式下,全部的顏色定義全用R、G、B三個值來表示,有時也加上 Alpha值(與透明度有關),即RGBA模式。在顏色表模式下,每個象素的顏色是用顏色表中的某個顏色索引值表示,而這個索引值指向了相應的R、G、 B值。這樣的一個表成爲顏色映射(Color Map)。

  9.2.1 RGBA模式(RGBA Mode
  在RGBA模式下,能夠用glColor*()來定義當前顏色。其函數形式爲:

  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值。這個函數有3和4兩種方式,在前一種方式下,a值缺省爲1.0,後一種Alpha值由用戶本身設定,範圍從0.0到1.0。一樣,它也可用指針傳遞參數。另外,函數的第二個後綴的不一樣使用,其相應的參數值及範圍不一樣,見下表9-1所示。雖然這些參數值不一樣,但實際上 OpenGL已自動將它們映射在0.0到1.0或-1.0或範圍以內。所以,靈活使用這些後綴,會給你編程帶來很大的方便。

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

  9.2.2 顏色表模式(Color_Index Mode
  在顏色表方式下,能夠調用glIndex*()函數從顏色表中選取當前顏色。其函數形式爲:

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


  設置當前顏色索引值,即調色板號。若值大於顏色位面數時則取模。

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

9.三、顏色應用舉例
  顏色是一個極具吸引力的應用,在前面幾章中已經逐步介紹了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);
  }


  這個程序運行結果是在屏幕上顯示八個連成扇形的不一樣顏色的三角形,每一個三角形的顏色定義採用顏色表模式。其中,調用了輔助庫函數auxSetOneColor()來裝載顏色映射表,即調色板。由於將某個顏色裝載到顏色查找表(color lookup table)中的過程必須依賴窗口系統,而OpenGL函數與窗口系統無關,因此這裏就調用輔助庫的函數來完成這個過程,而後才調用OpenGL本身的函數glIndex()設置當前的顏色號。

圖9-3 自定義調色板



10、OpenGL光照

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

10.二、光照模型

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

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

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

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

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

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

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

  建立具備某種特性的光源。其中第一個參數light指定所建立的光源號,如GL_LIGHT0、GL_LIGHT一、...、GL_LIGHT7。第二個參數pname指定光源特性,這個參數的輔助信息見表10-1所示。最後一個參數設置相應的光源特性值。

pname 參數名 缺省值 說明
GL_AMBIENT (0.0, 0.0, 0.0, 1.0) RGBA模式下環境光
GL_DIFFUSE (1.0, 1.0, 1.0, 1.0) RGBA模式下漫反射光
GL_SPECULAR (1.0,1.0,1.0,1.0) RGBA模式下鏡面光
GL_POSITION (0.0,0.0,1.0,0.0) 光源位置齊次座標(x,y,z,w)
GL_SPOT_DIRECTION (0.0,0.0,-1.0) 點光源聚光方向矢量(x,y,z)
GL_SPOT_EXPONENT 0.0 點光源聚光指數
GL_SPOT_CUTOFF 180.0 點光源聚光截止角
GL_CONSTANT_ATTENUATION 1.0 常數衰減因子
GL_LINER_ATTENUATION 0.0 線性衰減因子
GL_QUADRATIC_ATTENUATION 0.0 平方衰減因子
表10-1 函數glLight*()參數pname說明

   注意:以上列出的GL_DIFFUSE和GL_SPECULAR的缺省值只能用於GL_LIGHT0,其餘幾個光源的GL_DIFFUSE和GL_SPECULAR缺省值爲(0.0,0.0,0.0,1.0)。另外,表中後六個參數的應用放在下一篇中介紹。在上面例程中,光源的建立爲:

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


  其中light_position是一個指針,指向定義的光源位置齊次座標數組。其它幾個光源特性都爲缺省值。一樣,咱們也可用相似的方式定義光源的其餘幾個特性值,例如:

  GLfloat light_ambient [] = { 0.0, 0.0, 0.0, 1.0 };
  GLfloat light_diffuse [] = { 1.0, 1.0, 1.0, 1.0 };
  GLfloat light_specular[] = { 1.0, 1.0, 1.0, 1.0 };
  glLightfv(GL_LIGHT0, GL_AMBIENT , light_ambient );
  glLightfv(GL_LIGHT0, GL_DIFFUSE , light_diffuse );
  glLightfv(GL_LIGHT0, GL_SPECULAR, light_specular);


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

  glEnable(GL_LIGHTING);

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

  glEnable(GL_LIGHT0);

  其它光源相似,只是光源號不一樣而已。

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

  void glShadeModel(GLenum mode);

  函數參數爲GL_FLAT或GL_SMOOTH,分別表示平面明暗處理和光滑明暗處理。
  應用平面明暗處理模式時,多邊形內每一個點的法向一致,且顏色也一致;應用光滑明暗處理模式時,多邊形全部點的法向是由內插生成的,具備必定的連續性,所以每一個點的顏色也相應內插,故呈現不一樣色。這種模式下,插值方法採用的是雙線性插值法,如圖10-2所示。

圖10-2 Gouraud明暗處理

  Gouraud明暗處理一般算法爲:先用多邊形頂點的光強線性插值出當前掃描線與多邊形邊交點處的光強,而後再用交點的光強線插值處掃描線位於多邊形內區段上每一象素處的光強值。圖中顯示出一條掃描線與多邊形相交,交線的端點是A點和B點,P點是掃描線上位於多邊形內的任一點,多邊形三個頂點的光強分別爲I一、I2和I3.取A點的光強Ia爲I1和I2的線性插值,B點的光強Ib爲I1和I3的線性插值,P點的光強Ip則爲Ia和Ib的線性插值。採用Gouraud明暗處理不但能夠使用多邊形表示的曲面光強連續,並且計算量很小。這種算法還能夠以增量的形式改進,且能用硬件直接實現算法,從而普遍用於計算機實時圖形生成。請看下面光滑明暗處理的例程:

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


  以上程序運行結果是在屏幕上顯示一個色彩連續變化的三角形。這個程序是用的RGBA顯示模式,若改用顏色表模式,則顏色內插其實是顏色表的內插,所以呈現的顏色可能不連續。網友不妨本身試試。
  另外,若在light0.c程序中加上一句定義GL_FLAT明暗處理模式,則又會出現怎樣的情形呢?讀者能夠仔細比較一下。

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

10.四、材質

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

  10.4.2 材質定義
  材質的定義與光源的定義相似。其函數爲:

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

  定義光照計算中用到的當前材質。face能夠是GL_FRONT、GL_BACK、GL_FRONT_AND_BACK,它代表當前材質應該應用到物體的哪個面上;pname說明一個特定的材質;param是材質的具體數值,若函數爲向量形式,則param是一組值的指針,反之爲參數值自己。非向量形式僅用於設置GL_SHINESS。pname參數值具體內容見表10-1。另外,參數GL_AMBIENT_AND_DIFFUSE表示能夠用相同的 RGB值設置環境光顏色和漫反射光顏色。

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

  例10-3 材質定義例程(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)
  {
    /* 設置材質的各類光的顏色成分反射比率 */
    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);
  }


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

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

  例10-4 材質與光源的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),因此球大部分呈現藍色。

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

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

  例10-5 矩陣堆棧改變材質例程(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、光源和光照模型,在此不具體定義材質。*/
  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);

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

    /* 第一行第二列繪製的球有漫反射光和鏡面光,並有低高光,而無環境光 。*/
    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();

    /* 第一行第三列繪製的球有漫反射光和鏡面光,並有很亮的高光,而無環境光 。*/
    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();

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

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

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

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

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

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

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

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

    /* 第三行第四列繪製的球有漫反射光和有顏色的環境光以及輻射光,而無鏡面光。*/
    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 多種光和材質的變化效果

  以上程序運行結果是繪製12個球(3行4列)。第一行的球材質都沒有環境反射光,第二行的都有必定的環境反射光,第三行的都有某種顏色的環境光。而第一列的球材質僅有藍色的漫反射光;第二列的不只有藍漫反射光,並且還有鏡面反射光,較低的高光;第三列的不只有藍漫反射光,並且還有鏡面反射光,很亮的高光;第四列的還包括輻射光,但無鏡面光。
  這個程序運用矩陣堆棧屢次調用glMaterialfv()來設置每一個球的材質,也就是改變同一場景中的不一樣物體的顏色。但因爲這個函數的應用有個性能開銷,所以建議最好儘量少的改變材質,以減小改變材質時所帶來的性能開銷,可採用另外一種方式即改變材質顏色,相應函數爲glColorMaterial(),說明以下:

  void glColorMaterial(GLenum face,GLenum mode);

  函數參數face指定面,值有GL_FRONT、GL_BACK或GL_FRONT_AND_BACK(缺省值)。mode指定材質成分,值有 GL_AMBIENT、GL_DIFFUSE、GL_AMBIENT_AND_DIFFUSE(缺省值)、GL_SPECULAR或 GLEMISSION。
  注意:這個函數說明了兩個獨立的值,第一個參數說明哪個面和哪些面被修改,而第二個參數說明這些面的哪個或哪些材質成分要被修改。OpenGL並不爲每一種face保持獨立的mode變量。在調用glColorMterial() 之後,首先須要用GL_COLOR_MATERIAL做爲參數調用glEnable()來啓動顏色材質,而後在繪圖時調用glColor*()來改變當前顏色,或用glMaterial()來改變材質成分。當不用這種方式來改變材質時,可調用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);

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

  例10-6 顏色定義改變材質例程(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 漫反射材質改變



(4):個人解法

過程 :

    進行視角變換;

    進行整數化;

   進行鄰域填補(也叫膨脹算法);

   進行正射投影;

   檢索二維矩陣;

   逆向分析,根據相機參數和透視關係,還原單側視圖;


算法代碼:


 未完待續............................

相關文章
相關標籤/搜索