Shader 光照

Created by miccall (转载请注明出处 miccall.tech)

目录:

  • 基础光照 : 漫反射 , 高光反射
  • 光照叠加 : 衰减渐变 , 阴影 , 使用unity内置实现的衰减和阴影
  • 结合贴图的光照 : 基础,法线,渐变,遮罩纹理,折射, 反射 , Fresnel反射
  • 卡通: 反射, 高光
  • PBR基础
  • 理论深入
  • unity标准shader

一 . 基础光照

分为漫反射和高光反射,其中漫反射光照模型为 Lambert 模型 和 half Lambert 模型 。 Lambert模型分成逐顶点和逐像素两种写法 。高光反射分为phong 和 blinn-PHong 也是分为 逐顶点和逐像素两种写法 。

左起 第一个 和 第二个是只有漫反射光的物体,分别是逐顶点和逐像素的写法,在简单模型中,几乎看不出区别。
第三个为改进的漫反射光照,他会显得更亮,应为算法使他背后也会受光。
第四个开始加入了Phong高光反射,对比第四个和第五个,明显后者会更圆滑,而前者顶点明显突出,棱次感强。
第六个使用的是blinn phong 模型 ,他的高光范围在同等级上,会比Phong显得更大,更亮。

1 . 漫反射

//逐顶点Lambert     
Shader "miccall/Lighting/lambert_pre_vert"
{
    Properties
    {
        _MainTex ("main" ,2D) = "white"{}
        _LightFactor("Intency",Range(1,3)) = 1.0
        _Color ("color" ,Color) = (1,1,1,1)
    }
    SubShader
    {
    Tags { "RenderType"="Opaque" }  
    LOD 300  
    Pass
    {
        Tags { "LightMode"="ForwardBase"}
        CGPROGRAM
        #pragma vertex vert
        #pragma fragment frag
        // UnityObjectToWorldNormal helper function
        #include "UnityCG.cginc"
        #include "Lighting.cginc"
        struct appdata {
            float4 vertex : POSITION ;
            float3 normal : NORMAL;
        };
        struct v2f{
            float4 pos : SV_POSITION ; 
            fixed4 color : COLOR ;
        };
        fixed4 _Color ; 
        float _LightFactor;
        v2f vert (appdata v ) 
        {
            v2f o ;
            o.pos  = UnityObjectToClipPos(v.vertex);
            fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz; 
            fixed3 worldNormal = normalize(UnityObjectToWorldNormal(v.normal));
            fixed3 worldLight = normalize(_WorldSpaceLightPos0.xyz);
            fixed3 lambert = _LightFactor * _LightColor0.rgb * _Color.rgb * saturate(dot(worldNormal , worldLight ));
            o.color = fixed4(ambient+lambert , 1.0 );
            return o ; 
        }
        fixed4 frag (v2f i) : SV_Target
        {
            fixed4 color = i.color ;
            //color.rgb = i.worldNormal * 0.5 + 0.5 ;
            return color ;
        }
        ENDCG
    }
    }
    Fallback "Diffuse"
}
//逐像素lambert
Shader "miccall/Lighting/lambert_pre_pixel"
{
    Properties
    {
        _MainTex ("main" ,2D) = "white"{}
        _LightFactor("Intency",Range(1,3)) = 1.0
        _Color ("color" ,Color) = (1,1,1,1)
    }
    SubShader
    {
        Tags { "LightMode"="ForwardBase"}
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            // UnityObjectToWorldNormal helper function
            #include "UnityCG.cginc"
            #include "Lighting.cginc"
            struct appdata {
                float4 vertex : POSITION ;
                float3 normal : NORMAL;
            };
            struct v2f{
                float4 pos : SV_POSITION ; 
                float3 worldNormal : TEXCOORD0;
            };
            fixed4 _Color ; 
            float _LightFactor;
            v2f vert (appdata v ) 
            {
                v2f o ;
                o.pos  = UnityObjectToClipPos(v.vertex);
                o.worldNormal = UnityObjectToWorldNormal(v.normal);

                return o ; 
            }
            fixed4 frag (v2f i) : SV_Target
            {
                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
                fixed3 worldNormal = normalize( i.worldNormal );
                fixed3 worldLight = normalize(_WorldSpaceLightPos0.xyz);
                fixed3 lambert = _LightFactor * _LightColor0.rgb * _Color.rgb * saturate(dot(worldNormal , worldLight ));
                fixed4 color =  fixed4( ambient +lambert,1.0);
                return color ;
            }
            ENDCG
        }
    }
    Fallback "Diffuse"
}

可以看到,lambert 模型有一半的值为0 所以背部都是黑色,而半lambert只映射了一半,他就显得更加渐变光滑。

