遊戲中的Decal(貼花)

遊資網發表於2020-02-26
遊戲中的Decal(貼花)

在遊戲中,decal是一種非常常見的效果,常用來實現彈孔,血跡,塗鴉等效果。最近研究了下Decal在遊戲引擎中的實現方式,大致總結了一下:

1.基於面片實現:

直接用一個Quat的mesh,加上一張貼圖,簡單直觀的實現。

缺點:只能在平面上貼。

2.修改貼圖:

將物體的材質貼圖替換成原貼圖和decal貼圖的混合,適用於靜態批量的物體

缺點:只適用於靜態物體

3.基於SubMesh:

先獲取跟目標投影相交的mesh,然後將mesh根據投影框進行裁剪

1.獲取所有可能和投影框相交的mesh,一般遊戲引擎都會有Octree或BVH儲存mesh的aabb,這一步簡單獲取aabb相交的mesh即可。

2.將mesh的頂點資料變換到投影框的三維空間中,這樣一來是方便裁剪,二來是裁剪完成後可以將變換後的座標值直接作為uv值使用。

3.得到相交的三角形片:

判斷每個點是否在投影框內,如果三角形有任意一個點在框內,則認為三角形與投影框相交。當然這種方法會漏掉一些三角形,比如這中情況:當然如果mesh較小以及要求不精細的話也沒有太大問題。

遊戲中的Decal(貼花)
三角形頂點都不在框內,但是和框相交

比較嚴謹的求交演算法可以使用SAT演算法,參考 RealTimeRendering4 22.12 或者這個https://gdbooks.gitbooks.io/3dcollisions/content/Chapter4/aabb-triangle.html.

4.將所有相交的三角形片,合併成新的IndexBuffer,使用新的decal的紋理重新渲染一次,UV可以直接取對映到框中的xy值,當然要注意在shader中把uv 0~1之外的部分clip掉

5.如果你想的話,也可以對處在邊界,不完全在框內部的三角形進行裁剪,最後整理頂點生成新的mesh.

方法如下:

簡單的逐邊裁剪:

遊戲中的Decal(貼花)
逐邊裁剪

也可以一次性裁剪所有的方向,參考下面的演算法(來自<計算機圖形學第三版>6.8.1)

遊戲中的Decal(貼花)
一次性裁剪所有邊

缺點:比較適用於靜態的物體,建立過程可能耗時較長

4.基於Multi-Pass實現:

和上面方法很相似

1.獲取所有相交的mesh;

2.在mesh正常渲染結束後,再渲染一次,使用decal的shader,向shader中傳入一個ClipToDecal的矩陣(=ClipToWorld * WorldToDecal),在FS中計算計算對映到decal框中的座標,取決於具體的實現,可以將xy座標作為uv,以及裁剪掉uv0~1之外的部分,將decal渲染出來。

Unity的built-in管線中的Projector就是使用的這種方式。

缺點:如果投影框與多個mesh相交,或者mesh很大,則會產生很大的效能消耗。

5.修改渲染shader實現

判斷decal框和某個mesh相交時,將decal標記為需要渲染。修改mesh的shader,傳入一個或多個decal投影框矩陣+數張decal貼圖。FS得到原始的輸出顏色後,再根據decal拿到的顏色進行混合,如果同時有多個Decal,則需要不同數量改變shader變體。

缺點:需要大量調整shader,複雜繁瑣,而且一個mesh上的decal數量在執行時發生變化時,需要動態編譯shader變體。

6.基於後處理實現:

將decal整體作為一個長方體進行渲染兩次來對目標進行貼花

1.首先正常渲染其他的物體,拿到正常渲染的buffer和depth buffer.

2.將投影框作為一個長方體進行渲染,關閉Face Cull,將depth test設定為GreatEqual,輸出一個標誌位到 stencil buffer(或者任意其他可以標誌畫素點的方式),不需要輸出顏色值

3.再次將投影框作為長方體渲染,開啟Face Cull(只繪製長方形靠前的三個面),再上一步中stencil buffer測試通過的位置繪製,與前面方法不同的是,當前畫素點的 WorldPosition 通過從depth buffer中讀取然後反變換獲得(後處理中非常常用的方法).

兩種情況下decal繪製示意圖

遊戲中的Decal(貼花)
投影面在靠後的位置

遊戲中的Decal(貼花)
投影在靠前的位置

缺點:不支援光照

7.Deferred實現

大致和上面的方法相同

deferred 渲染管線中渲染所有gbuffer之後

和上一個方法中講到的一樣,同樣是先渲染長方體,寫入stencil buffer,然後再次渲染長方體,根據stencil buffer來改變gbuffer中的資料,根據需要選擇修改basecolor,normal等;

因為gbuffer被修改,後面的光照計算會產生decal的效果。

UE4中的DefferedDecal,就是這種方法(未使用Dbuffer時)

缺點: 只能用於Deferred,不支援烘培光(因為烘培光是在渲染gbuffer時加上的).

8.Dbuffer

先進行depth prepass渲染深度圖

用上面提到的方法將decal渲染到類似gbuffer的dbuffer上,然後在渲染gbuffer時(或者forward渲染時),直接應用同樣位置對dbuffer進行取樣,融合到gbuffer中,可以支援烘培光,支援deferred,forward管線。

Unity的HDRP以及UE4中的DeferredDecal(使用Dbuffer),使用該方法

遊戲中的Decal(貼花)
UE4中的Decal Material,當選擇使用Dbuffer渲染時,會根據不同的型別來使用不同數量的Dbuffer Render Target

缺點:很大的效能消耗

9.常見的問題

1.垂直角度投影時可能出現這樣的拉伸,可以加上一個角度判斷,丟棄角度超過某個閾值的畫素。

遊戲中的Decal(貼花)

2.可能需要在邊緣處加上一些fade off的效果,防止突兀的邊界。

3.blend方式,decal材質可以提供base color,normal等屬性,decal是半透明時,需要仔細考慮和原圖base color等的混合方式,normal 需要變換下切空間後再進行混合。

4.一般情況下decal無法支援帶骨骼動畫的物體。

作者:TC130
專欄地址:https://zhuanlan.zhihu.com/p/100748588

相關文章