遊戲引擎全剖析(2)


第2部份: 3D環境的光照和紋理

 


世界的燈光
  在變換過程中, 通常是在稱爲觀察空間的座標空間中, 我們遇到了最重要的運算之一: 光照計算。 它是一種這樣的事情, 當它工作時,你不關注它,但當它不工作時, 你就非常關注它了。有很多不同的光照方法,從簡單的計算多邊形對於燈光的朝向,並根據燈光到多邊形的方向和距離加上燈光顏色的百分比值,一直到產生邊緣平滑的燈光貼圖疊加基本紋理。而且一些 API 實際上提供預先建造的光照方法。舉例來說,OpenGL 提供了每多邊形,每頂點,和每像素的光照計算。 

  在頂點光照中,你要決定一個頂點被多少個多邊形共享,並計算出共享該頂點的所有多邊形法向量的均值(稱爲法向量),並將該法向量賦頂點。一個給定多邊形的每個頂點會有不同的法向量,所以你需要漸變或插值多邊形頂點的光照顏色以便得到平滑的光照效果。 你沒有必要用這種光照方式查看每個單獨的多邊形。 這種方式的優點是時常可以使用硬件轉換與光照(T & L)來幫助快速完成。 不足之處是它不能產生陰影。 舉例來說,即使燈光是在模型的右側,左手臂應該在被身體投影的陰影中,而實際上模型的雙臂卻以同樣的方式被照明瞭。

  這些簡單的方法使用着色來達到它們的目標。 當用平面光照繪製一個多邊形時, 你讓渲染(繪製)引擎把整個多邊形都着上一種指定的顏色。這叫做平面着色光照。 (該方法中,多邊形均對應一個光強度,表面上所有點都用相同的強度值顯示,渲染繪製時得到一種平面效果,多邊形的邊緣不能精確的顯示出來) 。

  對於頂點着色 ( Gouraud 着色) ,你讓渲染引擎給每個頂點賦予特定的顏色。 在繪製多邊形上各點投影所對應的像素時,根據它們與各頂點的距離,對這些頂點的顏色進行插值計算。 (實際上Quake III 模型使用的就是這種方法, 效果好的令人驚奇)。 

  還有就是 Phong 着色。如同 Gouraud 着色,通過紋理工作,但不對每個頂點顏色進行插值決定像素顏色值, 它對每個頂點的法向量進行插值,會爲每個頂點投影的像素做相同的工作。對於 Gouraud 着色,你需要知道哪些光投射在每個頂點上。對於 Phong 着色,你對每個像素也要知道這麼多。 

  一點也不令人驚訝, Phong 着色可以得到更加平滑的效果,因爲每個像素都需要進行光照計算,其繪製非常耗費時間。平面光照處理方法很快速, 但比較粗糙。Phong 着色比 Gouraud 着色計算更昂貴,但效果最好,可以達到鏡面高光效果("高亮")。 這些都需要你在遊戲開發中折衷權衡。


不同的燈光
  接着是生成照明映射,你用第二個紋理映射(照明映射)與已有的紋理混合來產生照明效果。這樣工作得很好, 但這本質上是在渲染之前預先生成的一種罐裝效果。如果你使用動態照明 (即,燈光移動, 或者沒有程序的干預而打開和關閉),你得必須在每一幀重新生成照明映射,按照動態燈光的運動方式修改這些照明映射。燈光映射能夠快速的渲染,但對存儲這些燈光紋理所需的內存消耗非常昂貴。你可以使用一些壓縮技巧使它們佔用較少的的內存空間,或減少其尺寸大小, 甚至使它們是單色的 (這樣做就不會有彩色燈光了),等等。 如果你確實在場景中有多個動態燈光, 重新生成照明映射將以昂貴的CPU週期而告終。 

  許多遊戲通常使用某種混合照明方式。 以Quake III爲例,場景使用照明映射, 動畫模型使用頂點照明。 預先處理的燈光不會對動畫模型產生正確的效果 -- 整個多邊形模型得到燈光的全部光照值 -- 而動態照明將被用來產生正確的效果。 使用混合照明方式是多數的人們沒有注意到的一個折衷,它通常讓效果看起來"正確"。 這就是遊戲的全部 – 做一切必要的工作讓效果看起來"正確",但不必真的是正確的。 

  當然,所有這些在新的Doom引擎裏面都不復存在了,但要看到所有的效果,至少需要 1GHZ CPU 和 GeForce 2 顯卡。是進步了,但一切都是有代價的。 

  一旦場景經過轉換和照明, 我們就進行裁剪運算。 不進入血淋淋的細節而,剪斷運算決定哪些三角形完全在場景 (被稱爲觀察平截頭體) 之內或部份地在場景之內。完全在場景之內的三角形被稱爲細節接受,它們被處理。對於只是部分在場景之內的三角形, 位於平截頭體外面的部分將被裁剪掉,餘下位於平截頭體內部的多邊形部分將需要重新閉合,以便其完全位於可見場景之內。 (更多的細節請參考我們的 3D 流水線指導一文)。

  場景經過裁剪以後,流水線中的下一個階段就是三角形生成階段(也叫做掃描 線轉換),場景被映射到2D 屏幕座標。到這裏,就是渲染(繪製)運算了。