//逐像素的 lambert_half
Shader "miccall/Lighting/lambert_half"
{
    Properties
    {
        _MainTex ("main" ,2D) = "white"{}
        _LightFactor("Intency",Range(1,3)) = 1.0
        _Color ("color" ,Color) = (1,1,1,1)
    }
    SubShader
    {
        Tags { "LightMode"="ForwardBase"}
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            // UnityObjectToWorldNormal helper function
            #include "UnityCG.cginc"
            #include "Lighting.cginc"
            struct appdata {
                float4 vertex : POSITION ;
                float3 normal : NORMAL;
            };
            struct v2f{
                float4 pos : SV_POSITION ; 
                float3 worldNormal : TEXCOORD0;
            };
            fixed4 _Color ; 
            float _LightFactor;
            v2f vert (appdata v ) 
            {
                v2f o ;
                o.pos  = UnityObjectToClipPos(v.vertex);
                o.worldNormal = UnityObjectToWorldNormal(v.normal);
                return o ; 
            }
            fixed4 frag (v2f i) : SV_Target
            {
                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
                fixed3 worldNormal = normalize(i.worldNormal) ;
                fixed3 worldLight = normalize(_WorldSpaceLightPos0.xyz);
                fixed3 half_lambert = _LightFactor * _LightColor0.rgb * _Color.rgb * dot(worldNormal , worldLight )*0.5+0.5;
                fixed4 color =  fixed4( ambient +half_lambert,1.0);
                return color ;
            }
            ENDCG
        }
    }
    Fallback "Diffuse"
}

2. 高光反射

phong和blinn phong的计算方式,只是把VR 换成了NH
R的计算方法相对H来说比较复杂。

