Unity3D The Blacksmith 角色陰影技術使用心得
這個demo放出來之後,對其中的角色陰影部分的技術十分感興趣,我就趕緊下過來研究了一下。官方的技術部落格裡有對這一部分技術的介紹,連結在這裡:獨特的角色陰影,
其中寫到只要在其他shader裡新增
#pragma multi_compile _ UNIQUE_SHADOW UNIQUE_SHADOW_LIGHT_COOKIE
#include "UniqueShadow_ShadowSample.cginc"
這兩行程式碼就能使用這個高階陰影了。 我之前也因為在做一些demo的時候發現unity預設陰影達不到理想效果,和ue4比起來還是有一定差距的。於是就想到使用這個陰影技術了。於是我興致勃勃的把這兩行程式碼和幾個庫檔案加到我的shader和專案裡去,一執行發現,高階陰影並沒有出現。哎,看樣子只能自己研究了。
既然是陰影,那麼著手點就是unity裡的陰影部分的程式碼,手動寫過自定義陰影的人應該都知道,unity陰影計算:SHADOW_COORDS、TRANSFER_SHADOW、SHADOW_ATTENUATION三個函數了,unity把陰影生成的程式碼都寫在了這三個函式裡,方便呼叫。我看The Blacksmith的技術部落格上寫著: 我們發現原來有一個非常簡單的方法來對常見的Unity shader所用的陰影方法進行重寫。 於是我琢磨著應該是他們也用了這三個方法,於是開啟 UniqueShadow_ShadowSample.cginc 檔案,果不其然,在最下面找到了這麼一段程式碼
#if defined(UNITY_PASS_FORWARDBASE) || defined(UNITY_PASS_FORWARDADD) || defined(UNIQUE_SHADOW_FORCE_REPLACE_BUILTIN) #undef SHADOW_COORDS #undef TRANSFER_SHADOW #undef SHADOW_ATTENUATION #define SHADOW_COORDS(i) UNIQUE_SHADOW_INTERP(i) #define TRANSFER_SHADOW o.uniqueShadowPos = mul(u_UniqueShadowMatrix, float4(worldPos.xyz, 1.f)); #define SHADOW_ATTENUATION(i) UNIQUE_SHADOW_SAMPLE(i); #endif
這裡的意思就是,如果判斷pass的名字是
UNITY_PASS_FORWARDBASE、NITY_PASS_FORWARDADD、UNIQUE_SHADOW_FORCE_REPLACE_BUILTIN
這三個的話,就會把
SHADOW_COORDS(i) 替換成UNIQUE_SHADOW_INTERP(i),
SHADOW_ATTENUATION(i)替換成UNIQUE_SHADOW_SAMPLE(i);
TRANSFER_SHADOW的值替換成後面的 o.uniqueShadowPos
這樣的話就好說了,之後只要把自己的shader裡生成陰影的pass名字改成三個裡的任意一個,生成陰影的程式碼還是按照原來的方式寫就可以了。
於是我立馬新建了一個Unlit Shader,新增上生成陰影的程式碼後,把pass的名字改成了UNITY_PASS_FORWARDBASE,程式碼如下
Shader "Custom/simpleSuperShadow"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
Tags{ "LightMode" = "ForwardBase" }
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_fwdbase
#include "UnityCG.cginc"
#include "AutoLight.cginc"
#define UNITY_PASS_FORWARDBASE
#pragma multi_compile _ UNIQUE_SHADOW UNIQUE_SHADOW_LIGHT_COOKIE
#include "UniqueShadow/UniqueShadow_ShadowSample.cginc"
struct v2f
{
float2 uv : TEXCOORD0;
float4 pos : SV_POSITION;
float3 worldPos : TEXCOORD1;
SHADOW_COORDS(2)
};
sampler2D _MainTex;
float4 _MainTex_ST;
v2f vert (appdata_full v)
{
v2f o;
o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
o.worldPos = mul(_Object2World, v.vertex).xyz;
TRANSFER_SHADOW(o);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
// sample the texture
fixed4 col = tex2D(_MainTex, i.uv);
fixed shadow = SHADOW_ATTENUATION(i);
return col * shadow;
}
ENDCG
}
}
FallBack "Diffuse"
}
點選執行之後,shader報錯,提示我頂點函式裡worldPos未定義,報錯的地方是在TRANSFER_SHADOW(o)這一行,但是我明明在v2f裡定義了這個變數,並且在上一行賦值了才對。這個提示不應該出現才對。對於這個問題,我毫無頭緒,不知道自己哪裡出了問題。於是我又新建了一個standard surface shader,這次我只在裡面添加了
#pragma multi_compile _ UNIQUE_SHADOW UNIQUE_SHADOW_LIGHT_COOKIE
#include "UniqueShadow/UniqueShadow_ShadowSample.cginc"
兩行,執行一下,居然成功了,於是我在編輯器裡點開了surface shader的原始程式碼,通過仔細對比陰影生成的三個函式發現,surface shader在
頂點函式使用worldPos的時候,並不是直接給o.worldPos賦值的,而是先定義了一個float3的worldPos變數,計算出來以後再讓o.worldPos = worldPos,這裡感覺應該是一樣的才是,並沒有什麼特別的地方啊。雖然我不是很清楚surface shader裡為啥要這麼寫,但是抱著試一試的心情,我把自己的shader的頂點函式從原來的
v2f vert (appdata_full v)
{
v2f o;
o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
o.worldPos = mul(_Object2World, v.vertex).xyz;
TRANSFER_SHADOW(o);
return o;
}
這樣,改成了
v2f vert (appdata_full v)
{
v2f o;
o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
float3 worldPos = mul(_Object2World, v.vertex).xyz;
o.worldPos = worldPos;
TRANSFER_SHADOW(o);
return o;
}
這樣,於是我執行一下。臥槽竟然成功了。按道理來說定義一個臨時變數和直接賦值應該沒有區別才是,為啥這裡不定義一個就不行呢,
mul(_Object2Wrold,v.vertex).xyz返回的本來就是一個float3的變數才是,為毛非要這麼做才能正確顯示呢。實在是不知道要怎麼解釋。希望知道的大大們能夠幫我解釋一下。
總結一下這個高階陰影的使用方法
1.給計算陰影的pass新增
#define UNITY_PASS_FORWARDBASE
#pragma multi_compile _ UNIQUE_SHADOW UNIQUE_SHADOW_LIGHT_COOKIE
#include "UniqueShadow/UniqueShadow_ShadowSample.cginc"
這三行程式碼,然後在頂點函式使用TRANSFES_SHADOW的地方改成
float3 worldPos = mul(_Object2World, v.vertex).xyz;
o.worldPos = worldPos;
TRANSFER_SHADOW(o);
這樣自定義的shader也能夠使用這個超高解析度的陰影了。
這裡我用了一個卡通shader做測試
未使用高階陰影:使用高階陰影:
這樣就能做出媲美ue4的高階陰影來了。