神作面部陰影渲染還原
前幾年學過兩天渲染,昨天花了一個晚上,學Unity的shaderlab,做的這個還原。
結果折騰了半天我也沒訪問到shadowmap,只好裝了URP然後在這個專案上寫的程式碼
這個面部陰影看上去是非常簡單的實現,網上卻沒找到相關資料,其他人的還原好像也都是什麼修改法線。沒辦法自己想想辦法,希望B站上MMD能做的好看點。
原理
面部陰影的來源
就這張圖了,這是張閾值圖,存了不同角度下陰影覆蓋臉上哪些部分。
用photoshop閾值功能可以看的更清楚一點。
生成
這張圖生成不算複雜,可以插值手繪圖得到
首先手繪一下幾個特定角度下陰影的覆蓋情況,然後生成SDF,這樣對於圖片上每個點,都知道我現在在手繪的圖的陰影的裡面還是外面,距離最近的點有多遠。
比方說我一個畫素點在30°陰影圖裡面,在60°陰影圖外,距離30°的圖是-50,距離60°的圖是50(一個正一個負,負的說明在陰影圖裡面),那麼我在45°的時候就應該在陰影裡,46°之後就不在陰影裡面了。
最後生成0-90°的所有陰影圖,加在一起除以圖片數量,就可以得到這張所謂的lightmap了。
理論上是這樣,我也沒有試。
使用
需要有lightDir,跟角色朝向Front。 垂直方向UP
dot(Front, lightDir)小於0說明光在角色背面,不應用lightmap(全部是陰影)
cross(Front , UP)可以得到角色的左向跟右向, Left 與 Right
dot(Left , lightDir)小於lightmap的值 說明當前片元在陰影下面。
不過由於這個lightmap只存了0-90°光照下的陰影,所以左半邊臉右半邊臉得各來一次。
shader
float3
UP
=
float3
(
0
,
1
,
0
);
// cs腳本里動態修改
float3
Front
=
float3
(
0
,
0
,
1
);
float3
Left
=
cross
(
UP
,
Front
);
float3
Right
=
-
cross
(
UP
,
Front
);
float
FrontL
=
dot
(
normalize
(
Front
。
xz
),
normalize
(
L
。
xz
));
float
LeftL
=
dot
(
normalize
(
Left
。
xz
),
normalize
(
L
。
xz
));
float
RightL
=
dot
(
normalize
(
Right
。
xz
),
normalize
(
L
。
xz
));
float
lightAttenuation
=
(
FrontL
>
0
)
*
min
(
(
surfaceData
。
_lightMap
。
r
>
LeftL
),
1
-
(
1
-
surfaceData
。
_lightMap
。
r
<
RightL
)
);
不是搞圖形學的,程式碼比較奔放,意會即可。
不過看起來unity用起來還挺簡單的?(小聲:要不轉行做遊戲算了。
最後,為啥這shader寫出來全是鋸齒啊
之前陰影方向搞反了,現在改了一下。
float3
Front
=
unity_ObjectToWorld
。
_12_22_32
;
float3
Right
=
unity_ObjectToWorld
。
_13_23_33
;
float
FrontL
=
dot
(
normalize
(
Front
。
xz
),
normalize
(
L
。
xz
));
float
RightL
=
dot
(
normalize
(
Right
。
xz
),
normalize
(
L
。
xz
));
RightL
=
-
(
acos
(
RightL
)
/
3。14159265
-
0。5
)
*
2
;
float
lightAttenuation
=
(
FrontL
>
0
)
*
min
(
(
surfaceData
。
_lightMap
。
r
>
RightL
),
(
surfaceData
。
_lightMap
。
g
>
-
RightL
)
);
鋸齒問題也解決了,取樣shaodwRamp的時候,貼圖得把mipmaps關掉,本身貼圖就特別窄,不知道他給生成了個啥mip