//Phong 高光模型 逐顶点
Shader "miccall/Lighting/Specular_pre_vert"
{
    Properties
    {
        _MainTex ("main" ,2D) = "white"{}
        _LightFactor("Intency",Range(1,3)) = 1.0
        _Color ("color" ,Color) = (1,1,1,1)
        _Specular("Specular",Color) = (1,1,1,1)
        _Gloss("Specular_Gloss" , Range(8,256)) = 20 
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }  
        LOD 300  
    Pass
    {
        Tags { "LightMode"="ForwardBase"}
        CGPROGRAM
        #pragma vertex vert
        #pragma fragment frag
        // UnityObjectToWorldNormal helper function
        #include "UnityCG.cginc"
        #include "Lighting.cginc"
        struct appdata {
            float4 vertex : POSITION ;
            float3 normal : NORMAL;
        };
        struct v2f{
            float4 pos : SV_POSITION ; 
            fixed4 color : COLOR ;
        };
        fixed4 _Color ; 
        float _LightFactor;
        fixed4 _Specular ;
        float _Gloss ;
        v2f vert (appdata v ) 
        {
            v2f o ;
            o.pos  = UnityObjectToClipPos(v.vertex);
            fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
            fixed3 worldNormal = normalize(UnityObjectToWorldNormal(v.normal));
            fixed3 worldLight = normalize(_WorldSpaceLightPos0.xyz);
            fixed3 lambert = _LightFactor * _LightColor0.rgb * _Color.rgb * saturate(dot(worldNormal , worldLight ));

            fixed3 refDir = normalize( reflect(-worldLight,worldNormal) );
            fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - mul(unity_ObjectToWorld , v.vertex ).xyz);
            fixed3 Specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(refDir , viewDir)),_Gloss);
            o.color = fixed4( ambient + lambert + Specular , 1.0 );
            return o ; 
        }
        fixed4 frag (v2f i) : SV_Target
        {
            fixed4 color = i.color ;
            //color.rgb = i.worldNormal * 0.5 + 0.5 ;
            return color ;
        }
        ENDCG
    }
    }
    Fallback "Diffuse"
}
//逐像素
Shader "miccall/Lighting/Specular_pre_pixel_phong"
{
    Properties
    {
        _MainTex ("main" ,2D) = "white"{}
        _LightFactor("Intency",Range(1,3)) = 1.0
        _Color ("color" ,Color) = (1,1,1,1)
        _Specular("Specular",Color) = (1,1,1,1)
        _Gloss("Specular_Gloss" , Range(8,256)) = 20 
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }  
        LOD 300  
    Pass
    {
        Tags { "LightMode"="ForwardBase"}
        CGPROGRAM
        #pragma vertex vert
        #pragma fragment frag
        // UnityObjectToWorldNormal helper function
        #include "UnityCG.cginc"
        #include "Lighting.cginc"
        struct appdata {
            float4 vertex : POSITION ;
            float3 normal : NORMAL;
        };
        struct v2f{
            float4 pos : SV_POSITION ; 
            fixed3 worldNormal : TEXCOORD0;
            fixed4 worldpos : TEXCOORD1 ;
    };
        fixed4 _Color ; 
        float _LightFactor;
        fixed4 _Specular ;
        float _Gloss ;
        v2f vert (appdata 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 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
            fixed3 worldLight = normalize(_WorldSpaceLightPos0.xyz);
        fixed3 worldNormal = normalize(i.worldNormal);
            fixed3 lambert = _LightFactor * _LightColor0.rgb * _Color.rgb * saturate(dot(worldNormal , worldLight ));
            fixed3 refDir = normalize(reflect(-worldLight, worldNormal));
            fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldpos.xyz );
            fixed3 Specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(refDir , viewDir)),_Gloss);
            fixed4 color = fixed4( ambient + lambert + Specular , 1.0 );

            //color.rgb = i.worldNormal * 0.5 + 0.5 ;
            return color ;
        }
        ENDCG
        }
    }
    Fallback "Diffuse"
}
//Blinn-Phong 高光模型
Shader "miccall/Lighting/Specular_pre_pixel_Blinn_phong"
{
    Properties
    {
        _MainTex ("main" ,2D) = "white"{}
        _LightFactor("Intency",Range(1,3)) = 1.0
        _Color ("color" ,Color) = (1,1,1,1)
        _Specular("Specular",Color) = (1,1,1,1)
        _Gloss("Specular_Gloss" , Range(8,256)) = 20 
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }  
        LOD 300  
    Pass
    {
        Tags { "LightMode"="ForwardBase"}
        CGPROGRAM
        #pragma vertex vert
        #pragma fragment frag
        // UnityObjectToWorldNormal helper function
        #include "UnityCG.cginc"
        #include "Lighting.cginc"
        struct appdata {
            float4 vertex : POSITION ;
            float3 normal : NORMAL;
        };
        struct v2f{
            float4 pos : SV_POSITION ; 
            fixed3 worldNormal : TEXCOORD0;
            fixed4 worldpos : TEXCOORD1 ;
        };

        fixed4 _Color ; 
        float _LightFactor;
        fixed4 _Specular ;
        float _Gloss ;
        inline fixed4 UnityObjectToWorldPos( float4 vertex )
        {
            return mul(unity_ObjectToWorld , vertex ) ;
        }
        v2f vert (appdata v ) 
        {
            v2f o ;
            o.pos  = UnityObjectToClipPos(v.vertex);
            o.worldNormal = UnityObjectToWorldNormal( v.normal );
            o.worldpos = UnityObjectToWorldPos( v.vertex ) ;
            return o ; 
        }
        fixed4 frag (v2f i) : SV_Target
        {
            fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
            fixed3 worldLight = normalize(UnityWorldSpaceLightDir(i.worldpos));
            fixed3 worldNormal = normalize(i.worldNormal);
            fixed3 lambert = _LightFactor * _LightColor0.rgb * _Color.rgb * saturate(dot(worldNormal , worldLight ));
            //fixed3 refDir = normalize(reflect(-worldLight,i.worldNormal));
            fixed3 viewDir = normalize( UnityWorldSpaceViewDir(i.worldpos));
            fixed3 half_lv = normalize(worldLight + viewDir); 
            fixed3 Specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(worldNormal , half_lv )),_Gloss);
            fixed4 color = fixed4( ambient + lambert + Specular , 1.0 );
            return color ;
        }
        ENDCG
    }
    }
    Fallback "Diffuse"
}

一 . 光照叠加

1. 光照衰减渐变

unity场景中,默认有一个平行光的,而这个光是没有位置属性的,而在前向渲染中,平行光可以被看作为没有衰减,他在物体上的光照强度总是一个定值,没有渐变效果。
所以我们针对不同的光,得做不同的计算。

点光源的强度是由位置和强度决定的,而聚光灯还与他的照射方向有关。我们得用两个渲染通道,第一次处理环境光,和最亮的平行光,第二个混合第一次的光源,在上面做叠加效果。

