Ogre 3D程序設計 Ogre材質1

  “材質(material)”,材質是一個很基本的術語,表示你的物體對光的反射方式。但是對它的實現並不像其解釋這樣簡單,我們會用這一章節進行詳細地介紹Ogre中相關的技術。

  我們剛纔提及過,材質定義了物體對光線反射的處理方法。這裏暗示了材質的表現與光源的類型相關:聚光(Spotlights)、點光源(point lights)以及有向光(directional lights)對材質的表現有着完全不同的影響。簡單來講,它們都是對物理世界真實光源類型的模擬,所以材質對它們的反射也遵守着其在物理世界中的規則。

  注意:雖然在Ogre裏面,材質定義了物體如何反射光線的方法,但實際上並沒有真的把光反射到附近的物體上。換句話說,場景中物體的輻射和反射屬性並沒有給場景增加任何光照。這是因爲Ogre(以及目前絕大多數的即時渲染算法中)使用了局部輻射的算法來處理光照效果。

  爲了幫助大家更好的理解局部輻射,在這裏講解一下與其相對的全局輻射算法的概念。所謂全局輻射光照模型,指的是通過光線跟蹤等技術,計算場景中所有包括反射、輻射等所有光線的照明結果的算法模型。雖然全局光照模型可以產生和真實照片媲美的渲染結果,但因爲其算法相對比較消耗時間,所以目前仍然無法應用於即時渲染領域(即時運算的3D應用程序,至少要每秒產生30幀畫面)。所以全局光照模型現在只活躍於用來產生照片質量動畫的“離線”渲染工具中,比如Pixar’s RenderMan(http://www.pixar.com),Mental ray(http://www.mentalp_w_picpaths.com,包括3D Studio Max 等商業軟件所使用的工具),POV-Ray(http://www.povray.org,一個開源的光線跟蹤渲染工具)以及Aqsis(一個免費開源的符合RenderMan標準的離線渲染器)中。

  材質的101[1]報告

  Ogre是一個硬件加速的事實3D渲染引擎。換句話說,它儘可能的使用硬件(GPU)來處理更多的渲染過程。而對於硬件來說,不論多麼複雜的材質,最終渲染到屏幕的結果只有一個類型,那就是顏色。而Ogre作爲一個典型的渲染引擎,把物體上的顏色分解成四種不同的光照作用:環境反射(Ambient)、漫反射(Diffuse)、放射(Emissive)以及鏡面反射(Specular)。

  物體着色的基礎

  在前一個章節中,我們介紹了Ogre中光源的類型。而在Ogre所採用的局部輻射度模型的着色算法中,對物體顏色產生影響的除了光源本身的屬性之外,還有觀察的角度以及物體本身的顏色(在實際處理中等同於的是攝像機的角度和物體所用材質屬性)。

  下面列舉了所有四種影響最終顏色的材質屬性,以及它們的含義。

  ·環境反射:近似的模擬了場景中的全局輻射;也就是用來近似模擬所有光在場景中不斷散射的結果。材質中有相應的屬性來代表這種環境反射顏色。

  ·漫反射:這種顏色是接收到直接從光源發射的光之後產生的,“漫反射”這個詞來源於現實世界,描述光被物體反射到各個方向的效果(換句話說就是散射)。

  ·放射:指的是自發光物體所擁有的顏色。這裏有一個有趣的話題,因爲在局部輻射光照模型中,放射光只能照亮自己卻不能對周圍任何物體產生影響,有時候這種效果卻可以讓人覺得極其虛幻(設想一下,在你的房間裏有一個灼熱的物體,卻不會照亮周圍任何東西,你就會了解這種感覺了)。

  ·鏡面反射:描述了物體對被光照後的“高光”效果。設想一下假如你有一個紅色皮球,而且擦得光亮。現在把它放在一個高瓦度的燈泡下面,你就會在上面看到一個被稱爲鏡面高光的亮斑。這是因爲光線被光滑的表面直接反射到你的眼睛中的緣故。

  在這裏鏡面反射的高光的顏色是可以被用戶定義的。這是因爲在現實世界中,和環境光、漫反射以及輻射光不同,鏡面反射高光的顏色除了材質自身和光的顏色之外也受到周圍其他的一些因素的影響。所以這裏允許通過手動調整,使用較低的代價來達到對真實世界的簡單模擬。另外我們也可以手動調整鏡面高光的反射能量,簡單的說就是可以用參數來控制紅色皮球上“光斑”的大小,同時調節物體發亮的程度:能量大的時候,光斑變得又小又亮.

  3DMax中“高光級別”參數等同於Ogre中“specular power(鏡面能量)”的概念,另外“光澤度”等同與Ogre中“shininess specular(鏡面亮點)”參數。鏡面能量決定能反射回來光的總量(表現爲曲線的高度),另外的鏡面亮度定義了高光如何被“展開”(越高的數值曲線越光滑)。

  這些基礎的着色參數在Ogre中是總是可用的,不論你的硬件是否支持幾何轉換和光照處理(T&L),這些參數都是硬件加速所需要最基本的數據。如果在材質中提供了多種技術方案(Technique在我們後面的“技術和方案”章節中會具體介紹)以供選擇的時候,至少對於其中使用固定功能着色(fixed-function shading)管線的來說這些參數仍然是必要的。

  紋理貼圖(Texture Mapping)

  大部分的圖形硬件都會至少支持一兩個紋理處理單元(Texture units)。這些單元允許你的程序把紋理(Texture)通過一個獨立的座標系映射到一個物體上。這裏的紋理不僅僅限於“2D圖片”的概念,也可以有MIP-map 細節度的數據、立方體(Cube)紋理或者體積紋理(Volume)等等。在構造紋理的時候,可以使用的2D圖片包括GIF,PNG或者網頁上面使用的JPG圖片格式,這些圖片通常可以從屏幕截圖或者數碼相機以及拍照手機中得到。簡單的說,使用了紋理貼圖,就等於用一個2D圖片包裹了3D物體,進而和物體表面的漫反射項進行混合(或者替代)來產生最終的顏色效果。

  提示:紋理貼圖的製造通常可以被分成兩個步驟。首先是藝術部分:美工使用3D模型工具來調整的紋理座標(一般被稱爲UV座標系)到模型的頂點上,進而產生一系列UV座標。然後是程序部分:這些UV座標被傳遞到圖形硬件上把模型頂點和紋理座標關聯起來。

  前面提到的一些技術,除了圖形硬件上的幾何轉換和光照處理(T&L),我們還提到了固定功能管道(fixed-function pipeline),這裏面所謂的“固定(fixed)”的意思就是不可以編程的;你告知硬件物體的頂點和圖片上像素的對應關係,然後就交給硬件就進行下面的工作:把物體的頂點信息映射到世界空間中去,然後結合紋理光柵化頂點的數據,最後繪製到屏幕上。

  可編程着色技術(Programmable Shading)

  可編程圖形管線(programmable graphics pipeline)的出現,是到目前爲止實時渲染3D圖形加速技術中最大的飛躍。這意味着,簡單的對每一頂點(頂點着色)和對每一像素(像素着色或者稱爲片斷着色)的運算可以放到程序片段中來進行,這些程序既可以使用高級語言也可以通過GPU的彙編語言來實現,這樣就能用我們所寫的程序來取代之前固定功能管道所做的事情。注意,我在這裏用到了“能取代”這個詞,而不是“必須取代”。你仍然可以堅持使用固定功能管道,即便是在今天GEFORCE 7950 GTX2(在我寫這文章時候市場上最強的圖形卡)上面。如果決定了採用可編程着色語言,那麼你就可以在渲染通路重處理任何頂點以及它們之間的片斷數據。紋理在這個時候成爲了程序的輸入數據,需要說明一下,這裏的輸入數據可以是任何你需要的東西。因爲如果你足夠聰明,GPU對你而言只不過是一個高速的並行計算引擎而以,你甚至可以用它完成你任何希望的計算工作(比如人工智能的計算,在遊戲編程精粹4中有相應的文章)。

  Ogre支持所有風格的GPU着色語言,其中包括低級的彙編和諸如Cg,GLSL和HLSL這種高級語言。然而“支持”這個詞同時意味着Ogre並沒有爲你做什麼具體的事情;你仍然要靠自己雙手去寫這些着色程序,不過還好Ogre提供了一個靈活並且可變的框架來幫助你在GPU上進行着色工作。你會在之後的“材質和可編程管線”章節中瞭解這個框架的具體信息。

  材質和程序設計

  在上一個章中,我們介紹了Ogre對物體中的定義的實體和幾何模型之間的關係。同時我們也知道需要通過材質來讓它們看起來更像現實世界中的樣子。在更進一步瞭解Ogre材質機理之前,我們還要了解一些更高級的編程概念。

  批次(Batching)

  Ogre最基本的渲染單元被稱爲可渲染對象(Renderable),它們通過不同的渲染狀態被分類傳遞到渲染隊列中去。相同渲染狀態的可渲染對象只需要一次的繪圖操作,也就是說它們在一個繪圖批次中完成了渲染過程。當渲染到不同的材質或者模型的時候都會導致渲染狀態的改變,而每次改變都會引起新的繪圖操作(新的繪圖批次)。繪圖操作都是一個耗費時間的過程,如同畫家在繪製新作品前要清洗調色板一樣,圖形硬件也需要進行頂點和紋理的更新操作。爲了渲染效率的提高,程序設計者要儘量減少渲染狀態的改變,換句話說也就是減少繪圖批次的數量。

  注意:通常而言,在你設定了渲染場景之後,所有的紋理都會被一次性的載入顯存,除非你的紋理佔用了過多的顯存,否則不會在每一幀都傳遞紋理數據到顯存中去。因此,你需要儘量的謹慎的處理紋理的使用:一旦確定紋理已經在顯存,在它失效之前就不要再嘗試去重載它。

  Ogre引擎在力所能及的範圍內儘量把相同渲染狀態的物體一起渲染,進而減少渲染狀態改變。然而即便如此,Ogre仍然尊重用戶設置的Renderable整體(也就是說Renderable是處理批次的最小原子結構),並不進行拆分。舉例來說,如果你有一個擁有20個使用相同材質的片段組成的模型,你應該把它們合併成一個Renderable對象而不分割成二十個不同的小對象批次:這樣就可以省略掉系統檢查這些片斷是否是一個相同的渲染狀態的步驟,從而可以一次處理完畢。在渲染的每一幀中,都會有一個明確的可以處理批次數量的上限,但是對這方面的討論超出了本書所介紹的範圍。如果你希望得到更多的信息,可以去查閱NVIDIA的工程師Matthias Wloka在CGD(遊戲開發者大會)上發表的相關論文[1]。在這本書的後面,你會學到如何在代碼中控制渲染狀態的改變。爲了達到以上的目的,你可能會需要使用3D模型工具來修改你所用的模型。

  材質克隆

  在Ogre中材質是被所有引用所共享的。當你從材質管理器重得到了一個指向材質的指針的時候,其他的使用相同材質對象也在處理一樣的指針。這意味着如果你希望改變某個物體上面材質的任何屬性的話,你都要把這個材質爲它單獨克隆一份,否則這個改變就會同時影響到所有使用相同材質的物體。在渲染通道中都需要再產生一個完全不同的材質。不過這裏也有一種可以替代的方法存在,把材質作爲顏色數據,通過改變可編程GPU着色的參數來實現不同材質的效果。這樣就可以避免產生一個新的批次。

  GPU着色

  GPU(圖形處理器)在設計之初就有着明確的目標,提供高性能的並行向量計算以供針對3D圖形處理使用。而具體如何使用和怎麼使用的權力都交給了用戶。GPU的存在是爲了減少CPU(中央處理器)的工作壓力,使用者負責分配具體的計算工作給它們。需要指出的是,GPU在其專署領域的運算速率大大的高於CPU,而且似乎在未來一個時期內這個差距仍然會增加。因此,你需要有計劃的開始在你設計中的管線加入可編程着色技術,尤其是當你將要計劃使用實時陰影技術或者更高級的渲染到紋理(Render-to-Texture)技術的時候更需要這方面的支援。

  技術(Technique) 和方案(Scheme)

  可以說技術和方案是Ogre引擎材質中最強大和活躍的兩個特性。在Ogre中,每個材質中都至少包含了一種“技術”實現,這種實現允許你對不同性能顯示卡和硬件平臺使用不同的材質屬性組合。簡而言之,技術就是“一種對物體的渲染方法”。通常來說對具體適用哪個渲染技術是由Ogre引擎自動甄選出來的(根據硬件性能、方案以及細節等級等信息),但是如果你希望的話也可以在代碼中完全控制這個過程。

  “方案”是Ogre使用的高級話題之一,事實上它是一個渲染技術集合的描述。舉例來說,你可能有三個不同的技術方案:高質量,中等質量,低質量。在遊戲運行的時候,允許用戶通過選擇這三個方案中的任意一個來確定在遊戲中具體使用的渲染技術集合。

  Ogre在渲染的時候,會有一個自動甄選所需渲染技術的固定流程:首先過濾掉那些不在當前方案中的所有技術(默認情況下當前方案是“Default”);然後選擇適配當前細節等級(LoD)的那些;最後在剩下的當中挑選當前硬件環境中可以執行的最優技術(最好效果的)。當Ogre找不到任何一個可以使用的渲染技術時,就會把物體渲染成單調的白色表面。換句話說,如果你看到了一片雪白,就要檢討一下你對材質的配置了。另外在默認的情況下,材質中所有技術的細節等級(LoD)都被設置成爲0,也就是最高的細節等級。換句話說,Ogre總是在儘可能的幫助你選擇最優材質技術。

  似乎技術和方案會帶來很多複雜的處理細節。但在實際的執行過程中,你只要在材質腳本中提供了充足的內容,Ogre就會接替你來管理這些瑣碎的細節。當然如果你喜歡,也可以用代碼完成腳本所進行的工作。

  材質細節等級(Material LoD)

  計算機圖形學中的細節等級(Level of detail)這個術語經常是用來描述幾何體複雜度等級和攝像機距離的關係。同樣的,這種描述也可以類似的用在材質上。例如說,你可以通過腳本把當前材質定義成多層紋理(用不同的方法來混合它們),並且同時擁有頂點和片斷兩種GPU着色程序。就算在一個很近的距離,也能在你的模型上面表現出“無與倫比”的漂亮材質細節。然而,當這個模型在屏幕上縮小到12像素的時候(就是說距離變遠了),你認爲這樣做還值得麼?

  答案明顯是不!解決這個問題的方法就是使用材質細節等級(LoD)管理。繼續我們上面的例子,好地解決方案是利用Ogre可以讓你在不同的細節等級中使用不同的技術實現,進而更有效的利用當前GPU的資源。爲了實現這個效果,你需要首先定義各個細節等級所對應的實際距離,然後把相應的技術索引到這些等級中去。這裏鼓勵你在每個細節等級中儘可能多的設置不同的技術實現,以供給不同的方案以及硬件選擇出最適用的實現。

  材質的組成

  在下圖6-2中,展示了Ogre的材質之中各種組成成分之間的關係。一份完整的材質至少有一種技術實現,每種技術實現中至少要有一個渲染通路。在圖示中,我們看到材質中包含了N種的技術實現,而在真正的渲染時,只會有一種技術被激活並進入渲染過程(選擇激活技術的工作一般交給Ogre自動完成)。如果我們激活了技術0,那麼技術0中所有通路都會被傳到圖形硬件中去依次渲染(換句話說,Ogre渲染了激活技術中的所有通路)。

  通路(Pass)

  在Ogre中通路是最基本的渲染單位,同時也是可渲染對象(Renderable)用來標示自己渲染狀態的基本單元。每個可渲染對象都會有自己的材質,Ogre在材質中甄選出最適合當前應用的技術實現。然後把當前技術中所有的“通路”依次放入圖形硬件的渲染通路中。顧名思義,Ogre材質中的“通路”對應於圖形硬件中“渲染通路”的概念。也就是說當前技術中如果包含了3個通路,那麼在繪製是用這個材質的模型的時候,在每一幀就要進行3次渲染。

  在實際的使用中,通路里面還有“紋理單元(texture unit)”的定義,你可以在一個通路中定義任意數量的紋理單元,當然一個不用也是沒問題的。

  紋理單元(Texture Unit)

  在Ogre對材質的定義中,紋理單元的概念對應於圖形硬件中的紋理採樣(texture sampler)。爲了運行Ogre程序,至少需要一個硬件紋理採樣支持。不過這並不是什麼大問題,因爲現代的圖形硬件基本上都會有多個紋理採樣,因此我們可以在一次渲染通路的執行中,同時處理多個紋理單元。

  顧名思義,紋理單元裏都會包含一張紋理。你可以直接用硬盤中的圖片文件,也可以通過實時的渲染來得到,甚至可以通過一個視頻流來動態生成紋理圖案。在Ogre中並沒有對通路中紋理單元的數量進行限制,這是因爲Ogre能根據圖形硬件能力動態拆分通路(這裏假設沒有使用硬件着色程序)。具體點說,如果你的圖形硬件只能同時處理4個紋理採樣,但是應用程序卻使用了一個6紋理單元的通路。這時候Ogre會自動的把這個6紋理通路拆分成兩個分別兩次進行渲染,不過雖然最後的渲染結果和預期的一樣,但是仍然是通過兩次渲染通路來實現的,對效率的影響不言自明。

  最後需要注意的是,紋理在被真正的拋棄之前,都會一直存在顯存中,而並不是每幀從內存傳遞到顯存。但是如果紋理數量太多或者體積過大的時候,硬件無法同時處理所有紋理,效率會嚴重降低。

  紋理壓縮

  很多現代圖形硬件都會支持壓縮紋理(比如DXTC方案),不過Ogre只是簡單的載入紋理然後把它傳到圖形硬件上,並沒有執行紋理的任何壓縮過程。如果需要紋理壓縮的支持,就需要離線進行圖片的壓縮工作(多數情況是把圖片轉換成壓縮的DDS格式),然後把這些預壓縮的紋理交給程序使用。如果你的硬件不支持壓縮圖片格式,Ogre會幫助你在程序運行的時候進行解壓縮,然後再交給硬件使用。

  視頻流

  雖然Ogre並沒有在紋理單元中內置對視頻流的支持,不過因爲紋理單元可以支持外部數據源的處理,所以通過這個這個簡單的機理可以幫助實現對視頻流的支持。Ogre社區已經提供了一個針對Theora(http://www.theora.org)這個視頻流處理器爲基礎的插件。通過這個插件你可以把任何你所喜歡的流加入到你的Ogre程序中,甚至可以直接從實況電視轉播中得到視頻源:任何你能想象得到的應用都能幫你實現。

  實體(Entity)

  實體是Ogre中比較神奇和複雜的概念之一。在每個實體中都包含着一些子實體(SubEntity)的實現,這些子實體是真正的可渲染對象,它們維護着具體的材質特性。而每個子實體又和一個子模型(SubMesh)對應(通常來源於3D模型工具建立的模型)。概括的來說,實體和子實體是物體渲染特性的入口,而模型(Mesh)與子模型是物體結構特性(幾何體數據)的入口。

  材質的例子

  爲了便於大家更好的理解,我們會通過講解一些簡單的例子來繼續這個章節。這裏大多是從Ogre的演示程序中找到的例子,不過也有我寫的的幾個材質腳本的最簡單實現。

  材質與固定功能管道(Fixed-Function Pipeline)

  代碼6-1展示了一個最簡單的材質腳本,裏面包含一個最簡單的技術實現,技術實現裏只有一個通路。如果你用它對你的物體進行渲染,會得到一個灰色的結果。

  代碼6-1:宇宙中最簡單的Ogre材質

  material VerySimple

  {

  technique

  {

  pass

  {

  diffuse 0.5 0.5 0.5

  }

  }

  }

  上面代碼6-1中定義的材質,並沒有什麼特殊漂亮的地方,但是對於我們用來介紹Ogre材質腳本層次和結構來說已經足夠了。通過它你可以直觀地瞭解到腳本內部的嵌套關係:材質包含了技術,而技術包含了通路。通路中定義了漫反射參數。

  現在讓我們看一個更復雜一點的例子,在下面代碼6-2中,我們在材質中建立了兩個技術,而且提供了更多的固定功能着色(Fixed-function shading)的控制。

  代碼 6-2:對代碼6-1的簡單擴充

  material NotQuiteAsSimple

  {

  technique

  {

  pass

  {

  diffuse 0.5 0.5 0.5

  ambient 0.1 0.2 0.3

  specular 0.8 0.8 0.8 68

  texture_unit

  {

  texture ReallyCool.jpg

  colour_op modulate

  }

  }

  }

  technique

  {

  pass

  {

  diffuse 0.5 0.5 0.5

  }

  }

  }

  代碼6-2已經很接近在實際中使用的材質定義了。在其中第一個技術的通路里面使用了texture_unit標記來定義所使用的紋理貼圖,這裏假設有一張叫做ReallyCool.jpg的圖片。在這個定義裏面有趣的部分是定義如何把紋理的像素點混合到已經存在的顏色上:colour_op modulate這段腳本讓Ogre把紋理上面的顏色數據和當前像素上的顏色數據(之前顏色混合的結果)相乘。在我們的例子中,所謂的當前顏色數據,其實指的就是程序中對局部光照模型混合的結果,具體點來說就是在這個相同的通路中對環境光,漫反射以及鏡面反射採樣的計算結果。

  代碼6-2也是我們第一次在材質腳本中加入一個用來“墊底”的技術實現。如果程序不幸的運行在一個不能支持紋理數據的硬件環境上的時候,第二個備用的技術實現就會被系統拿出來工作,把你的模型渲染成單一的灰色,雖然不怎麼好看,但至少保證了程序安全運行。因爲我們定義的兩個技術都被系統默認的設置成相同的“方案”(默認的“Default”方案)以及相同的LoD(細節等級:0),所以的這個“墊底”才能良好的被運行。

  注意:在你造成上面的問題之前,可能還有更嚴重的問題會出現。如果你的硬件真的很“老”,以至於無法支持紋理貼圖屬性,這時候甚至基本的3D API都不能運行(不論是DirectX還是OpenGL),進而導致Ogre不能啓動。雖然Ogre是個強大的引擎,但是不能幫你升級硬件。

  代碼6-3是一個真正在Ogre例子裏面使用的材質腳本——Example.material。你在Ogre目錄下面的Samples\Media\materials\scripts 子目錄下面可以找到這個文件。

  代碼6-3:一個定義紋理貼圖的材質腳本

  material Examples/EnvMappedRustySteel

  {

  technique

  {

  pass

  {

  texture_unit Diffuse

  {

  texture RustySteel.jpg

  }

  texture_unit Environment

  {

  texture spheremap.png

  colour_op_ex add src_texture src_current

  colour_op_multipass_fallback one one

  env_map spherical

  }

  }

  }

  }

  上面代碼和之前最大的不同就是增加了材質的命名:Examples/EnvMappedRustySteel。雖然看起來似乎好像存在着一個目錄結構,但事實上並不真的存在任何層級關係,這樣命名只不過是一個爲了更方便處理的習慣罷了:Ogre系統只是簡單的認爲,在material標籤右面相同行的所有字母都是這個材質的名字。這個名字在整個程序中必是唯一的。因爲在Ogre中對於材質的名稱並沒有一個類似“命名空間”的概念:就算在不同的資源載入的材質,它們的名字也是同時存在一個空間的。如果你真的希望可以用一樣的名字命名不同的材質,就最好遵從我們上面提到的習慣,用類似路徑名的方法命名材質,其中路徑就可以替代命名空間的實現。

  Ogre會給所有材質之中的元素(包括技術,通路以及紋理單元)提供一個默認的名字。還是用我們之前的腳本作爲例子,在材質Examples/EnvMappedRustySteel中,裏面的唯一的技術實現被命名爲“0”,而通路因爲一樣的原因得到了“0”這個名字。兩個紋理單元分別被命名爲“0”和“1”。這個名稱是和他在材質腳本中的順序相匹配的。當你要通過“繼承”的方式擴充腳本的時候(後面回講到具體細節),有必要給這些元素手動命名,以便實現擴充功能的作用。相對於材質本身,Ogre並沒有要求這些元素名稱具有全局唯一性(當然,在它們自己的容器中,還是需要有不同的名字來辨別的)。

  在代碼6-3中,第二個紋理單元被指定成爲一個球體環境貼圖(通過env_map標記來實現)。環境貼圖是一種用很低的代價實現的模擬反射表面的效果,其中並沒有真的使用光線跟蹤算法。再加上前面使用的“生鏽的鋼鐵”紋理,最後混合上環境貼圖的效果。最後能產生什麼樣子呢?那就要參考下面的幾幅截圖和貼圖來了解了。

  材質的繼承(Material Inheritance)

  Ogre提供了體面的紋理繼承機制,可以幫助我們更簡單的改變紋理的某些部分。比如在前面我們定義了材質腳本Examples/EnvMappedRustySteel,如果我們需要換成壓縮紋理。這時候既不用在原腳本上修改,也不用重新寫另外一個。通過材質腳本的繼承機制,我們可從原有的腳本中派生出新的紋理出來。

  不過在Ogre的代碼和文檔中把這種行爲稱爲材質拷貝(material copying),這種稱爲揭示了“材質繼承”與面向對象中的繼承概念的區別。Ogre並沒有對材質腳本實現面向對象的繼承關係;也就是說在代碼中Ogre系統名沒有真正的使用原是材質進行繼承,而只不過是簡單的對原始腳本的屬性進行了拷貝,其結果是:改變父腳本的屬性不會影響子腳本。

  雖然不怎麼地道,但是確實是如繼承一樣方便的方法。你可以建立一個新的材質,把已經存在的材質作爲基礎,拷貝所有舊材質的屬性到新材質裏面,並對其中的屬性做輕微的調整。這樣做既可以減少工作量也可以讓腳本看起來更清晰簡單。

  命名的重要性

  雖然大多數情況可以依賴系統對材質中成員的默認名稱。但如果你使用了繼承,並且準備在新的材質中增加技術,通道或者新的紋理單元。這時候就要考慮手動對程序進行命名了,這是因爲需要明確的區分父材質和子材質(也就是原材質腳本和目標材質腳本)中各元素的名稱。如果你在之前的材質中使用了稱爲“Primary”的技術,在擴充的拷貝材質中就不能增加一個被稱爲“Primary”的技術,這是因爲相同的名稱會覆蓋原材質的屬性。

  紋理覆蓋(Texture Aliases)

  在很多情況下,派生材質的目的可能只是需要改變已有腳本中的紋理圖片。這時候可以通過簡單的紋理覆蓋機制來實現。下面的代碼片斷展示瞭如何通過紋理覆蓋機制把父材質中的圖片換成相應的壓縮格式。

  material Examples/EnvMappedCompressedRustySteel : Examples/EnvMappedRustySteel

  {

  set_texture_alias Diffuse rustySteel.dds

  set_texture_alias Environment aphereMap.dds

  }

  上面代碼實現了我們需要的功能,現在Examples/EnvMappedCompressedRustySteel的使用的紋理已經不同於Examples/EnvMappedRustySteel,其他屬性仍然繼承了下來。一目瞭然,這種方法比麻煩的拷貝粘貼方法要好上很多。

  我們再來看一個稍微特別一點的材質實現,下面的材質腳本是Ogre演示程序中真實使用的例子。

  代碼6-4:在Sampls/Media/materials/scripts/Example.material中使用的材質紋理效果

  material Examples/TextureEffect4

  {

  technique

  {

  pass

  {

  ambient 0.3 0.3 0.3

  scene_blend colour_blend

  cull_hardware none

  cull_software none

  texture_unit

  {

  texture Water02.jpg

  scrool_anim 0.01 0.01

  }

  }

  }

  }

  在默認的情況下,Ogre會使用硬件揀選來過濾掉逆時針的表面(也就是說圖形硬件只渲染那些邊是面向攝像機順時針排列的三角形)。而有一些場景管理器只會渲染那些法線面向攝像機的三角形。當我們希望平面的兩個面在場景中都可見的時候,就需要關掉這兩種揀選方式,通過腳本中所用的cull_hardware none和cull_software none兩行來實現。

  在圖6-6中我們可以看到背景的藍天白雲透過半透明的材質被展現出來。這是材質和場景之中已經有的顏色進行混合的結果;因爲紋理圖片本身並沒有透明效果(也就是沒有自己的Alpha通道),所以使用了:scene_blend colour_blend來實現了簡單的混合效果。如果使用了有Alpha通道的透明效果紋理,則可以使用:scene_blend alpha_blend來得到更好的效果。

  材質和可編程渲染管線

  在之前所提及的所有材質都使用了固定的渲染管線。而現在我們要接觸更復雜一些的可編程渲染管線。雖然之前所有的腳本也都可以在可編程的管線中正常使用,但是當我們調用了着色程序之後,其中大部分的設置都會被忽略掉。

  在進行Ogre材質的GPU程序設計的時候有三件事情需要關心:程序本身、程序聲明以及在材質中的使用。在這裏首先讓我們看一個簡單一些GPU程序產生材質的例子:一個在Ogre的某個Demo程序中使用的硬鐵皮表面材質。

  代碼6-5:在文件Samples/Media/materials/programs/Example.cg中的Cg程序

  void hardwareSkinningOneWeight_vp(

  float4 position : POSITION,

  float3 normal : NORMAL,

  float2 uv : TEXCOORD0,

  float blendIdx : BLENDINDICES,

  out float4 oPosition : POSITION,

  out float2 oUv : TEXCOORD0,

  out float4 colour : COLOR,

  // Support up to 24 bones of float3x4

  // vs_1_1 only supports 96 params so more than this is not feasible

  uniform float3x4 worldMatrix3x4Array[24],

  uniform float4x4 viewProjectionMatrix,

  uniform float4 lightPos[2],

  uniform float4 lightDiffuseColour[2],

  uniform float4 ambient)

  {

  // transform by indexed matrix

  float4 blendPos = float4(mul(worldMatrix3x4Array[blendIdx], position).xyz, 1.0);

  // view / projection

  oPosition = mul(viewProjectionMatrix, blendPos);

  // transform normal

  float3 norm = mul((float3x3)worldMatrix3x4.Ogre 3D程序設計 Ogre材質1 .Array[blendIdx], normal);

  // Lighting - support point and directional

  float3 lightDir0 = normalize(

  lightPos[0].xyz - (blendPos.xyz * lightPos[0].w));

  float3 lightDir1 = normalize(

  lightPos[1].xyz - (blendPos.xyz * lightPos[1].w));

  oUv = uv;

  colour = ambient +

  (saturate(dot(lightDir0, norm)) * lightDiffuseColour[0]) +

  (saturate(dot(lightDir1, norm)) * lightDiffuseColour[1]);

  }

  就算你不瞭解6-5這個程序具體是什麼意思也不要緊,現在你只要簡單的注意一下用黑體字標明的函數名稱就好了。另外,對於儲存GPU程序的文件可以任意命名,如果你喜歡甚至可以改變擴展名。一會兒你就能瞭解到,這個文件的名字對於Ogre系統來說沒有任何意義。

  注意:在這本書裏面並沒有要講解GPU程序設計的計劃。這裏所列出的所有GPU程序代碼都只是爲了作爲實際的例子,如果你不理解這些代碼的意思又對其有興趣,就需要找一些資料來學習一下相關的知識了。

  一旦你完成了上面的程序定義,你接下來需要做的工作是建立一個程序聲明(declaration)。這種聲名被用來給材質腳本中使用,下面代碼6-6就是對上面程序的聲明。

 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章