1. 程式人生 > >遊戲引擎剖析(一)

遊戲引擎剖析(一)




介紹
  自Doom遊戲時代以來我們已經走了很遠。 DOOM不只是一款偉大的遊戲,它同時也開創了一種新的遊戲程式設計模式: 遊戲 "引擎"。 這種模組化,可伸縮和擴充套件的設計觀念可以讓遊戲玩家和程式設計者深入到遊戲核心,用新的模型,場景和聲音創造新的遊戲, 或向已有的遊戲素材中新增新的東西。大量的新遊戲根據已經存在的遊戲引擎開發出來,而大多數都以ID公司的Quake引擎為基礎, 這些遊戲包括Counter  Strike, Team Fortress, Tac Ops, Strike Force, 以及Quake Soccer。Tac Ops 和Strike Force 都使用了Unreal Tournament 引擎。事實上, "遊戲引擎" 已經成為遊戲玩家之間交流的標準用語,但是究竟引擎止於何處,而遊戲又從哪裡開始呢?畫素的渲染,聲音的播放,怪物的思考以及遊戲事件的觸發,遊戲中所有這一切的幕後又是什麼呢? 如果你曾經思考過這些問題, 而且想要知道更多驅動遊戲進行的東西,那麼這篇文章正好可以告訴你這些。 本文分多個部分深入剖析了遊戲引擎的核心, 特別是Quake引擎,因為我最近工作的公司Raven Software已經在Quake引擎的基礎上開發出了多款遊戲,其中包括著名的Soldier of Fortune 。


開始

  讓我們首先來看看一個遊戲引擎和遊戲本身之間的主要區別。 許多人們會混淆遊戲引擎和整個遊戲 。這有點像把一個汽車發動機和整個汽車混淆起來一樣 。 你能夠從汽車裡面取出發動機, 建造另外一個外殼,再使用發動機一次。 遊戲也像那。 遊戲引擎被定義為所有的非遊戲特有的技術。 遊戲部份是被稱為 '資產' 的所有內容 (模型,動畫,聲音,人工智慧和物理學)和為了使遊戲執行或者控制如何執行而特別需要的程式程式碼, 比如說AI--人工智慧。

  對於曾經看過 Quake 遊戲結構的人來說, 遊戲引擎就是 Quake。exe ,而遊戲部分則是 QAGame。dll 和 CGame。dll 。 如果你不知道這是什麼意思, 也沒有什麼關係;在有人向我解釋它以前, 我也不知道是什麼意思。 但是你將會完全明白它的意思。 這篇遊戲引擎指導分為十一個部份。 是的, 從數量上來說,總共是十一個部份! 每個部分大概3000字左右。現在就從第一部分開始我們的探索吧,深入我們所玩遊戲的核心,在這裡我們將瞭解一些基本的東西, 為後面的章節作鋪墊。。。


