您當前的位置:首頁 > 攝影

Unity3D 深度貼花

作者:由 StriteR 發表于 攝影時間:2021-10-24

一年前遇到在地面挖坑需求的時候第一眼想的是CPU端構建,如程式化地形以及CSG,但是實現起來十分吃力,同時應用場景相對有限,隨之棄坑,不過在後續的學習過程中瞭解到視差貼圖原理,以及深度修改,於是將這兩點結合整理出深度貼花。

示例

Unity3D 深度貼花

Unity3D 深度貼花

https://www。zhihu。com/video/1435394469462491136

示例場景: Billboard

渲染佇列配置: 1998 地面 1999 貼花 2000+普通渲染

程式碼實現:UberDiffuse。Shader 索引 189行

核心庫:Parallax。hlsl

視差貼圖對映

通常在表面空間做視覺凹陷,在各種文章上了解到最多的就是視差貼圖 (Parallax Mapping)。

透過在切線空間的視線向量求出平面空間的uv偏移向量,並做迴圈取樣獲取視角偏移距離,並與uv偏移向量相乘獲取最終的uv偏移,即可獲得根據視角進行深度偏移的貼圖。

Unity3D 深度貼花

基礎的視差貼圖對映

https://www。zhihu。com/video/1435412105470644224

//專案內程式碼片段

float

ParallaxMappingPOM

TEXTURE2D_PARAM

_texture

_sampler

),

float

depthOffset

float2

uv

float2

uvOffset

uint

marchCount

{

float

deltaParallax

=

1。0

h

/

marchCount

float2

deltaUV

=

uvOffset

/

marchCount

float

layer

=

0。

h

float

parallaxSample

=

SAMPLE_TEXTURE2D_LOD

_texture

_sampler

uv

0

)。

r

-

depthOffset

float

preParallaxSample

=

0。

h

[unroll]

for

uint

i

=

0

u

i

<

128

u

i

+=

1

u

{

if

parallaxSample

<

layer

break

preParallaxSample

=

parallaxSample

parallaxSample

=

SAMPLE_TEXTURE2D_LOD

_texture

_sampler

uv

0

)。

r

-

depthOffset

layer

+=

deltaParallax

uv

-=

deltaUV

}

float

d1

=

layer

-

parallaxSample

float

d2

=(

layer

-

deltaParallax

)-

preParallaxSample

float

interpolate

=

d1

*

rcp

d1

-

d2

);

return

layer

-

interpolate

*

deltaParallax

}

//輸入UV以及世界切線空間矩陣,以及世界空間視角方向

void

ParallaxUVMapping

inout

float2

uv

half3x3

TBNWS

half3

viewDirWS

{

half3

viewDirTS

=

mul

TBNWS

viewDirWS

);

half3

viewDir

=

normalize

viewDirTS

);

float2

uvOffset

=

viewDir

xy

/

viewDir

z

//UV偏移

uvOffset

*=

INSTANCE

_DepthScale

);

//深度比

float

depthOffset

=

INSTANCE

_DepthOffset

);

//深度偏差

uint

parallaxCount

=

INSTANCE

_ParallaxCount

);

float

parallax

=

ParallaxMappingPOM

_DepthTex

sampler_DepthTex

depthOffset

uv

uvOffset

parallaxCount

);

uv

-=

uvOffset

*

parallax

}

注:視差貼圖對映可以實現相較於體積雲更便宜的雲海效果

可參考ParallaxCloud。shader,更多詳情參考知乎某位大佬文章,。

空間資訊修改

在上文的基礎上可以發現,視差貼圖對映只與平面空間的uv做互動,如果要與幾何資訊互動做出更多符合視覺的效果(如陰影,光照與螢幕深度)則需要在對世界座標以及深度進行修改。

對於陰影貼圖與光照取樣透過修改

positionWS

變數,在

fragment

階段計算相應的輸入。

對於螢幕深度則需要透過

SV_DEPTH

輸出修改後的螢幕空間深度深度。

計算步驟

Unity3D 深度貼花

已知其餘資訊,求projectionWS

在進行視差對映前獲取

viewDirTS與normalTS(0,0,1)的點乘

用於後續的資料計算。

由於視差對映獲取的是世界切線空間空間深度

depthTS

與引數相乘求得

depthWS

透過1。得出的投影係數進行投影,獲取到世界空間視差投射距離

projectionDistance

與viewDirWS相乘求出世界座標偏移,與原始世界座標相加求出最終世界座標(

positionWS

)。

透過矩陣轉換成螢幕空間深度SV_DEPTH。

Unity3D 深度貼花

帶空間資訊修改的視差貼圖對映

https://www。zhihu。com/video/1435413151173525504

//inout 世界座標以及螢幕深度,用於後續的陰影以及SS深度(如AO)相關計算,並獲得正確的幾何關係。

void

ParallaxUVMapping

inout

float2

uv

inout

float

depth

inout

float3

positionWS

half3x3

TBNWS

half3

viewDirWS

{

half3

viewDirTS

=

mul

TBNWS

viewDirWS

);

half3

viewDir

=

normalize

viewDirTS

);

float2

uvOffset

=

viewDir

xy

/

viewDir

z

*

INSTANCE

_DepthScale

);

float

parallax

=

0。

float

depthOffset

=

INSTANCE

_DepthOffset

);

float

marchDelta

=

saturate

dot

half3

0。

0。

1。

),

viewDirTS

));

uint

parallaxCount

=

INSTANCE

_ParallaxCount

);

//最佳化,視角越垂直與平面視差貼圖迴圈次數越少

parallaxCount

=

min

lerp

parallaxCount

/

2

u

parallaxCount

marchDelta

),

128

u

);

parallax

=

ParallaxMappingPOM

_DepthTex

sampler_DepthTex

depthOffset

uv

uvOffset

parallaxCount

);

float

projectionDistance

=

parallax

*

INSTANCE

_DepthScale

)*

step

0。01

h

marchDelta

)*

rcp

marchDelta

);

positionWS

=

positionWS

-

viewDirWS

*

projectionDistance

*

INSTANCE

_DepthBufferScale

);

depth

=

EyeToRawDepth

TransformWorldToEyeDepth

positionWS

UNITY_MATRIX_V

));

uv

-=

uvOffset

*

parallax

}

侷限性及拓展

-由於進行深度覆蓋,ZTest需要Greater或者Always

-SV_DEPTH 需要更高的Target Level,在低端機型不適用。

-深度構建在GPU端,所以無法同步深度資訊到CPU端,遇到複雜碰撞需求需要做更多邏輯。

*對於視差貼圖的重複取樣最佳化可以嘗試:

透過降低取樣數以及Dither的方式。

對於幾何複雜度不高的淺坑,可以使用單次視差對映取樣結果配合空間資訊修改。

將邏輯替換為幾何射線相交檢測(如AABB與BS),避免多次取樣貼圖帶來的效能開銷。

*在複雜度較高的場景,由於ZTest導致貼花渲染錯誤,目前想到的解決方法有兩點:

透過Geometry佇列深度貼圖做深度比較並對畫素做Clip操作。

CPU Culling把渲染的幾何複雜度降低。

標簽: 貼圖  深度  視差  uv  instance