翻譯12 Unity 半透明陰影

支持鏤空陰影
噪聲
粗略的半透明陰影
鏤空陰影和半透明陰影之間切換算法

使用Unity 5.6.6f1函數

1 鏤空陰影

翻譯11介紹了鏤空渲染,可能也注意到了,物體的投射的陰影是物體自己的形狀,跟鏤空形狀徹底不一致。這是由於咱們以前的Shader投射陰影只是簡單的採樣了光的方向和到達物體表面的距離,沒有區分表面形狀。spa

image

圖1.1 不符合現實的半透明陰影翻譯


1.1 重寫陰影

爲了將透明度考慮在內,須要在陰影投射pass通道訪問alpha值。這意味着咱們要採樣albedo紋理。然而,當僅用opaque渲染模式時就不須要採樣,爲此須要適配Shader變體。code

如今的Shader已經集成了兩個變體陰影,一是針對PointLight立方體陰影,二是針對其餘類型燈光。如今增長第三個變體。改造一下以前的Shadow.cginc-略blog

1.2 裁剪陰影片斷

就像渲染鏤空效果時,陰影的鏤空也根據alpha值來決定是否丟棄片斷。所以算法上大體類似,必要變量也類似。拷貝tint、albedo、renderingsettings。ip

#include "UnityCG.cginc"

float4 _Tint;
sampler2D _MainTex;
float4 _MainTex_ST, _AlphaCutOff;