渲染器

  讓我們從渲染器來開始遊戲引擎設計的探討吧, 我們將從遊戲開發者(本文作者的背景)的角度來探討這些問題。事實上,在本文的各個段落,我們將常常從遊戲開發者的角度探討, 也讓您像我們一樣思考問題!

  什麼是渲染器,為什麼它又這麼重要呢?好吧,如果沒有它,你將什麼也看不到。它讓遊戲場景視覺化,讓玩家/觀眾可以看見場景,從而讓玩家能夠根據螢幕上所看到的東西作出適當的決斷。 儘管我們下面的探討可能讓新手感到有些恐懼,先別去理會它。 渲染器做些什麼?為什麼它是必須的?我們將會解釋這些重要問題。

  當構造一個遊戲引擎的時候, 你通常想做的第一件事情就是建造渲染器。 因為如果看不見任何東西 – 那麼你又如何知道你的程式程式碼在工作呢? 超過 50% 的 CPU 處理時間花費在渲染器上面; 通常也是在這個部分,遊戲開發者將會受到最苛刻的評判。 如果我們在這個部分表現很差,事情將會變得非常糟糕, 我們的程式技術,我們的遊戲和我們的公司將在 10 天之內變成業界的笑話。 它也是我們最依賴於外部廠商和力量的地方,在這裡他們將處理最大限度的潛在操作目標。 如此說來, 建造一個渲染器確實不象聽起來那麼吸引人(事實如此), 但如果沒有一個好的渲染器, 遊戲或許永遠不會躋身於排行榜前10 名。

  如今,在螢幕上生成畫素,涉及到 3D 加速卡, API ,三維空間數學, 對 3D 硬體如何工作的理解等等。對於主機(遊戲機)遊戲來說,也需要相同型別的知識,但是至少對於主機, 你不必去嘗試擊中一個移動中的目標。 因為一臺主機的硬體配置是固定的 "時間快照", 和PC(個人計算機)不同, 在一臺主機的生命期中,它的硬體配置不會改變。

  在一般意義上,渲染器的工作就是要創造出遊戲的視覺閃光點,實際上達到這個目標需要大量的技巧。3D圖形本質上是用最少的努力創造出最大效果的一門藝術, 因為額外的 3D 處理在處理器時間和和記憶體頻寬方面都是極為昂貴的。 它也是一種預算, 要弄清楚你想在什麼地方花費處理器時間,而你寧願在什麼地方節省一些從而達到最好的整體效果。 接下來我們將會介紹一些這方面的工具,以及怎樣更好的用它們讓遊戲引擎工作。


建造3D世界

  最近,當我和一位從事計算機圖形方面工作長達數年之久的人會談時,她向我吐露道, 當她第一次看到實時操縱計算機 3D 圖象時, 她不知道這是怎麼實現的, 也不知道計算機如何能夠儲存 3D 圖象。 今天這對於在大街上的普通人來說或許是真實的,即使他們時常玩 PC 遊戲, 遊戲機遊戲, 或街機遊戲。

  下面我們將從遊戲設計者的角度討論創造 3D 世界的一些細節,你也應該看一看 Dave Salvator 所寫的“3D 管線導論“,以便對3D 圖象生成的主要過程有一個整體的瞭解。

  3D 物體(物件)被儲存成 3D 世界中的一系列點(被稱為頂點), 彼此之間有相互關係,所以計算機知道如何在世界中的這些點之間畫線或者是填充表面。 一個立方體由8個點組成,每個角一個點。立方體有6個表面, 分別代表它的每一個面。 這就是 3D 物件儲存的基礎。 對於一些比較複雜的 3D 物體, 比如說一個 Quake 的關卡,將有數以千計(有時數以十萬計)的頂點, 和數以千計的多邊形表面。

  參見上圖的線框表示(注:原文在這裡有一幅圖)。 本質上與上面的立方體例子類似, 它僅僅是由許許多多的小多邊形組成的一些複雜場景。模型和世界如何儲存是渲染器的一部份功能, 而不屬於應用程式/遊戲部份。 遊戲邏輯不需要知道物件在記憶體中如何表示, 也不需要知道渲染器將怎樣把他們顯示出來。 遊戲只是需要知道渲染器將使用正確的視野去表示物件, 並將在正確的動畫幀中把正確的模型顯示出來。

  在一個好的引擎中,渲染器應該是可以完全被一個新的渲染器替換掉, 並且不需要去改動遊戲的一行程式碼。許多跨平臺引擎, 而且許多自行開發的遊戲機引擎就是這樣的,如 Unreal 引擎, --舉例來說, 這個遊戲 GameCube 版本的渲染器就可以被你任意的替換掉。

  讓我們再看看內部的表示方法—除了使用座標系統,還有其他方法可以在計算機記憶體裡表示空間的點。在數學上,你可以使用一個方程式來描述直線或曲線, 並得到多邊形, 而幾乎所有的 3D 顯示卡都使用多邊形來作為它們的最終渲染圖元。 一個圖元就是你在任何顯示卡上面所能使用的最低階的繪製(渲染)單位,幾乎所有的硬體都是使用三個頂點的多邊形(三角形)。 新一代的 nVidia 和 ATI 顯示卡可以允許你以數學方式渲染(被稱為高次表面), 但因為這不是所有圖形卡的標準, 你還不能靠它作為渲染策略。

  從計算的角度來看,這通常有些昂貴,但它時常是新的實驗技術的基礎,例如,地表的渲染,或者對物件銳利的邊緣進行柔化。 我們將會在下面的曲面片小節中更進一步介紹這些高次表面。


