Bootstrap

Unity Shader基础(四)——更复杂的光照

        基于内置渲染管线中的不同渲染路径,鬼知道看这一节课踩了多少坑。个人推荐了解即可,因为阴影的处理和多光源光照是一个大坑,不是简简单单就能实现出来的,等以后有机会了再深入了解

        前向渲染路径,延迟渲染路径,顶点照明渲染路径,旧版延迟渲染路径(后两个已经很少用了)。

        指定渲染路径后,Unity才会对对应的Pass填充对应的内置光照变量等等。

一、前向渲染

       渲染原理:  就按前面的光照模型正常渲染。

        多光源的处理:最亮平行光按照Base Pass处理,其余设置为重要的光源按照Additional Pass处理,不过不重要的光源也是放在Base Pass中处理,不懂为什么。光源和物体的数量越多,负担越大,每一次都是从头计算。

        光照衰减:平行光默认为1,即不衰减,其他种类的光源,用Unity内置的光照衰减纹理采样即可,为什么这样采样不知道。

        使用两个Pass,分别渲染不同光源,只含光照衰减。       

        1.1 多光照处理,含光照衰减:

// 前向渲染,光照衰减
Shader "Custom/Test0"
{
    Properties
    {
        _Diffuse ("Diffuse", Color) = (1, 1, 1, 1) 
        _Specular ("Specular", Color) = (1, 1, 1, 1) 
        _Gloss ("Gloss", Range(8, 256)) = 20 
    }

    SubShader
    {
        Tags { "RenderType" = "Opaque" }
        //第一个Pass正常来    
        pass
        {
            Tags { "LightMode" = "ForwardBase" }
            
            CGPROGRAM
            
            // 编译指令,保证在pass中得到Pass中得到正确的光照变量
            #pragma multi_compile_fwdbase

            #pragma vertex vert
            #pragma fragment frag

            #include "Lighting.cginc"

            fixed4 _Diffuse;
            fixed4 _Specular;
            float _Gloss;

            
            struct a2v
            {
                float4 vertex: POSITION; 
                float3 normal: NORMAL; 
            };

            struct v2f
            {
                float4 pos: SV_POSITION; 
                float3 worldNormal: TEXCOORD;
                float3 worldPos: TEXCOORD1;
            };

           
            v2f vert(a2v v)
            {
                v2f o;
               
                o.pos = UnityObjectToClipPos(v.vertex);
                               
                o.worldNormal = UnityObjectToWorldNormal(v.normal);
                
                o.worldPos = mul(unity_ObjectToWorld, v.vertex);

                return o;
            }

            fixed4 frag(v2f i): SV_TARGET
            {
                fixed3 worldNormal = normalize(i.worldNormal);
                
                fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);

                fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * max(0, dot(worldNormal, worldLightDir));

                fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz);

                float3 halfDir = normalize(worldLightDir + viewDir);

                fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0, dot(worldNormal, halfDir)), _Gloss);

                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;

                fixed atten = 1.0;

                return fixed4(ambient + (diffuse + specular) * atten, 1);
            }
            
            ENDCG
            
        }
        //这个Pass计算其他重要光源
        pass
        {
            Tags { "LightMode" = "ForwardAdd" }

            // 开启混合,
            Blend One One
            CGPROGRAM
            
            #pragma multi_compile_fwdadd
            
            #pragma vertex vert
            #pragma fragment frag

            #include "Lighting.cginc"
            #include "AutoLight.cginc"
            
            fixed4 _Diffuse;
            fixed4 _Specular;
            float _Gloss;

            struct a2v
            {
                float4 vertex: POSITION;
                float3 normal: NORMAL;
            };

            struct v2f
            {
                float4 pos: SV_POSITION;
                float3 worldNormal: TEXCOORD0;
                float3 worldPos: TEXCOORD1;
            };

            v2f vert(a2v v)
            {
                v2f o;

                o.pos = UnityObjectToClipPos(v.vertex);

                o.worldNormal = UnityObjectToWorldNormal(v.normal);

                o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;

                return o;
            }

            // 片元着色器
            fixed4 frag(v2f i): SV_TARGET
            {
                fixed3 worldNormal = normalize(i.worldNormal);
            
                //关于光源那个矩阵,unity疯狂报错,有时结果正确,有时错误,不知道是不是自己的问题。
                //计算光源方向
                #ifdef USING_DIRECTIONAL_LIGHT
                    // 平行光
                    fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
                #else
                    // 非平行光
                    fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz - i.worldPos.xyz);
                #endif

                fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb *
                    max(0, dot(worldNormal, worldLightDir));

                fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz);

                fixed3 halfDir = normalize(worldLightDir + viewDir);
                
                fixed3 specular = _LightColor0.rgb * _Specular.rgb *
                            pow(max(0, dot(worldNormal, halfDir)), _Gloss);

                // 计算光照衰减
                #ifdef USING_DIRECTIONAL_LIGHT // 平行光
                    // 平行光,光照衰减不变
                    fixed atten = 1.0;
                #else
                    #if defined(POINT) // 点光源

                        float3 lightCoord = mul(unity_WorldToLight, float4(i.worldPos, 1)).xyz;

                        fixed atten = tex2D(_LightTexture0, dot(lightCoord, lightCoord).rr)
                                    .UNITY_ATTEN_CHANNEL;
                    #elif defined(SPOT) // 聚光灯
                        
                        float4 lightCoord = mul(unity_WorldToLight, float4(i.worldPos, 1));
                       
                        fixed atten = (lightCoord.z > 0) * tex2D(_LightTexture0, lightCoord.xy
                            / lightCoord.w + 0.5).w * tex2D(_LightTextureB0,
                                dot(lightCoord, lightCoord).rr).UNITY_ATTEN_CHANNEL;
                    #else
                        fixed atten = 1.0;
                    #endif
                #endif

                // 纹理采样方法可以减少计算衰减时的复杂度,但有时也可以使用纯数学公式计算光照衰减
                // float distance = length(_WorldSpaceLightPos0.xyz - i.worldPos.xyz);
                // float atten = 1.0 / distance;

                return fixed4((diffuse + specular) * atten, 1.0);
            }
            ENDCG
        }
    }
}

       1.2:阴影处理

                阴影分为投射阴影和接受阴影。

                投射阴影:简单的说将物体相关信息输出到阴影映射纹理。阴影投射也会受到物体正反面的影响,有时需要在Mesh Renderer设置双面,Addtional Pass按书上的说法也无法投射阴影,不知道怎么回事。