當使用Cutout渲染模式就須要採樣albedo紋理,可是隻有當不使用albedo紋理alpha值做爲平滑度時才能這樣作(以前是把albedo`s alpha做爲了平滑值,避免衝突)。若是知足上述條件,就可把uv座標傳遞給片元程序。get

寫一個宏_SHADOW_NEED_UV it

#if defined(_RENDERING_CUTOUT) && !defined(_SMOOTHNESS_ALBEDO)
    #define _SHADOW_NEED_UV 1
#endif
struct Interplotars {
    float4 position : SV_POSITION;
#if defined(_SHADOW_NEED_UV)
    float2 uv      : TEXCOORD0;
#endif
#if defined(SHADOWS_CUBE)
    float3 lightVec : TEXCOORD1;
#endif
};

實現宏_SHADOW_NEED_UV ,須要傳遞uv座標時就採樣io

#if defined(_SHADOW_NEED_UV)
        i.uv = TRANSFORM_TEX(v.uv, _MainTex);
#endif

拷貝GetAlpha函數,把厲害的宏替換爲_SHADOW_NEED_UV

//採樣alpha
float GetAlpha(Interpolators i) {
    float alpha = _Tint.a;
#if defined(_SHADOW_NEED_UV)
    alpha *= tex2D(_MainTex, i.uv.xy).a;
#endif
    return alpha;
}

如今能夠在fragment程序中獲取alpha值,而後在Cutout渲染模式下使用clip裁切。

    float alpha = GetAlpha(i);
#if defined(_RENDERING_CUTOUT)
    clip(alpha - _AlphaCutOff);
#endif

image

圖1.2 鏤空陰影

2 局部陰影

爲了同時支持Fade和Transparent渲染模式的陰影,必須將其關鍵字添加到shadowCaster通道的着色器鍾並定義ShaderFeature。

#pragma shader_feature _ _RENDERING_CUTOUT _RENDERING_FADE _RENDERING_TRANSPARENT

在Fade和Transparent模式下陰影也應該是半透明的,定義宏SHADOWS_SEMITRANSPARENT

#if defined(_RENDERING_FADE) || defined(_RENDERING_TRANSPARENT)
    #define SHADOWS_SEMITRANSPARENT 1
#endif

再次調整Shadeow_need_uv

#if SHADOWS_SEMITRANSPARENT || defined(_RENDERING_CUTOUT)
    #if !defined(_SMOOTHNESS_ALBEDO)
        #define SHADOWS_NEED_UV 1
    #endif
#endif

這個時候因爲是使用了Clip,還不能正確投射半透明陰影。繼續!

2.1 抖動陰影

        陰影貼圖包含了光線到表面的距離。光線阻擋結果信息存在陰影貼圖,存儲的結果是:0或1。所以是沒有辦法指定光被半透明表面部分阻擋
        目前能作到的就是將陰影表面的一部分剪掉(鏤空)。這就是爲鏤空陰影所作的。可是,除了基於閾值進行裁剪外,咱們還能夠裁剪片斷。例如,若是一個表面讓一半的光經過,並經過使用特定抖動紋理裁剪全部其餘片斷。就能夠把生成的陰影顯示爲完整陰影的一半。
依靠紋理的alpha值,咱們可使用帶有更多或更少孔的圖案,就不會出現重複的模式。並且,若是咱們混合使用這些模式,則能夠對陰影的密度進行平滑過渡。基本上,咱們僅使用兩種狀態來近似漸變。這種技術被稱爲抖動。
        Unity包含一個抖動模式圖集,咱們可使用。它包含16種4×4像素的不一樣圖案。它從一個徹底空的模式開始。每一個連續的模式填充一個額外的像素,直到有七個像素被填充。而後模式被反轉和反轉,直到全部像素都被填充。

2.2 VPOS

       要對咱們的陰影應用抖動模式,咱們必須對其進行採樣。 不能使用網格的UV座標,由於它們在陰影空間中不一致。 相反,咱們須要使用片斷的屏幕空間座標。 陰影是從光的視角角度渲染貼圖的,這會使圖案與陰影貼圖對齊。

        片斷的屏幕空間座標能夠在片元程序中訪問,方法是添加一個帶有VPOS語義的參數。這些座標不是由頂點程序顯式輸出的,但GPU能夠程序用使用它們。

        然而不幸的是,VPOS和SV_POSITION語義,在一些平臺上,它們最終可能會映射到相同的語義位置。因此咱們不能同時在咱們的結構體中使用它們。幸運的是,咱們只須要在頂點程序中使用SV_POSITION,而VPOS只須要在fragment程序中使用。因此分別爲Vertex和Fragment程序定義一個單獨的結構體。

2.3 抖動處理

        要訪問Unity的抖動模式紋理,須要增長變量_DitherMaskLOD,不一樣模式存儲在3D紋理的不一樣layer中,必須使用sampler3D聲明。

sampler3D _DitherMaskLOD;

        若是須要半透明陰影,就在片元程序採樣該紋理。經過tex3D採樣,參數是_DitherMaskLOD和一個3D座標。因爲該紋理有16種模式,模式選擇由Z座標決定,Z的範圍是0-1。以0.0625增量步進。

#if SHADOWS_SEMITRANSPERANT
    tex3D(_DitherMaskLOD, float3(i.vpos.xy, 0.0625));
#endif

        採樣該紋理的目的是取得改紋理的alpha通道,再使用它減去一個較小值,而後用clip裁剪掉

#if SHADOWS_SEMITRANSPERANT
    float dither = tex3D(_DitherMaskLOD, float3(i.vpos.xy, 0.0625)).a;
    clip(dither – 0.01);
#endif

        要真正看到該模式紋理顯示,須要調整顯示密度大小,這能夠經過將紋理的座標位置乘以0.01來實現的。聚光燈的陰影下觀察它。Z值從0-1,紋理將從不渲染到徹底渲染。

tex3D(_DitherMaskLOD, float3(i.vpos.xy * 0.1, 0.0625))

image image

圖2.1 Dither抖動紋理 0.0625 vs. 0.9375

2.4 近似半透明

       根據tex3D採樣函數,可知Z座標值決定是否要渲染片元。那麼只需將該片元的alpha值與最高LOD層級(15/16=0.9375)相乘,得出該片元alpha所在的LOD層級。

tex3D(_DitherMaskLOD, float3(i.vpos.xy * 0.1, alpha * 0.9375))

image

圖2.2 近似模擬半透

        抖動紋理密度大小能夠縮放VPOS指定的紋理座標實現,Unity使用了0.25值來縮放。

tex3D(_DitherMaskLOD, float3(i.vpos.xy * 0.25, alpha * 0.9375))

image

圖2.3 縮放後

         縮放後效果還行但不完美,這取決於抖動紋理的分辨率。越高的分辨率填充的越密集,效果越好。同時,抖動紋理在Hard和Soft陰影模式下的效果也不相同

image image

圖2.4 soft vs. hard

        這個陰影模式在物體移動時有一個視覺錯誤,整個陰影都在動。密集恐懼。

dither_pattern

圖2.5 抖動


3 組合剪影與淡出效果

        可能Dither模式太難堪了,咱們可能既想要投射陰影,也要半透明的效果。這就能夠組合Cutout與Fade渲染。在陰影pass中增長一個新的關鍵字:

_SEMITRANSPARENT_SHADOWS。若沒有啓用淡出半透陰影,回退到裁剪陰影。
#pragma shader_feature _ _SEMITRANSPARENT_SHADOWS

#if defined(_RENDERING_FADE) || defined(_RENDERING_TRANSPARENT)
    //#define SHADOWS_SEMITRANSPARENT 1
    #if _SEMITRANSPARENT_SHADOWS
        #define SHADOWS_SEMITRANSPARENT 1
#elif         #define _RENDERING_CUTOUT
    #endif
#endif

       上面這段代碼加入後,會自動切換爲裁剪陰影。

3.1 啓用自定義關鍵字

3.2 合併顯示

        void DoSimetransparentShadow(RenderMode mode)
        {
            if (mode == RenderMode.Fade || mode == RenderMode.Transparent)
            {
                EditorGUI.BeginChangeCheck();
                bool enable = EditorGUILayout.Toggle
                (
                    new GUIContent("Semitransparent Shadow"),
                    IsKeyEnable("_SEMITRANSPARENT_SHADOWS")
                );
                if(!enable) isShowCutoffAlpha = true;
                if (EditorGUI.EndChangeCheck())
                {
                    SetKeyword("_SEMITRANSPARENT_SHADOWS", enable);
                }
            }
        }

image

相關文章
相關標籤/搜索