剔除概觀
  問題來了。 我現在有一個由幾十萬個頂點/多邊形描述的世界。 我以第一人稱視角位於我們這個 3D 世界的一邊。 在視野中可以看見世界的一些多邊形, 而另外一些則不可見, 因為一些物體, 比如一面看得見的牆壁, 遮擋住了它們。 即使是最好的遊戲編碼人員, 在目前的 3D 顯示卡上, 在一個視野中也不能處理 300,000個三角形且仍然維持 60fps (一個主要目標)。 顯示卡不能處理它, 因此我們必須寫一些程式碼,在把它們交給顯示卡處理之前除去那些看不見的多邊形。 這個過程被稱為剔除。

  有許多不同的剔除方法。 在深入瞭解這些之前,讓我們探討一下為什麼圖形顯示卡不能處理超高數量的多邊形。 我是說,最新的圖形卡每秒鐘不能處理幾百萬個多邊形嗎?它不應該能夠處理嗎? 首先,你必須理解市場銷售宣稱的多邊形生成率和真實世界的多邊形生成率。 行銷上宣稱的多邊形生成率是圖形顯示卡理論上能夠達到的多邊形生成率。

  如果全部多邊形都在螢幕上, 相同的紋理,相同的尺寸大小, 正在往顯示卡上傳送多邊形的應用程式除了傳送多邊形以外什麼也不做, 這時顯示卡能處理多少多邊形數量, 就是圖形晶片廠商呈現給你的數字。

  然而,在真實的遊戲情形中,應用程式時常在後臺做著許多其他的事情 -- 多邊形的 3D 變換, 光照計算, 拷貝較多的紋理到顯示卡記憶體, 等等。 不僅紋理要送到顯示卡, 而且還有每個多邊形的細節。一些比較新的顯示卡允許你實際上在顯示卡記憶體本身裡面儲存模型/世界幾何細節, 但這可能是昂貴的,將會耗光紋理正常可以使用的空間,所以你最好能確定每一幀都在使用這些模型的頂點, 否則你只是在浪費顯示卡上的儲存空間。 我們就說到這裡了。 重要的是,在實際使用顯示卡時,並不必然就能達到你在顯示卡包裝盒上所看到的那些指標,如果你有一個比較慢速的CPU , 或沒有足夠的記憶體時,這種差異就尤為真實。


基本的剔除方法
  最簡單的剔除方式就是把世界分成區域, 每個區域有一個其他可見區域的列表。 那樣, 你只需要顯示針對任何給定點的可見部分。 如何生成可見視野區域的列表是技巧所在。 再者, 有許多方法可以用來生成可見區域列表, 如 BSP 樹, 窺孔等等。

  可以肯定,當談論 DOOM 或 QUAKE 時,你已經聽到過使用 BSP 這個術語了。 它表示二叉空間分割。

  BSP 是一種將世界分成小區域的的方法,通過組織世界的多邊形,容易確定哪些區域是可見的而哪些是不可見的 – 從而方便了那些不想做太多繪製工作的基於軟體的渲染器。它同時也以一種非常有效的方式讓你知道你位於世界中的什麼地方。

  在基於窺孔的引擎 ( 最早由 3D Realms 已經取消的 Prey 專案引入遊戲世界 )裡,每個區域 ( 或房間) 都建造有自己的模型, 通過每個區域的門 ( 或窺孔 )能夠看見另外的區段。 渲染器把每個區域作為獨立的場景單獨繪製。 這就是它的大致原理。 足以說這是任何一個渲染器的必需部份,而且非常重要。

  儘管一些這樣的技術歸類在 "遮擋剔除"之下,但是他們全部都有同樣的目的: 儘早消除不必要的工作。 對於一個FPS遊戲(第一人稱射擊遊戲) 來說,視野中時常有許多三角形,而且遊戲玩家承擔視野的控制,丟棄或者剔除不可見的三角形就是絕對必要的了。 對空間模擬來說也是這樣的, 你可以看見很遠很遠的地方 – 剔除超過視覺範圍外面的東西就非常重要。 對於視野受到限制的遊戲來說 – 比如 RTS (即時戰略類遊戲)--通常比較容易實現。 通常渲染器的這個部份還是由軟體來完成, 而不是由顯示卡完成, 由顯示卡來做這部分工作只是一個時間問題。


