3D 效能最佳化
剔除
Godot 會自動執行視錐剔除,以避免繪製在視窗外的物件。這對於小型場景的遊戲效果很好,但在大型關卡中可能很快就會遇到效能瓶頸。
遮蔽剔除
舉例來說,當你在小鎮中走動時,實際上你只能看到你所在街道上的幾棟建築物,還有天空與頭頂飛過的幾隻鳥。但對於單純的算繪器來說,它認為你能看到整個小鎮。它不只會算繪你面前的建築,還會算繪後面的街道、街上的行人,以及再更後方的建築。你很快就會遇到需要算繪比實際可見多 10 倍、甚至 100 倍物件的情況。
情況並沒有看起來那麼糟,因為 Z 緩衝區通常會讓 GPU 只對前面的物件進行完整著色。這稱為 深度預先通過 (depth prepass),在 Godot 使用 Forward+ 或相容性算繪模式時預設啟用。但不必要的物件仍然會降低效能。
我們可以減少算繪量的一個方法是**善用遮擋**。Godot 4.0 及之後版本提供了使用遮擋器節點(Occluder Node)進行遮擋剔除的新方式。請參閱 遮蔽剔除 以瞭解如何在場景中設定遮擋剔除。
備註
在某些情況下,你可能需要調整你的關卡設計以增加更多遮擋機會。例如,你可以增加更多牆壁,避免玩家看到過遠的區域,否則因為遮擋剔除機會減少而導致效能下降。
透明物件
Godot 會依據 Material 和 Shader 對物件排序以提升效能。然而,透明物件無法這麼做。透明物件需要從遠到近繪製,以正確混合背景。因此,請儘量減少透明物件的使用。如果某個物件只有一小部分需要透明,建議將該部分拆分成獨立的表面,給它專屬材質。
更多資訊請參考 GPU 最佳化 文件。
細節層級(LOD)
在某些情境下,尤其是遠距離時, 以更簡化的模型取代複雜幾何體 是個好方法。最終使用者很可能分辨不出差異。想像一下遠方有大量樹木的情境,你可以採用多種策略依照距離替換模型,例如使用較低面數(Low Poly)的模型,或利用透明度模擬更複雜的幾何外觀。
Godot 4 提供了多種控制細節層級(LOD)的方法:
使用 網格的細節級別(LOD) 於匯入網格時自動產生 LOD。
在 3D 節點中透過 可見範圍(HLOD) 手動設定可見範圍。
雖然這些方法可以單獨使用,但搭配在一起時效果最佳。例如,你可以設定可見範圍來隱藏玩家無法注意到的遠距粒子特效,同時利用網格 LOD 讓粒子在遠處時以較低細節算繪。
可見範圍也很適合為遠距幾何體設置「冒充物」(impostor)(見下文)。
看板物件與冒充物(Billboard 與 Impostor)
利用透明度實現 LOD 最簡單的方式就是使用看板(Billboard)。例如,你可以用一個透明四邊形來代表遠方的樹。這能大幅降低算繪成本,但如果有大量樹彼此重疊時,透明度會消耗填充率(Fill Rate)。(更多關於填充率的資訊請見 GPU 最佳化 )。
另一種方式不是只用單一樹木,而是把多棵樹作為一個群組一次算繪。如果該區域玩家只能遠觀,無法靠近,這種方法特別有效。
你可以預先算繪物件在不同角度下的影像,製作成冒充物(Impostor)。甚至可以進一步,定期把物件當前視角重新算繪到貼圖上作為冒充物。在遠處時,視角要有明顯變化需要很大移動距離。雖然實作上較為複雜,但對某些專案來說非常值得。
使用自動實例化
此功能僅在 Forward+ 算繪器中實作,Mobile 與 Compatibility 不支援。
若場景中有大量相同物件,可利用自動實例化以減少 draw call 次數。對於使用相同網格與材質的 MeshInstance3D,這會自動生效,無需手動設定。
自動實例化要能發揮作用,材質必須為不透明或通過 Alpha 測試(alpha scissor 或 alpha hash)。採用 Alpha 混合或深度預通過的材質不會以此方式實例化,需改用下文所述的 MultiMesh。
使用手動實例化(MultiMesh)
如果需要在同一區域或附近繪製多個相同物件,請考慮使用 MultiMesh。MultiMesh 可以用極低效能成本繪製成千上萬個物件,非常適合用於大量群體、草地、粒子等場景。
詳情請參閱 使用 MultiMesh 文件。
烘焙光照
燈光運算是最吃資源的算繪操作之一,特別是即時光照、陰影(尤其是多盞燈),以及 全域照明。這些在低階裝置上可能完全跑不動。
建議使用烘焙光照,尤其是在行動裝置上。這種方法畫面效果很棒,但缺點是光源無法動態改變。有時這是值得的取捨。
關於如何使用烘焙光照貼圖,請參考 使用光照貼圖全域光照。為了最佳效能,請將燈光的烘焙模式設為 靜態**(Static),而非預設的 **動態,如此可略過已烘焙網格的即時光照運算。
靜態**燈光的缺點是無法把陰影投射到已烘焙光照的網格上。這會讓戶外場景與動態物件看起來比較單調。效能與畫質之間的折衷方案,是讓 :ref:`class_DirectionalLight3D` 使用 **動態,而大多數(甚至全部)點光源與聚光燈則用 靜態。
動畫與蒙皮(Skinning)
在某些平台上,動畫與頂點動畫(如蒙皮與變形)效能消耗非常大。你可能需要大幅降低動畫模型的面數,或限制同時出現在畫面的數量。你也可以針對遠距或被遮擋的模型減少動畫播放速率,甚至在玩家不會注意時直接暫停動畫。
你可以利用 VisibleOnScreenEnabler3D 與 VisibleOnScreenNotifier3D 節點來達成上述控制。
大型世界
如果你要設計大型世界,會遇到跟小型遊戲完全不同的挑戰與考量。
大型世界通常需要分割為可隨需求載入的區塊(Tiles),你在世界中移動時再動態加載。這可以避免記憶體暴增,並將運算集中在玩家所在區域。
在大型世界中,由於浮點數誤差,可能會出現算繪或物理異常。你可以參考 大世界座標 來解決這個問題。如果無法使用大型世界座標系統,也可以考慮讓世界隨玩家移動、或定期平移原點來保持所有物件圍繞 Vector3(0, 0, 0) 為中心。