紋理與MIP映射
  紋理在使3D場景看起來真實方面異常重要,它們是你應用到場景區域或對象的一些分解成多邊形的小圖片。多重紋理耗費大量的內存,有不同的技術來幫助管理它們的尺寸大小。紋理壓縮是在保持圖片信息的情況下,讓紋理數據更小的一種方法。紋理壓縮佔用較少的遊戲CD空間,更重要的是,佔用較少內存和3D 顯卡存儲空間。另外,在你第一次要求顯卡顯示紋理的時候,壓縮的(較小的) 版本經過 AGP 接口從 PC 主存送到3D 顯卡, 會更快一些。紋理壓縮是件好事情。 在下面我們將會更多的討論紋理壓縮。 


MIP 映射(多紋理映射)
  遊戲引擎用來減少紋理內存和帶寬需求的另外一個技術就是 MIP 映射。 MIP 映射技術通過預先處理紋理,產生它的多個拷貝紋理,每個相繼的拷貝是上一個拷貝的一半大小。爲什麼要這樣做?要回答這個問題,你需要了解 3D 顯卡是如何顯示紋理的。最壞情況,你選擇一個紋理,貼到一個多邊形上,然後輸出到屏幕。我們說這是一對一的關係,最初紋理映射圖的一個紋素 (紋理元素) 對應到紋理映射對象多邊形的一個像素。如果你顯示的多邊形被縮小一半,紋理的紋素就每間隔一個被顯示。這樣通常沒有什麼問題 -- 但在某些情況下會導致一些視覺上的怪異現象。讓我們看看磚塊牆壁。 假設最初的紋理是一面磚牆,有許多磚塊,磚塊之間的泥漿寬度只有一個像素。如果你把多邊形縮小一半, 紋素只是每間隔一個被應用,這時候,所有的泥漿會突然消失,因爲它們被縮掉了。你只會看到一些奇怪的圖像。 

  使用 MIP 映射,你可以在顯示卡應用紋理之前,自己縮放圖像,因爲可以預先處理紋理,你做得更好一些,讓泥漿不被縮掉。當 3D 顯卡用紋理繪製多邊形時,它檢測到縮放因子,說,"你知道,我要使用小一些的紋理,而不是縮小最大的紋理,這樣看起來會更好一些。" 在這裏, MIP 映射爲了一切,一切也爲了 MIP 映射。


多重紋理與凹凸映射
  單一紋理映射給整個3D 真實感圖形帶來很大的不同, 但使用多重紋理甚至可以達到一些更加令人難忘的效果。過去這一直需要多遍渲染(繪製),嚴重影響了像素填充率。 但許多具有多流水線的3D 加速卡,如ATI's Radeon 和 nVidia's GeForce 2及更高級的顯卡,多重紋理可以在一遍渲染(繪製)過程中完成。 產生多重紋理效果時, 你先用一個紋理繪製多邊形,然後再用另外一個紋理透明地繪製在多邊形上面。這讓你可以使紋理看上去在移動,或脈動, 甚至產生陰影效果 (我們在照明一節中描述過)。繪製第一個紋理映射,然後在上面繪製帶透明的全黑紋理,引起一種是所有的織法黑色的但是有一個透明分層堆積過它的頂端 , 這就是 -- 即時陰影。 該技術被稱爲照明映射 ( 有時也稱爲 暗映射),直至新的Doom ,一直是Id引擎裏關卡照明的傳統方法。 

  凹凸貼圖是最近涌現出來的一種古老技術。幾年以前 Matrox 第一個在流行的 3D 遊戲中發起使用各種不同形式的凹凸貼圖。就是生成紋理來表現燈光在表面的投射,表現表面的凹凸或表面的裂縫。 凹凸貼圖並不隨着燈光一起移動 -- 它被設計用來表現一個表面上的細小瑕疵,而不是大的凹凸。 比如說,在飛行模擬器中,你可以使用凹凸貼圖來產生像是隨機的地表細節,而不是重複地使用相同的紋理,看上去一點趣味也沒有。 

  凹凸貼圖產生相當明顯的表面細節,儘管是很高明的戲法,但嚴格意義上講,凹凸貼圖並不隨着你的觀察角度而變化。比較新的 ATI 和 nVidia 顯卡片能執行每像素運算,這種缺省觀察角度的不足就真的不再是有力而快速的法則了。 無論是哪一種方法, 到目前爲止,沒有遊戲開發者太多的使用; 更多的遊戲能夠且應該使用凹凸貼圖。