基本的圖形管線流程
  一個簡單的例子,從遊戲到多邊形繪製的圖形管線過程大致是這樣:
    · 遊戲決定在遊戲中有哪些物件, 它們的模型, 使用的紋理, 他們可能在什麼動畫幀,以及它們在遊戲世界裡的位置。 遊戲也決定照相機的位置和方向。

    · 遊戲把這些資訊傳遞給渲染器。以模型為例 ,渲染器首先要檢視模型的大小 ,照相機的位置, 然後決定模型在螢幕上是否全部可見, 或者在觀察者 (照相機視野) 的左邊,在觀察者的後面,或距離很遠而不可見。它甚至會使用一些世界測定方式來計算出模型是否是可見的。 (參見下面這條)

    · 世界視覺化系統決定照相機在世界中的位置,並根據照相機視野決定世界的哪些區域 / 多邊形是可見的。有許多方法可以完成這個任務, 包括把世界分割成許多區域的暴力方法,每個區域直接為"我能從區域 D 看見區域 AB & C", 到更精緻的 BSP(二叉空間分割)世界。 所有通過這些剔除測試的多邊形被傳遞給多邊形渲染器進行繪製。

    · 對於每一個被傳遞給渲染器的多邊形, 渲染器依照區域性數學 ( 也就是模型動畫) 和世界數學(相對於照相機的位置?)對多邊形進行變換,並檢查決定多邊形是不是背對相機 (也就是遠離照相機)。背面的多邊形被丟棄。 非背面的多邊形由渲染器根據發現的附近燈光照亮。然後渲染器要看多邊形使用的紋理,並且確定 API/ 圖形卡正在使用那種紋理作為它的渲染基礎。 在這裡,多邊形被送到渲染 API,然後再送給顯示卡。

  很明顯這有些過分簡單化了,但你大概理解了。 下面的圖表摘自Dave Salvator's 3D 管線一文,可以給你多一些具體細節:

  3D 管線
  - 高層的概觀
   1. 應用程式/ 場景
  ·場景/ 幾何資料庫遍歷
  ·物件的運動,觀察相機的運動和瞄準
  ·物件模型的動畫運動
  ·3D 世界內容的描述
  ·物件的可見性檢查,包括可能的遮擋剔除
  ·細節層次的選擇 (LOD)

  2. 幾何
  ·變換 (旋轉,平移, 縮放)
  ·從模型空間到世界空間的變換 (Direct3D)
  ·從世界空間到觀察空間變換
  ·觀察投影
  ·細節接受/ 拒絕 剔除
  ·背面剔除 (也可以在後面的螢幕空間中做)
  光照
  ·透視分割 - 變換到裁剪空間
  ·裁剪
  ·變換到螢幕空間

  3. 三角形生成
  ·背面剔除 ( 或者在光照計算之前的觀察空間中完成)
  ·斜率/ 角度計算
  ·掃瞄線變換

  4. 渲染 / 光柵化
  ·著色
  ·紋理
  ·霧
  ·Alpha 透明測試
  ·深度緩衝
  ·抗鋸齒 (可選擇的)
  ·顯示

  通常你會把所有的多邊形放到一些列表內, 然後根據紋理對這個列表排序(這樣你只需要對顯示卡傳送一次紋理, 而不是每個多邊形都傳送一次), 等等。在過去,會把多邊形按照它們到相機的距離進行排序,首先繪製那些距離相機最遠的多邊形, 但現在由於 Z 緩衝的出現,這種方法就不是那麼重要了。 當然那些透明的多邊形要除外,它們要在所有的非半透明多邊形繪製之後才能夠繪製 ,這樣一來,所有在它們後面的多邊形就能正確地在場景中顯現出來。 當然,象那樣,實際上你必須得從後到前地繪製那些多邊形。 但時常在任何給定的 FPS 遊戲場景中, 通常沒有太多透明的多邊形。 它可能看起來像有,但實際上與那些不透明的多邊形相比,其比率是相當低的。

  一旦應用程式將場景傳遞到 API, API 就能利用硬體加速的變換和光照處理 (T&L), 這在如今的 3D 顯示卡中是很平常的事情。 這裡不討論涉及到的矩陣數學(參見Dave的 3D 管線導論),幾何變換允許 3D 顯示卡按照你的嘗試,根據相機在任何時間的位置和方向,在世界的正確角度和位置繪製多邊形。

  對於每個點或頂點都有大量的計算, 包括裁剪運算,決定任何給定的多邊形實際上是否可見,在螢幕上完全不可見或部分可見。 光照運算,計算紋理色彩明亮程度,這取決於世界的燈光從什麼角度如何投射到頂點上。 過去,處理器處理這些計算,但現在,當代圖形硬體就能為你做這些事情, 這意謂著你的處理器可以去做其他的事情了。很明顯這是件好事情 (tm) ,由於不能指望市面上所有的 3D 顯示卡板上都有T & L, 所以無論如何你自己將必須寫所有的這些例程 (再一次從遊戲開發者角度來說)。 你將在本文各處的不同段落看到 "好事情 ( tm)" 這個詞彙。 我認為,這些特徵為使遊戲看起來更好作出了非常有用的貢獻。 毫不令人吃驚,你也將會看見它的對立面;你猜到了,那就是“壞事情 (tm)”。 我正在嘗試爭取這些詞彙的版權, 你要使用他們就得支付一筆小小的費用喲。