Pass
        {
            Tags{"LightMode"="ShadowCaster"}
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile_shadowcaster
            #include "UnityCG.cginc"
            struct v2f
            {
                V2F_SHADOW_CASTER;
            };
            v2f vert(appdata_base v)
            {
                v2f o;
                TRANSFER_SHADOW_CASTER_NORMALOFFSET(o);
                return o;
            }
            fixed4 frag(v2f i):SV_Target
            {
                SHADOW_CASTER_FRAGMENT(i);
            }
            ENDCG
        }

                接受阴影:根据阴影映射纹理采样结果计算。                

                接受阴影使用的内置宏需要开发者定义的变量名和Unity使用的变量名相同,否则会报错。

// 前向渲染,光照衰减
Shader "Custom/Test0"
{
    Properties
    {
        _Diffuse ("Diffuse", Color) = (1, 1, 1, 1) 
        _Specular ("Specular", Color) = (1, 1, 1, 1) 
        _Gloss ("Gloss", Range(8, 256)) = 20 
    }

    SubShader
    {
        Tags { "RenderType" = "Opaque" }
        Pass
        {
            Tags{"LightMode"="ShadowCaster"}
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile_shadowcaster
            #include "UnityCG.cginc"
            struct v2f
            {
                V2F_SHADOW_CASTER;
            };
            v2f vert(appdata_base v)
            {
                v2f o;
                TRANSFER_SHADOW_CASTER_NORMALOFFSET(o);
                return o;
            }
            fixed4 frag(v2f i):SV_Target
            {
                SHADOW_CASTER_FRAGMENT(i);
            }
            ENDCG
        }
        pass
        {
            Tags { "LightMode" = "ForwardBase" }
            
            CGPROGRAM
            
            // 编译指令,保证在pass中得到Pass中得到正确的光照变量
            #pragma multi_compile_fwdbase

            #pragma vertex vert
            #pragma fragment frag

            #include "Lighting.cginc"
            #include "AutoLight.cginc"

            fixed4 _Diffuse;
            fixed4 _Specular;
            float _Gloss;

            
            struct a2v
            {
                float4 vertex: POSITION; 
                float3 normal: NORMAL; 
            };

            struct v2f
            {
                float4 pos: SV_POSITION; 
                float3 worldNormal: TEXCOORD;
                float3 worldPos: TEXCOORD1;
                SHADOW_COORDS(2)
            };

           
            v2f vert(a2v v)
            {
                v2f o;
               
                o.pos = UnityObjectToClipPos(v.vertex);
                               
                o.worldNormal = UnityObjectToWorldNormal(v.normal);
                
                o.worldPos = mul(unity_ObjectToWorld, v.vertex);
                TRANSFER_SHADOW(o);
                return o;
            }

            fixed4 frag(v2f i): SV_TARGET
            {
                fixed3 worldNormal = normalize(i.worldNormal);
                
                fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);

                fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * max(0, dot(worldNormal, worldLightDir));

                fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz);

                float3 halfDir = normalize(worldLightDir + viewDir);

                fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0, dot(worldNormal, halfDir)), _Gloss);

                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;

                fixed atten = 1.0;
                fixed shadow=SHADOW_ATTENUATION(i);
                return fixed4(ambient+(diffuse+specular)*atten*shadow,1);
            }
            
            ENDCG
            
        }

        pass
        {
            Tags { "LightMode" = "ForwardAdd" }

            // 开启混合,
            Blend One One
            CGPROGRAM
            
            #pragma multi_compile_fwdadd
            
            #pragma vertex vert
            #pragma fragment frag

            #include "Lighting.cginc"
            #include "AutoLight.cginc"
            
            fixed4 _Diffuse;
            fixed4 _Specular;
            float _Gloss;

            struct a2v
            {
                float4 vertex: POSITION;
                float3 normal: NORMAL;
            };

            struct v2f
            {
                float4 pos: SV_POSITION;
                float3 worldNormal: TEXCOORD0;
                float3 worldPos: TEXCOORD1;
            };

            v2f vert(a2v v)
            {
                v2f o;

                o.pos = UnityObjectToClipPos(v.vertex);

                o.worldNormal = UnityObjectToWorldNormal(v.normal);

                o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;

                return o;
            }

            // 片元着色器
            fixed4 frag(v2f i): SV_TARGET
            {
                fixed3 worldNormal = normalize(i.worldNormal);

                #ifdef USING_DIRECTIONAL_LIGHT
                    // 平行光
                    fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
                #else
                    // 非平行光
                    fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz - i.worldPos.xyz);
                #endif

                fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb *
                    max(0, dot(worldNormal, worldLightDir));

                fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz);

                fixed3 halfDir = normalize(worldLightDir + viewDir);
                
                fixed3 specular = _LightColor0.rgb * _Specular.rgb *
                            pow(max(0, dot(worldNormal, halfDir)), _Gloss);

                // 计算光照衰减
                #ifdef USING_DIRECTIONAL_LIGHT // 平行光
                    // 平行光,光照衰减不变
                    fixed atten = 1.0;
                #else
                    #if defined(POINT) // 点光源

                        float3 lightCoord = mul(unity_WorldToLight, float4(i.worldPos, 1)).xyz;

                        fixed atten = tex2D(_LightTexture0, dot(lightCoord, lightCoord).rr)
                                    .UNITY_ATTEN_CHANNEL;
                    #elif defined(SPOT) // 聚光灯
                        
                        float4 lightCoord = mul(unity_WorldToLight, float4(i.worldPos, 1));
                       
                        fixed atten = (lightCoord.z > 0) * tex2D(_LightTexture0, lightCoord.xy
                            / lightCoord.w + 0.5).w * tex2D(_LightTextureB0,
                                dot(lightCoord, lightCoord).rr).UNITY_ATTEN_CHANNEL;
                    #else
                        fixed atten = 1.0;
                    #endif
                #endif

                // 尽管纹理采样方法可以减少计算衰减时的复杂度,有时也可以使用数学公式计算光照衰减:
                // float distance = length(_WorldSpaceLightPos0.xyz - i.worldPos.xyz);
                // float atten = 1.0 / distance;

                return fixed4((diffuse + specular) * atten, 1.0);
            }
            ENDCG
        }
    }
}

        1.3 统一管理光照衰减和接受阴影相关:                