Shader "miccall/Lighting/Specular_Forward_Rendering_Blinn_phong"
{
    Properties
    {
        _MainTex ("main" ,2D) = "white"{}
        _LightFactor("Intency",Range(1,3)) = 1.0
        _Color ("color" ,Color) = (1,1,1,1)
        _Specular("Specular",Color) = (1,1,1,1)
        _Gloss("Specular_Gloss" , Range(8,256)) = 20 
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        Pass
        {
            Tags { "LightMode"="ForwardBase"}

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            // 光照衰减 
            #pragma multi_compile_fwdbase 

            #include "UnityCG.cginc"
            #include "Lighting.cginc"

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

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


            fixed4 _Color ; 
            float _LightFactor;
            fixed4 _Specular ;
            float _Gloss ;

            inline fixed4 UnityObjectToWorldPos( float4 vertex )
            {
                return mul(unity_ObjectToWorld , vertex ) ;
            }

            v2f vert (appdata v ) 
            {
                v2f o ;
                o.pos  = UnityObjectToClipPos(v.vertex);
                o.worldNormal = UnityObjectToWorldNormal(v.normal);
                o.worldpos = UnityObjectToWorldPos( v.vertex ).xyz ;
                return o ; 
            }

            fixed4 frag (v2f i) : SV_Target
            {

                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
                // L
                fixed3 worldLight = normalize(UnityWorldSpaceLightDir(i.worldpos));
                // N
                fixed3 worldNormal = normalize(i.worldNormal);
                // 漫反射
                fixed3 lambert =  _LightColor0.rgb * _Color.rgb * saturate( dot( worldNormal , worldLight ));
                // R 
                fixed3 refDir =  normalize( reflect( -worldLight , i.worldNormal ) );
                // V 
                fixed3 viewDir = normalize( UnityWorldSpaceViewDir(i.worldpos) );
                // H 
                fixed3 half_lv = normalize( worldLight+viewDir ) ; 
                // 高光反射 
                fixed3 Specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(worldNormal , half_lv ) ), _Gloss );
                // 光照强度 
                fixed atten = 1.0 ; 

                // 最终颜色 
                fixed4 color = fixed4( ambient + (lambert + Specular) * atten  , 1.0 );
                return color ;
            }
            ENDCG
        }
        Pass
        {
            Tags{"LightMode"="ForwardAdd"}
            // 光照混合 
            Blend One One 
            CGPROGRAM
            #pragma multi_compile_fwdadd
            #pragma vertex vert
            #pragma fragment frag 
            #include "Lighting.cginc"
            #include "UnityCG.cginc"
            #include "AutoLight.cginc"

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

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

            fixed4 _Color ; 
            float _LightFactor;
            fixed4 _Specular ;
            float _Gloss ;

            inline fixed4 UnityObjectToWorldPos( float4 vertex )
            {
                return mul(unity_ObjectToWorld , vertex ) ;
            }

            v2f vert(appdata v)
            {
                v2f o ; 
                o.pos = UnityObjectToClipPos(v.vertex ) ; 
                o.worldNormal = UnityObjectToWorldNormal(v.normal);
                o.worldpos = UnityObjectToWorldPos(v.vertex ).xyz ;
                return o ;
            }

            fixed4 frag(v2f i ) : SV_Target 
            {
                fixed3 worldNormal = normalize ( i.worldNormal );

                // 如果是 DIRECTIONAL_LIGHT : 
                #ifdef USING_DIRECTIONAL_LIGHT
                    fixed3 worldLight = normalize( _WorldSpaceLightPos0.xyz );
                #else
                    fixed3 worldLight = normalize(_WorldSpaceLightPos0.xyz - i.worldpos.xyz);
                #endif

                fixed3 lambert = _LightColor0.rgb * _Color.rgb * saturate(dot(worldNormal , worldLight ));
                fixed3 refDir = normalize(reflect(-worldLight,i.worldNormal));
                fixed3 viewDir = normalize( UnityWorldSpaceViewDir(i.worldpos));
                fixed3 half_lv = normalize(worldLight+viewDir); 
                fixed3 Specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(worldNormal , half_lv )),_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

                return fixed4((lambert + Specular) * atten, 1.0);
            }
            ENDCG
        }
    }
    Fallback "Specular"
}

第一个pass很好理解,我们没有做大多的改变,只是在最后,漫反射光和高光乘了一个atten系数,而这个系数的值为1 ,所以没有什么作用。而第二个pass,我们做了很多判断,比如判断光源类型。以及不同光源的位置计算,衰减强度计算。

重点看看强度计算方法 :

  1. 点光源 :直接计算他的强度范围显然计算量很大,unity为我们提供了一个_LightTexture0 的变量,这个变量他是一个二维纹理,我们只需要对它进行取样(使用光源空间到点到光源中心的距离平方来作为采样坐标),不同的点就会得到一个不同的衰减值,在坐标0处值为1,在坐标1处值为0,这意味着,当距离光源越近,atten值越接近于1。这样我们看到的物体就会有一个渐变光照。

  2. 聚光灯 :聚光灯和点光源的区别就是,它不仅仅只和位置距离有关,他还与角度和张角有关。
    前面说过,我们通过采样纹理对他进行强度计算。他的纹理被存储在_LightTextureB0中,然而,他还与点光源类似,所以不能忘记_LightTexture0的采样。

通过(lightCoord.z > 0)判断光源是否照射到,
张角计算思路是,如果和光源空间的中心轴(+z轴)刚好重合,那么这个衰减值应该是1,而在聚光灯的张角边缘处,这个值因该是0。

这样我们通过 lightCoord.xy / lightCoord.w + 0.5 进行的坐标计算,在_LightTextureB0 中采样的点,就是一个范围了。由此就不难看出,在张角中心,即坐标0.5处衰减值为1,而在两侧是接近0的

