3D引擎設計構想
一、渲染流水線
渲染流水線是指在更高層面上我們自己定義並實現的渲染狀態管理機制。
渲染狀態包括:D3D的RenderState和TextureStageState。
渲染流水線要根據渲染對象的這些屬性,對渲染對象進行分組、統一渲染,從而減少硬體狀態轉換。
D3D有幾十個RenderState,但不是所有的RenderState都作為分組的依據,只有部分對視覺效果有較明顯影響的RenderState才作為分組的依據。比較重要的RenderState有:Zenable(開啟/關閉深度緩衝)、AlphaBlendEnable(開啟/關閉Alpha混合)等。其中AlphaBlendEnable是最為重要的分組依據,凡是AlphaBlendEnable設為TRUE的渲染對象,都必須分在同一組渲染,而且是在所有AlphaBlendEnable設為FALSE的渲染對象渲染之後,並且渲染前還應進行排序。
TextureStageState也是對渲染對象進行分組的重要依據。這是出於這樣一條準則:“使用相同貼圖的三角形一塊渲染,可以提高渲染速度”。另外TextureStageState還包含一些貼圖層級的混合操作,這些操作也可作為分組依據。
總體上講,渲染流水線就是實現這樣一種功能:在一楨開始時,接受所有該楨將要渲染的對象,按這些對象的屬性對它們分組,然後對每一組,設定一遍渲染狀態,進行渲染,直到所有組都被渲染。
渲染流水線是基於一套抽象的3D渲染API工作的。因此我們要抽象定義D3D(包括Device、Texture、VertexBuffer等)和它相關的列舉資料型別。這樣的抽象3DAPI將是版本無關的,甚至可以用OpenGL來實現。
二、SceneGraph
3D遊戲的情境十分龐大複雜。為了提高渲染效能,並讓這個虛擬世界同玩家互動起來,有必要使用一種高效的情境組織方法。SceneGraph就是實現情境組織的模組。
SceneGraph要有如下兩個功能:劃分空間、組織物體。
劃分空間往往使用一種樹型結構。對一個室外渲染引擎來說,可能使用四叉樹或者八叉樹。我的地形渲染演算法就是基於四叉樹的,不過我的四叉樹結構有一個問題不好解決,所以打算使用八叉樹代替。八叉樹可以更好的劃分一個封閉的立體空間。每一個樹的結點都可以擁有8個子結點,這8個子結點平分他們父結點所對應的空間。
由此可見使用樹形結構劃分空間的一大特點是:倘若某個結點位於一個範圍內,則這個結點的所有子結點也都位於這個範圍內;倘若某個結點位於一個範圍外,則這個結點的所有子結點也都位於這個範圍外。這一特點可以用於實現快速的可視判斷。我們把所有的渲染對象按其空間位置將其“連結”到相應的樹結點上。那麼如果該結點在視野範圍內,則該結點所連結的所有渲染對象及其子結點的所有渲染對象都是可見的。我們將這些可見的渲染對象提交給渲染流水線,分組後即可渲染到螢幕上了。
由於所有的對象都連結在相應的樹結點上,且同層結點是平分他們父結點的空間範圍且不會有重疊,因此位於不同葉結點內的渲染對象之間一定是互相不接觸的,那麼碰撞就只發生在同一結點內。這樣對每一個要移動的物體來說,他只需與當前自己所位於的結點內的其他對象依次作碰撞檢測就可以了,而不用與整個情境內的所有物體都做碰撞檢測,這大大減小了碰撞檢測的計算次數,提高了遊戲運行速度。
以上說的都是空間劃分。SceneGraph還要有組織物體的功能。比如想要做一個能量球,讓它繞著玩家旋轉,為玩家提供能量保護,而玩家本身還在做著或上或下或前或後的運動,這將使能量球在世界空間中的運動變得非常複雜。如果即時的更新該能量球在世界空間中的位置,那代碼將是非常醜陋的。事實上能量球只是以玩家為中心做公轉運動罷了。那麼在渲染能量球的時候,將能量球相對玩家的位置加上玩家的位置便是能量球在世界空間中的位置。為此,我們可以實現一種將物體組織在一塊的SceneGraph,優雅的解決多個物體的相對運動。這也要用到樹結構,但與空間樹不同,物體樹的子結點與父結點沒有空間上的內含項目關聯性,物體樹的子結點只是相對父結點運動,而不是平分父結點的空間。
骨骼動畫就類似這種物體樹。
將空間劃分為結點,另一個重要的目的是與室內情境的無縫銜接。室內情境因為有很多封閉的走廊、房間,所以很自然的可以用結點劃分空間(室外常景往往是用四叉樹或八叉樹強行劃分,且是標準的平衡樹)。如果室內室外都是結點,銜接問題自然很好解決(有著相容的介面)。