// 前向渲染,光照衰减
Shader "Custom/Test0"
{
    Properties
    {
        _Diffuse ("Diffuse", Color) = (1, 1, 1, 1) 
        _Specular ("Specular", Color) = (1, 1, 1, 1) 
        _Gloss ("Gloss", Range(8, 256)) = 20 
    }

    SubShader
    {
        Tags { "RenderType" = "Opaque" }
        Pass
        {
            Tags{"LightMode"="ShadowCaster"}
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile_shadowcaster
            #include "UnityCG.cginc"
            struct v2f
            {
                V2F_SHADOW_CASTER;
            };
            v2f vert(appdata_base v)
            {
                v2f o;
                TRANSFER_SHADOW_CASTER_NORMALOFFSET(o);
                return o;
            }
            fixed4 frag(v2f i):SV_Target
            {
                SHADOW_CASTER_FRAGMENT(i);
            }
            ENDCG
        }
        pass
        {
            Tags { "LightMode" = "ForwardBase" }
            
            CGPROGRAM
            
            // 编译指令,保证在pass中得到Pass中得到正确的光照变量
            #pragma multi_compile_fwdbase

            #pragma vertex vert
            #pragma fragment frag

            #include "Lighting.cginc"
            #include "AutoLight.cginc"

            fixed4 _Diffuse;
            fixed4 _Specular;
            float _Gloss;

            
            struct a2v
            {
                float4 vertex: POSITION; 
                float3 normal: NORMAL; 
            };

            struct v2f
            {
                float4 pos: SV_POSITION; 
                float3 worldNormal: TEXCOORD;
                float3 worldPos: TEXCOORD1;
                SHADOW_COORDS(2)
            };

           
            v2f vert(a2v v)
            {
                v2f o;
               
                o.pos = UnityObjectToClipPos(v.vertex);
                               
                o.worldNormal = UnityObjectToWorldNormal(v.normal);
                
                o.worldPos = mul(unity_ObjectToWorld, v.vertex);
                TRANSFER_SHADOW(o);
                return o;
            }

            fixed4 frag(v2f i): SV_TARGET
            {
                fixed3 worldNormal = normalize(i.worldNormal);
                
                fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);

                fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * max(0, dot(worldNormal, worldLightDir));

                fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz);

                float3 halfDir = normalize(worldLightDir + viewDir);

                fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0, dot(worldNormal, halfDir)), _Gloss);

                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
                
                UNITY_LIGHT_ATTENUATION(atten,i,i.worldPos);
   
                return fixed4(ambient+(diffuse+specular)*atten,1);
            }
            
            ENDCG
            
        }

        pass
        {
            Tags { "LightMode" = "ForwardAdd" }

            // 开启混合,
            Blend One One
            CGPROGRAM
            
            #pragma multi_compile_fwdadd            
            
            #pragma vertex vert
            #pragma fragment frag

            #include "Lighting.cginc"
            #include "AutoLight.cginc"
            
            fixed4 _Diffuse;
            fixed4 _Specular;
            float _Gloss;

            struct a2v
            {
                float4 vertex: POSITION;
                float3 normal: NORMAL;
            };

            struct v2f
            {
                float4 pos: SV_POSITION;
                float3 worldNormal: TEXCOORD0;
                float3 worldPos: TEXCOORD1;
                SHADOW_COORDS(2)
            };

            v2f vert(a2v v)
            {
                v2f o;

                o.pos = UnityObjectToClipPos(v.vertex);

                o.worldNormal = UnityObjectToWorldNormal(v.normal);

                o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
                TRANSFER_SHADOW(o);
                return o;
            }

            // 片元着色器
            fixed4 frag(v2f i): SV_TARGET
            {
                fixed3 worldNormal = normalize(i.worldNormal);

                #ifdef USING_DIRECTIONAL_LIGHT
                    // 平行光
                    fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
                #else
                    // 非平行光
                    fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz - i.worldPos.xyz);
                #endif

                fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb *
                    max(0, dot(worldNormal, worldLightDir));

                fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz);

                fixed3 halfDir = normalize(worldLightDir + viewDir);
                
                fixed3 specular = _LightColor0.rgb * _Specular.rgb *
                            pow(max(0, dot(worldNormal, halfDir)), _Gloss);

                UNITY_LIGHT_ATTENUATION(atten,i,i.worldPos);
   
                return fixed4((diffuse+specular)*atten,1);                

                return fixed4((diffuse + specular) * atten, 1.0);
            }
            ENDCG
        }
    }
}

                关于透明物体阴影,只提一点生成阴影时要考虑物体的透明通道即可,Unity有内置宏,可以用FallBack的方式,我的建议是直接不看。

二、延迟渲染

        原理大致就是只有两个Pass,第一个Pass只计算那些片元可见,可见然后放到G缓冲区,之后由第二个Pass计算,也就是说,不和上面的那种分光源计算,所有光源都用一个Pass。

没有例子。

;