2. 反射阴影


由图可以看到,我们之前写的shader ,被一个物体挡住后,他是不能产生阴影的。
要产生阴影效果,其实在unity中实现很简单。

Shader "miccall/Lighting/Shadow_Blinn_phong"
{
    Properties
    {
        _MainTex ("main" ,2D) = "white"{}
        _LightFactor("Intency",Range(1,3)) = 1.0
        _Color ("color" ,Color) = (1,1,1,1)
        _Specular("Specular",Color) = (1,1,1,1)
        _Gloss("Specular_Gloss" , Range(8,256)) = 20 
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        Pass
        {
            Tags { "LightMode"="ForwardBase"}

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            // 光照衰减 
            #pragma multi_compile_fwdbase 

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

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

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


            fixed4 _Color ; 
            float _LightFactor;
            fixed4 _Specular ;
            float _Gloss ;

            inline fixed4 UnityObjectToWorldPos( float4 vertex )
            {
                return mul(unity_ObjectToWorld , vertex ) ;
            }

            v2f vert (appdata v ) 
            {
                v2f o ;
                o.pos  = UnityObjectToClipPos(v.vertex);
                o.worldNormal = UnityObjectToWorldNormal(v.normal);
                o.worldpos = UnityObjectToWorldPos( v.vertex ).xyz ;
                TRANSFER_SHADOW(o);
                return o ; 
            }

            fixed4 frag (v2f i) : SV_Target
            {

                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
                // L
                fixed3 worldLight = normalize(UnityWorldSpaceLightDir(i.worldpos));
                // N
                fixed3 worldNormal = normalize(i.worldNormal);
                // 漫反射
                fixed3 lambert =  _LightColor0.rgb * _Color.rgb * saturate( dot( worldNormal , worldLight ));
                // R 
                fixed3 refDir =  normalize( reflect( -worldLight , i.worldNormal ) );
                // V 
                fixed3 viewDir = normalize( UnityWorldSpaceViewDir(i.worldpos) );
                // H 
                fixed3 half_lv = normalize( worldLight+viewDir ) ; 
                // 高光反射 
                fixed3 Specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(worldNormal , half_lv ) ), _Gloss );
                // 光照强度 
                fixed atten = 1.0 ; 
                fixed shadow = SHADOW_ATTENUATION(i);
                // 最终颜色 
                fixed4 color = fixed4( ambient + (lambert + Specular) * atten  * shadow , 1.0 );
                return color ;
            }
            ENDCG
        }
        Pass
        {
            Tags{"LightMode"="ForwardAdd"}
            // 光照混合 
            Blend One One 
            CGPROGRAM
            #pragma multi_compile_fwdadd
            #pragma vertex vert
            #pragma fragment frag 
            #include "Lighting.cginc"
            #include "UnityCG.cginc"
            #include "AutoLight.cginc"

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

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

            fixed4 _Color ; 
            float _LightFactor;
            fixed4 _Specular ;
            float _Gloss ;

            inline fixed4 UnityObjectToWorldPos( float4 vertex )
            {
                return mul(unity_ObjectToWorld , vertex ) ;
            }

            v2f vert(appdata v)
            {
                v2f o ; 
                o.pos = UnityObjectToClipPos(v.vertex ) ; 
                o.worldNormal = UnityObjectToWorldNormal(v.normal);
                o.worldpos = UnityObjectToWorldPos(v.vertex ).xyz ;
                return o ;
            }

            fixed4 frag(v2f i ) : SV_Target 
            {
                fixed3 worldNormal = normalize ( i.worldNormal );

                // 如果是 DIRECTIONAL_LIGHT : 
                #ifdef USING_DIRECTIONAL_LIGHT
                    fixed3 worldLight = normalize( _WorldSpaceLightPos0.xyz );
                #else
                    fixed3 worldLight = normalize(_WorldSpaceLightPos0.xyz - i.worldpos.xyz);
                #endif

                fixed3 lambert = _LightColor0.rgb * _Color.rgb * saturate(dot(worldNormal , worldLight ));
                fixed3 refDir = normalize(reflect(-worldLight,i.worldNormal));
                fixed3 viewDir = normalize( UnityWorldSpaceViewDir(i.worldpos));
                fixed3 half_lv = normalize(worldLight+viewDir); 
                fixed3 Specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(worldNormal , half_lv )),_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

                return fixed4((lambert + Specular) * atten, 1.0);
            }
            ENDCG
        }
    }
    Fallback "Specular"
}

第二个Pass没有改变,第一个pass中,添加了几段定义。

3.综合以上

当然,看懂最好,但这一标准也被unity内置了,我们使用unity内部的定义,也可以达到以上这些( 光照衰减和阴影投射 )的效果。

