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做测试

未使用高级阴影:
Unity3D The Blacksmith 角色阴影技术使用心得_第1张图片

使用高级阴影:
Unity3D The Blacksmith 角色阴影技术使用心得_第2张图片

这样就能做出媲美ue4的高级阴影来了。

你可能感兴趣的:(unity,shader学习)