高速緩存抖動 = 糟糕的事物
  紋理高速緩存的管理遊戲引擎的速度至關重要。 和任何高速緩存一樣,緩存命中很好,而不命中將很糟糕。如果遇到紋理在圖形顯示卡內存被頻繁地換入換出的情況,這就是紋理高速緩存抖動。發生這種情況時,通常API將會廢棄每個紋理,結果是所有的紋理在下一幀將被重新加載,這非常耗時和浪費。對遊戲玩家來說,當API重新加載紋理高速緩存時,會導致幀速率遲鈍。

  在紋理高速緩存管理中,有各種不同的技術將紋理高速緩存抖動減到最少 – 這是確保任何 3D 遊戲引擎速度的一個決定性因素。 紋理管理是件好事情 – 這意味着只要求顯卡使用紋理一次,而不是重複使用。這聽起來有點自相矛盾,但效果是它意謂着對顯卡說,"看, 所有這些多邊形全部使用這一個紋理,我們能夠僅僅加載這個紋理一次而不是許多次嗎?" 這阻止API ( 或圖形驅動軟件) 上傳多次向顯卡加載紋理。象OpenGL這樣的API實際上通常處理紋理高速緩存管理,意謂着,根據一些規則,比如紋理存取的頻率,API決定哪些紋理儲存在顯卡上,哪些紋理存儲在主存。 真正的問題來了:a) 你時常無法知道API正在使用的準確規則。 b)你時常要求在一幀中繪製更多的紋理,以致超出了顯卡內存空間所能容納的紋理。 

  另外一種紋理高速緩存管理技術是我們早先討論的紋理壓縮。很象聲音波形文件被壓縮成 MP3 文件,儘管無法達到那樣的壓縮比率,但紋理可以被壓縮。 從聲音波形文件到MP3的壓縮可以達到 11:1的壓縮比率,而絕大多數硬件支持的紋理壓縮運算法則只有 4:1 的壓縮比率,儘管如此,這樣能產生很大的差別。 除此之外,在渲染(繪製)過程中,只有在需要時,硬件才動態地對紋理進行解壓縮。這一點非常棒,我們僅僅擦除即將可能用到的表面。

  如上所述,另外一種技術確保渲染器要求顯卡對每個紋理只繪製一次。確定你想要渲染(繪製)的使用相同紋理的所有多邊形同時送到顯卡,而不是一個模型在這裏,另一個模型在那裏,然後又回到最初的紋理論。僅僅繪製一次,你也就通過AGP接口傳送一次。Quake III 在其陰影系統就是這麼做的。處理多邊形時,把它們加入到一個內部的陰影列表,一旦所有的多邊形處理完畢,渲染器遍歷紋理列表,就將紋理及所有使用這些紋理的多邊形同時傳送出去。 

  上述過程在使用顯卡的硬件 T & L(如果支持的話)時,並不怎麼有效。你面臨的結局是,滿屏幕都是使用相同紋理的大量的多邊形小羣組,所有多邊形都使用不同的變換矩陣。這意謂着更多的時間花在建立顯卡的硬件 T & L 引擎 ,更多的時間被浪費了。 無論如何,因爲他們有助於對整個模型使用統一的紋理,所以它對實際屏幕上的模型可以有效地工作。但是因爲許多多邊形傾向使用相同的牆壁紋理,所以對於世界場景的渲染,它常常就是地獄。通常它沒有這麼嚴重,因爲大體而言,世界的紋理不會有那麼大,這樣一來API的紋理緩存系統將會替你處理這些,並把紋理保留在顯卡以備再次使用。 

  在遊戲機上,通常沒有紋理高速緩存系統(除非你寫一個)。在 PS2 上面,你最好是遠離"一次紋理" 的方法。在 Xbox 上面, 這是不重要的,因爲它本身沒有圖形內存(它是 UMA 體系結構),且所有的紋理無論如何始終保留在主存之中。 

  事實上,在今天的現代PC FPS 遊戲中,試圖通過AGP接口傳送大量紋理是第二個最通常的瓶頸。最大的瓶頸是實際幾何處理,它要使東西出現在它應該出現的地方。在如今的3D FPS 遊戲中,最耗費時間的工作,顯然是那些計算模型中每個頂點正確的世界位置的數學運算。如果你不把場景的紋理保持在預算之內,僅居其次的就是通過AGP接口傳送大量的紋理了。然而,你確實有能力影響這些。 通過降低頂層的 MIP 級別(還記得系統在哪裏不斷地爲你細分紋理嗎?), 你就能夠把系統正在嘗試送到顯卡的紋理大小減少一半。你的視覺質量會有所下降-- 尤其是在引人注目的電影片斷中--但是你的幀速率上升了。這種方式對網絡遊戲尤其有幫助。實際上,Soldier of Fortune II和Jedi Knight II: Outcast這兩款遊戲在設計時針對的顯卡還不是市場上的大衆主流顯卡。爲了以最大大小觀看他們的紋理,你的3D 顯卡至少需要有128MB的內存。這兩種產品在思想上都是給未來設計的。 

  上面就是第 2 部份。在下面章節中,我們將介紹許多主題,包括內存管理,霧效果,深度測試, 抗鋸齒,頂點着色,API等。
 

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