曲面片(高次表面)
  除了三角形,曲面片的使用現在正變得更普遍。 因為他們能用數學表示式來描述幾何 ( 通常涉及某種曲線的幾何形體) ,而不僅僅只是列出大量的多邊形以及在遊戲世界中的位置, 所以曲面片 ( 高次表面的另一個名稱) 非常好。 這樣,你實際上就能夠動態地根據方程式來建立( 和變形 )多邊形網格, 並決定你實際想要從曲面片上看到的多邊形數量。 因此,舉例來說,你可以描述一個管道, 然後在世界中就可以有這種管道的許多樣例。 在一些房間中, 你已經顯示了 10,000個多邊形,你可以說,"因為我們已經顯示了大量的多邊形,而且任何更多的多邊形將會使幀速率下降, 所以這個管道應該只有 100 個多邊形"。 但在另外一個房間中, 視野中只有 5,000個可見的多邊形,你可以說,"因為我們還沒有達到預算可以顯示的多邊形數量 , 所以,現在這個管道能有 500 個多邊形"。 非常美妙的東西 --但你必須首先知道所有這些,並建立網格,這不是無足輕重的。 通過 AGP 傳送同一個物件的曲面方程確實要比傳送其大量頂點節省成本。 SOF2 就使用了這個方法的一種變體來建立它的地表系統。

  事實上現在的 ATI 顯示卡具有 TruForm, 它能帶一個以三角形為基礎的模型,並將該模型轉換為基於高次表面的模型,使其平滑 — 接著再用十倍三角形數量把模型轉換回基於大量三角形的模型 (被稱為retesselation)。然後模型送往管線做進一步的處理。 實際上 ATI 僅僅在 T & L 引擎之前增加了一個階段來處理這個過程。這裡的缺點是,要控制哪些模型需要被平滑處理而哪些又不需要。你常常想要一些邊緣比較尖銳, 比如鼻子,但它卻被不恰當的平滑過了。 這仍然是一種很好的技術,而且我能預見它在將來會被更多的應用。

  這就是第一部份 -- 我們將會在第二部分繼續介紹光照和紋理,下面的章節會更加深入。