Lv.2 Unity主线: 添加阴影

阴影的必要代码

PassTag

需要声明前向渲染ForwardBase,这是声明给程序端C++看的

1
2
3
Tags{
      "LightMode" = "ForwardBase"
    }

pragma指令

编译变体fwdbase,也就多次编译不同的Shdaer版本,这里是声明构成前向渲染专用变体,这和Tags的声明不同,它是给Shdaer编译器看的

1
#pragma multi_compile_fwdbase

Include文件

只需要阴影的话Lighting可以不引入也行,不过都写上就完了

1
2
3
4
#include "UnityCG.cginc"
#include "Lighting.cginc"
// 阴影宏定义在这里
#include "AutoLight.cginc"             

appdata结构体声明要求

之前写的物体空间的顶点posOS必须换成vertex。原因在下面将 图片

v2f结构体声明要求

这里在结构体中声明阴影数据,注意括号中的顺序号 图片

1
2
3
4
float4 pos : SV_POSITION;
----
// 阴影坐标声明(自动处理多个变体)
UNITY_SHADOW_COORDS(4)

这里需要额外说明一点,裁剪空间下的顶点位置,必须命名为pos.同时这也是这是物体空间的顶点posOS必须换成vertex的原因。AutoLight.cginc文件下Unity阴影函数里面固定的,其他名称会报错 图片

顶点着色器

阴影数据处理,直接系统函数 图片

1
2
//传输阴影数据(关键!)
TRANSFER_SHADOW(o);

片元着色器

计算阴影衰减,输出结果和diffuse乘法计算就行,注意不要和环境光相乘 这里需要说明一点,阴影在Shader中分为两个内容,一个是接收其它的阴影,一个是投射自己的阴影 图片

1
2
 // 计算阴影衰减(自动处理所有阴影类型)
fixed shadow = SHADOW_ATTENUATION(i);

投射阴影Pass

基本上是固定写法,如果是半透明裁剪一类的,需要自己做裁剪

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
Pass
        {
            Name"ShadowCaster"
            Tags{
                "LightMode" = "ShadowCaster"
            }
             CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            
            // 阴影投射需要的变体
            #pragma multi_compile_shadowcaster
            
            #include "UnityCG.cginc"
            
            struct appdata
            {
                float4 vertex : POSITION;
                float3 normal : NORMAL;
            };
            
            struct v2f
            {
                V2F_SHADOW_CASTER;  // Unity预定义的结构
            };
            
            v2f vert(appdata v)
            {
                v2f o;
                TRANSFER_SHADOW_CASTER_NORMALOFFSET(o)  // 转换到阴影空间
                return o;
            }
            
            fixed4 frag(v2f i) : SV_Target
            {
                //裁剪处理位置clip()
                SHADOW_CASTER_FRAGMENT(i)  // 写入阴影深度
            }
            ENDCG
        }
      

如果不想写这个Pass,可以直接在末尾加上 FallBack “Diffuse”,但是没有自己写性能好 图片

代码参考

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
Shader "Unlit/AddShadow"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100

        Pass
        {
           Name "Forward"
           Tags{
            "LightMode" = "ForwardBase"
           }
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            // make fog work
            
            #pragma multi_compile_fwdbase

            #include "UnityCG.cginc"
            #include "Lighting.cginc"
            #include "AutoLight.cginc"             // 1.阴影宏定义在这里

            struct appdata
            {
                float4 posOS     : POSITION;
                float2 uv        : TEXCOORD0;
                float3 normalOS  : NORMAL;
                float4 tangentOS : TANGENT;
            };

            struct v2f
            {
                float4 pos : SV_POSITION;
                float2 texcoord : TEXCOORD0;
                float3 posWS : TEXCOORD1;
                float3 normalWS :TEXCOORD2;
                float3 tangentWS: TEXCOORD3;
                // 阴影坐标声明(自动处理多个变体)
                UNITY_SHADOW_COORDS(4) 
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;

            v2f vert (appdata v)
            {
                v2f o;
                o.pos = UnityObjectToClipPos(v.posOS);
                o.texcoord = TRANSFORM_TEX(v.uv, _MainTex);
                o.posWS = mul(unity_ObjectToWorld, v.posOS);
                o.normalWS = UnityObjectToWorldNormal(v.normalOS);
                o.tangentWS = UnityObjectToWorldDir(v.tangentOS);
                // 传输阴影数据(关键!)
                TRANSFER_SHADOW(o);
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                // sample the texture
                fixed4 col = tex2D(_MainTex, i.texcoord);

                // 计算阴影衰减(自动处理所有阴影类型)
                fixed shadow = SHADOW_ATTENUATION(i);

                return col *shadow;
            }
            ENDCG
        }
        Pass
        {
            Name"ShadowCaster"
            Tags{
                "LightMode" = "ShadowCaster"
            }
             CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            
            // 阴影投射需要的变体
            #pragma multi_compile_shadowcaster
            
            #include "UnityCG.cginc"
            
            struct appdata
            {
                float4 vertex : POSITION;
                float3 normal : NORMAL;
            };
            
            struct v2f
            {
                V2F_SHADOW_CASTER;  // Unity预定义的结构
            };
            
            v2f vert(appdata v)
            {
                v2f o;
                TRANSFER_SHADOW_CASTER_NORMALOFFSET(o)  // 转换到阴影空间
                return o;
            }
            
            fixed4 frag(v2f i) : SV_Target
            {
                SHADOW_CASTER_FRAGMENT(i)  // 写入阴影深度
            }
            ENDCG
        }
        
    }
    FallBack "Diffuse"
}




    Enjoy Reading This Article?

    Here are some more articles you might like to read next:

  • URP - RendererFeature :ScreenSpaceOutline
  • 平滑法线处理 - 八面体映射
  • Lv.3 Unity主线:一个简单的PBRShader
  • 理论支线:直接光漫反射与GGX高光的混合问题
  • 理论支线:PBR - 基于图像的照明( image based lighting-IBL)
  • # #