Shader "miccall/Lighting/UnityLightShadow"
{
    Properties
    {
        _MainTex ("main" ,2D) = "white"{}
        _LightFactor("Intency",Range(1,3)) = 1.0
        _Color ("color" ,Color) = (1,1,1,1)
        _Specular("Specular",Color) = (1,1,1,1)
        _Gloss("Specular_Gloss" , Range(8,256)) = 20 
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        Pass
        {
            Tags { "LightMode"="ForwardBase"}

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            // 光照衰减 
            #pragma multi_compile_fwdbase 

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

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

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


            fixed4 _Color ; 
            float _LightFactor;
            fixed4 _Specular ;
            float _Gloss ;

            inline fixed4 UnityObjectToWorldPos( float4 vertex )
            {
                return mul(unity_ObjectToWorld , vertex ) ;
            }

            v2f vert (appdata v ) 
            {
                v2f o ;
                o.pos  = UnityObjectToClipPos(v.vertex);
                o.worldNormal = UnityObjectToWorldNormal(v.normal);
                o.worldpos = UnityObjectToWorldPos( v.vertex ).xyz ;
                TRANSFER_SHADOW(o);
                return o ; 
            }

            fixed4 frag (v2f i) : SV_Target
            {

                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
                // L
                fixed3 worldLight = normalize(UnityWorldSpaceLightDir(i.worldpos));
                // N
                fixed3 worldNormal = normalize(i.worldNormal);
                // 漫反射
                fixed3 lambert =  _LightColor0.rgb * _Color.rgb * saturate( dot( worldNormal , worldLight ));
                // R 
                fixed3 refDir =  normalize( reflect( -worldLight , i.worldNormal ) );
                // V 
                fixed3 viewDir = normalize( UnityWorldSpaceViewDir(i.worldpos) );
                // H 
                fixed3 half_lv = normalize( worldLight+viewDir ) ; 
                // 高光反射 
                fixed3 Specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(worldNormal , half_lv ) ), _Gloss );

                UNITY_LIGHT_ATTENUATION(atten, i, i.worldpos);

                fixed4 color = fixed4( ambient + (lambert + Specular) * atten  , 1.0 );
                return color ;
            }
            ENDCG
        }
        Pass
        {
            Tags{"LightMode"="ForwardAdd"}
            // 光照混合 
            Blend One One 
            CGPROGRAM
            #pragma multi_compile_fwdadd
            #pragma vertex vert
            #pragma fragment frag 
            #include "Lighting.cginc"
            #include "UnityCG.cginc"
            #include "AutoLight.cginc"

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

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

            fixed4 _Color ; 
            float _LightFactor;
            fixed4 _Specular ;
            float _Gloss ;

            inline fixed4 UnityObjectToWorldPos( float4 vertex )
            {
                return mul(unity_ObjectToWorld , vertex ) ;
            }

            v2f vert(appdata v)
            {
                v2f o ; 
                o.pos = UnityObjectToClipPos(v.vertex ) ; 
                o.worldNormal = UnityObjectToWorldNormal(v.normal);
                o.worldpos = UnityObjectToWorldPos(v.vertex ).xyz ;
                TRANSFER_SHADOW(o);
                return o ;
            }

            fixed4 frag(v2f i ) : SV_Target 
            {
                fixed3 worldNormal = normalize ( i.worldNormal );
                fixed3 worldLight = normalize(UnityWorldSpaceLightDir(i.worldpos));
                fixed3 lambert = _LightColor0.rgb * _Color.rgb * saturate(dot(worldNormal , worldLight ));
                fixed3 refDir = normalize(reflect(-worldLight,i.worldNormal));
                fixed3 viewDir = normalize( UnityWorldSpaceViewDir(i.worldpos));
                fixed3 half_lv = normalize(worldLight+viewDir); 
                fixed3 Specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(worldNormal , half_lv )),_Gloss);

                UNITY_LIGHT_ATTENUATION(atten, i, i.worldpos);

                return fixed4((lambert + Specular) * atten, 1.0);
            }
            ENDCG
        }
    }
    Fallback "Specular"
}

3. 结合贴图的光照

1. 基础纹理

