遊戲中的實時光線追蹤技術(技巧)

提到遊戲使用的實時渲染,很多人都會覺得光柵化渲染是唯一的方法,而想要在遊戲中使用實時的光線追蹤,似乎還是遙遠的夢想。

雖然整體的光線追蹤實時渲染架構以及相關硬體還在試驗階段,但從2016GDC的技術展示來看,遊戲開發者在實踐中已經找到了不少能利用現有架構實現光線追蹤的小技巧,光線追蹤技術已經悄悄地開始在遊戲開發中發揮作用了。

光線追蹤的優點自不用說,容易理解、實現簡單、支持體積渲染,況且還有shadertoy上那麼多使用光線追蹤製作的眩目效果。但問題是光線追蹤和現有的遊戲引擎架構不統一,而且實時運行的效率不高,如果要做體積渲染還存在如何存儲和採樣體積數據的問題。

下面的內容會介紹一些在現有遊戲引擎里針對這些問題解決方案。

減少計算量

首先光線追蹤在實時運行時最大的問題是對PixelShader的壓力太大,shadertoy中很多代碼都在PixelShader中循環幾十上百次,顯然在真實開發中很難使用這樣的代碼。

這裡介紹一種使用VertexShader進行光線追蹤的思路,以製作實時溶球作為例子。

直接實現的思路是建立溶球的Distance Field,在PixelShader中從視點開始推進光線,循環直到Distance Field為0,達到溶球表面,然後計算反射折射等。這樣的方法,在PixelShader中至少需要十幾次循環才能trace到溶球表面。

改進思路是將光線追蹤代碼移到VertexShader中,在Mesh的每個頂點進行光線追蹤計算,按照光線追蹤的結果進行頂點變形。如圖中,使用一個球形的Mesh做頂點變形,每個頂點的位置按照光線追蹤的結果向內收縮,直到溶球表面。

如果選擇合適的球形Mesh大小和位置,收縮的步驟可以小到三次以內。這個方法同時減少了光線追蹤的循環次數,也減輕了PixelShader的壓力。完全可以在移動平台上運行。

實時光線追蹤的溶球,左:添加反射,右:添加折射和深度

另一種減少計算量的方法就是Dithering,在獨立遊戲Inside中的體積光渲染就使用了這種的技術。

在我的另外一篇遊戲開發相關實時渲染技術之體積光描述過高質量的體積光渲染應該使用光線追蹤的方法。問題在於優質的體積渲染需要的循環次數可能會達到一百次以上,如果循環次數降低到一定程度會因為採樣數不足,會造成條紋狀瑕疵。

這裡的改進思路是,在光線追蹤的起點添加隨機噪音,這樣條紋狀瑕疵會變成更加隨機的噪點。之後再添加一層模糊或者是抗鋸齒,這樣整體的瑕疵感就會明顯減少。

左:條紋狀瑕疵,中:隨機噪點,右:抗鋸齒模糊

這種方法的缺點就是渲染的對象會顯得比較模糊,不過用於體積光這種本身就不是很清晰的渲染對象倒是很適合。

體積貼圖

光線追蹤重要的應用之一就是體積渲染。shadertoy中的體積渲染大多是使用隱式參數方程和實時演算法生成噪音來製造體積的Distance Field。這種方法對於不熟悉數學的人來說難度有點大,也不適合美術人員創作。所以體積渲染時最常用的還是用Houdini等軟體生成體積貼圖。

目前主流遊戲引擎還沒有全面支持3DTexture,所以我們要用體積切片的方法模擬3DTexture。Houdini和Blender都有方法渲染體積切片。渲染好的切片按照順序組成一張平鋪貼圖。

在採樣的時候,先確定採樣點的所處z軸位置上下兩張貼圖,在uv位置採樣兩次,然後通過z軸lerp兩個採樣數據,就可以得到採樣點的密度數據了。

引擎集成

將光線追蹤的體積渲染集成到遊戲引擎中會有兩個問題。

第一是作為體積渲染的載體Mesh。片面Mesh肯定是不合適的,因為轉一個角度可能就看不到了,應該選擇閉合的Mesh。這個Mesh應該仿照天空球方法,法線向內,因為如果法線向外,進入Mesh內部就沒辦法顯示了。

第二是和場景現有物體的遮擋問題。解決方案是獲得場景的深度信息,做光線追蹤的時候反過來從場景的深度位置向視點進行追蹤,這樣就能完美實現遮擋了。如果做正向光線追蹤就需要在每次循環的時候檢查一下深度信息,做無謂的消耗。

圖:使用反向法線和場景深度信息製作的體積渲染

補充最近用這種方法做的webgl測試網頁

使用chrome瀏覽器可瀏覽

簡單體積渲染

foxhuntd.github.io/crys

體積流體模擬

foxhuntd.github.io/simu
推薦閱讀:

Pico-8——神奇的虛構遊戲機
技術美術的核心競爭力是什麼?
大家對遊戲開發都存在哪些認知誤區?
從事遊戲開發,需要什麼技能?

TAG:计算机图形学 | 游戏开发 |