Shader "miccall/Lighting2/TextureWithLight"
{
    Properties
    {
        _MainTex ("main" ,2D) = "white"{}
        _LightFactor("Intency",Range(1,3)) = 1.0
        _Color ("color" ,Color) = (1,1,1,1)
        _Specular("Specular",Color) = (1,1,1,1)
        _Gloss("Specular_Gloss" , Range(8,256)) = 20 
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }  
        LOD 300  
        Pass
        {
            Tags { "LightMode"="ForwardBase"}
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            #include "Lighting.cginc"

            struct appdata {
                float4 vertex : POSITION ;
                float3 normal : NORMAL;
                float4 UVcoord :  TEXCOORD0 ;
            };

            struct v2f{
                float4 pos : SV_POSITION ; 
                fixed3 worldNormal : TEXCOORD0;
                fixed4 worldpos : TEXCOORD1 ;
                float2 uv : TEXCOORD2;
            };

            sampler2D _MainTex ;
            float4 _MainTex_ST ;

            fixed4 _Color ; 
            float _LightFactor;
            fixed4 _Specular ;
            float _Gloss ;

            inline fixed4 UnityObjectToWorldPos( float4 vertex )
            {
                return mul(unity_ObjectToWorld , vertex ) ;
            }

            v2f vert (appdata v ) 
            {
                v2f o ;
                o.pos  = UnityObjectToClipPos(v.vertex);
                o.worldNormal = UnityObjectToWorldNormal(v.normal);
                o.worldpos = UnityObjectToWorldPos( v.vertex ) ;
                o.uv = v.UVcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw ; 
                return o ; 
            }

            fixed4 frag (v2f i) : SV_Target
            {
                fixed3 albedo = tex2D(_MainTex , i.uv).rgb * _Color.rgb ;
                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo*3 ;
                fixed3 worldLight = normalize(UnityWorldSpaceLightDir(i.worldpos));
                fixed3 worldNormal = normalize(i.worldNormal);
                fixed3 lambert = _LightFactor * _LightColor0.rgb * _Color.rgb * saturate(dot(worldNormal , worldLight ));
                fixed3 refDir = normalize(reflect(-worldLight,i.worldNormal));
                fixed3 viewDir = normalize( UnityWorldSpaceViewDir(i.worldpos));
                fixed3 half_lv = normalize(worldLight+viewDir); 
                fixed3 Specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(worldNormal , half_lv )),_Gloss);
                fixed4 color = fixed4( ambient + (lambert + Specular)*0.5 , 1.0 );
                return color ;
            }
            ENDCG
        }
    }
    Fallback "Specular"
}

2. 增加法线贴图

Shader "miccall/Lighting2/LightwithTexandNormal"
{
    Properties
    {
        _MainTex ("main" ,2D) = "white"{}
        _BumpMap ("Normal Map", 2D) = "bump" {}
        _BumpScale ("Bump Scale", Float) = 1.0
        _LightFactor("Intency",Range(1,3)) = 1.0
        _Color ("color" ,Color) = (1,1,1,1)
        _Specular("Specular",Color) = (1,1,1,1)
        _Gloss("Specular_Gloss" , Range(8,256)) = 20 
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }  
        LOD 300  
        Pass
        {
            Tags { "LightMode"="ForwardBase"}
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            #include "Lighting.cginc"

            struct appdata {
                float4 vertex : POSITION ;
                float3 normal : NORMAL;
                float4 tangent : TANGENT;
                float4 UVcoord :  TEXCOORD0 ;
            };

            struct v2f{
                float4 pos : SV_POSITION ; 
                float4 uv : TEXCOORD0;
                float4 TtoW0 : TEXCOORD1;  
                float4 TtoW1 : TEXCOORD2;  
                float4 TtoW2 : TEXCOORD3;

            };

            sampler2D _MainTex ;
            float4 _MainTex_ST ;
            sampler2D _BumpMap ;
            float4 _BumpMap_ST;
            float _BumpScale ;

            fixed4 _Color ; 
            float _LightFactor;
            fixed4 _Specular ;
            float _Gloss ;

            inline fixed4 UnityObjectToWorldPos( float4 vertex )
            {
                return mul(unity_ObjectToWorld , vertex ) ;
            }

            v2f vert (appdata v ) 
            {
                v2f o ;
                o.pos  = UnityObjectToClipPos(v.vertex);

                float3 worldPos = UnityObjectToWorldPos( v.vertex ).xyz;  
                fixed3 worldNormal = UnityObjectToWorldNormal(v.normal);  
                fixed3 worldTangent = UnityObjectToWorldDir(v.tangent.xyz);  
                fixed3 worldBinormal = cross(worldNormal, worldTangent) * v.tangent.w; 

                o.TtoW0 = float4(worldTangent.x, worldBinormal.x, worldNormal.x, worldPos.x);
                o.TtoW1 = float4(worldTangent.y, worldBinormal.y, worldNormal.y, worldPos.y);
                o.TtoW2 = float4(worldTangent.z, worldBinormal.z, worldNormal.z, worldPos.z);

                o.uv.xy = v.UVcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;
                o.uv.zw = v.UVcoord.xy * _BumpMap_ST.xy + _BumpMap_ST.zw;
                return o ; 
            }

            fixed4 frag (v2f i) : SV_Target
            {
                float3 worldPos = float3(i.TtoW0.w, i.TtoW1.w, i.TtoW2.w);
                fixed3 worldLight = normalize(UnityWorldSpaceLightDir(worldPos));
                fixed3 viewDir = normalize( UnityWorldSpaceViewDir(worldPos));
                fixed3 bump = UnpackNormal(tex2D(_BumpMap, i.uv.zw));
                bump.xy *= _BumpScale;
                bump.z = sqrt(1.0 - saturate(dot(bump.xy, bump.xy)));
                bump = normalize(half3(dot(i.TtoW0.xyz, bump), dot(i.TtoW1.xyz, bump), dot(i.TtoW2.xyz, bump)));

                fixed3 albedo = tex2D(_MainTex , i.uv).rgb * _Color.rgb ;
                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo*3 ;


                fixed3 lambert = _LightFactor * _LightColor0.rgb * _Color.rgb * saturate(dot( bump , worldLight ));

                fixed3 half_lv = normalize(worldLight+viewDir); 
                fixed3 Specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(bump , half_lv )),_Gloss);
                fixed4 color = fixed4( ambient + (lambert + Specular)*0.5 , 1.0 );
                return color ;
            }
            ENDCG
        }
    }
    Fallback "Specular"
}

法线贴图中,他存储的向量范围是-1 到 1 ,而在shader中,贴图的颜色一般也是(0,1)区间,所以,我们在计算时只需要把从法线贴图中采样得到的法线值进行映射,将其从(0,1)区间转化到(-1,1)区间。
计算公式就是

pixel = (normal + 1 ) / 2 ;  或者 
nomarl = pixel * 2 - 1 ;

但这个公式被unity封装了一下,使用UnpackNormal函数就可以得到法线向量信息。

// UnityCG.cginc
inline fixed3 UnpackNormalDXT5nm (fixed4 packednormal)  
{  
    fixed3 normal;  
    normal.xy = packednormal.wy * 2 - 1;  
    normal.z = sqrt(1 - saturate(dot(normal.xy, normal.xy)));  
    return normal;  
}  

inline fixed3 UnpackNormal(fixed4 packednormal)  
{  
#if defined(UNITY_NO_DXT5nm)  
    return packednormal.xyz * 2 - 1;  
#else  
    return UnpackNormalDXT5nm(packednormal);  
#endif  
}

3.渐变纹理控制漫反射强度

Shader "miccall/Lighting2/LightwithRamp"
{
    Properties
    {
        _Color ("color" ,Color) = (1,1,1,1)
        _RampTex ("Ramp Tex", 2D) = "white" {}
        _Specular("Specular",Color) = (1,1,1,1)
        _Gloss("Specular_Gloss" , Range(8,256)) = 20 
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }  
        LOD 300  
        Pass
        {
            Tags { "LightMode"="ForwardBase"}
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            #include "Lighting.cginc"

            struct appdata {
                float4 vertex : POSITION ;
                float3 normal : NORMAL;
                float4 UVcoord :  TEXCOORD0 ;
            };

            struct v2f{
                float4 pos : SV_POSITION ;
                float2 uv : TEXCOORD0;
                fixed3 worldPos : TEXCOORD1 ;
                fixed3 worldNormal : TEXCOORD2 ;
            };

            sampler2D _RampTex;
            float4 _RampTex_ST;
            fixed4 _Color ; 
            fixed4 _Specular ;
            float _Gloss ;

            inline fixed4 UnityObjectToWorldPos( float4 vertex )
            {
                return mul(unity_ObjectToWorld , vertex ) ;
            }

            v2f vert (appdata v ) 
            {
                v2f o ;
                o.pos  = UnityObjectToClipPos(v.vertex);
                o.worldPos = UnityObjectToWorldPos( v.vertex ).xyz;  
                o.worldNormal = UnityObjectToWorldNormal(v.normal);  
                o.uv = TRANSFORM_TEX(v.UVcoord, _RampTex);
                return o ; 
            }

            fixed4 frag (v2f i) : SV_Target
            {
                float3 worldPos = normalize(i.worldPos);
                fixed3 worldLight = normalize(UnityWorldSpaceLightDir(worldPos));
                fixed3 viewDir = normalize( UnityWorldSpaceViewDir(worldPos));
                fixed3 worldNormal = normalize(i.worldNormal);
                fixed3 half_lv = normalize( worldLight+viewDir );  

                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz ;

                fixed  halflambert = 0.5 * dot( worldNormal , worldLight ) + 0.5 ;
                fixed3 RampColor = _LightColor0.rgb * _Color.rgb * tex2D(_RampTex,fixed2( halflambert , halflambert ) ).rgb ;
                fixed3 Specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot ( worldNormal , half_lv ) ),_Gloss);
                fixed4 color = fixed4( ambient + ( RampColor + Specular ) * 0.5 , 1.0 );
                return color ;
            }
            ENDCG
        }
    }
    Fallback "Specular"
}

4. 遮罩纹理控制高光反射