76.Unity自带全局雾效

76.深度和法线纹理-效果实现-深度纹理实现全局雾效-Unity自带全局雾效


76.1 知识点

主要讲解内容

全局雾效是什么

Unity自带的全局雾效





修改标准漫反射Shader产生雾效

复制Lesson20_标准光照着色器_标准漫反射的shader Lesson20_BumpedDiffuse

Shader "Unlit/Lesson76_BumpedDiffuse"
{
    Properties
    {
        _MainColor("MainColor", Color) = (1,1,1,1)//  漫反射颜色 
        _MainTex("MainTex", 2D) = ""{}//  单张纹理
        _BumpMap("BumpMap", 2D) = ""{}//  法线纹理
        _BumpScale("BumpScale", Range(0,1)) = 1//  凹凸程度
        //        _SpecularColor("SpecularColor", Color) = (1,1,1,1)//  高光反射颜色
        //        _SpecularNum("SpecularNum", Range(0,20)) = 18//  光泽度
    }
    SubShader
    {
        //  加入渲染标签Tags { "RenderType"="Opaque" "Queue"="Geometry"}
        //  渲染类型设置为不透明的、渲染队列设置为几何队列(不透明的几何体通常使用该队列)
        Tags
        {
            "RenderType"="Opaque" "Queue"="Geometry"
        }

        //Base Pass
        Pass
        {
            Tags
            {
                "LightMode"="ForwardBase"
            }
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            // 用于帮助我们编译所有变体 并且保证衰减相关光照变量能够正确赋值到对应的内置变量中
            #pragma multi_compile_fwdbase

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

            //使用阴影宏内置文件
            #include "AutoLight.cginc"

            float4 _MainColor; //漫反射颜色
            sampler2D _MainTex; //颜色纹理
            float4 _MainTex_ST; //颜色纹理的缩放和平移
            sampler2D _BumpMap; //法线纹理
            float4 _BumpMap_ST; //法线纹理的缩放和平移
            float _BumpScale; //凹凸程度
            // float4 _SpecularColor; //高光颜色
            // fixed _SpecularNum; //光泽度

            struct v2f
            {
                //裁剪空间下坐标
                float4 pos:SV_POSITION;

                //纹理uv

                //我们可以单独的声明两个float2的成员用于记录 颜色和法线纹理的uv坐标
                //float2 uvTex:TEXCOORD0;//颜色纹理
                //float2 uvBump:TEXCOORD1;//法线纹理

                //也可以直接声明一个float4的成员 xy用于记录颜色纹理的uv,zw用于记录法线纹理的uv
                float4 uv:TEXCOORD0; //纹理变量


                //顶点相对于世界坐标的位置 主要用于 之后的 视角方向的计算
                //float3 worldPos:TEXCOORD1;

                //切线 到 世界空间的 变换矩阵
                //float3x3 rotation:TEXCOORD2;

                //代表我们切线空间到世界空间的 变换矩阵的3行 三个变量的w存世界坐标
                float4 TtoW0:TEXCOORD1;
                float4 TtoW1:TEXCOORD2;
                float4 TtoW2:TEXCOORD3;


                //阴影坐标宏
                //n为下一个可用的插值寄存器的索引值(结构体前面有几个TEXCOORD就填几)
                //比如这里前面有四个
                SHADOW_COORDS(4)
            };

            v2f vert(appdata_full v)
            {
                v2f v2f;

                //把模型空间下的顶点转到裁剪空间下
                v2f.pos = UnityObjectToClipPos(v.vertex);

                //计算纹理的缩放偏移
                v2f.uv.xy = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;
                v2f.uv.zw = v.texcoord.xy * _BumpMap_ST.xy + _BumpMap_ST.zw;

                //得到世界空间下的 顶点位置 用于之后在片元函数中计算视角方向(世界空间下的)
                //v2f.worldPos = mul(unity_ObjectToWorld, appdata_full.vertex);
                float3 worldPos = mul(unity_ObjectToWorld, v.vertex);

                //把模型空间下的法线、切线转换到世界空间下
                float3 worldNormal = UnityObjectToWorldNormal(v.normal);
                float3 worldTangent = UnityObjectToWorldDir(v.tangent);

                //计算副切线 计算叉乘结果后 垂直与切线和法线的向量有两条 通过乘以 切线当中的w,就可以确定是哪一条
                //cross是叉乘 appdata_full.tangent.w代表叉乘结果方向
                float3 worldBinormal = cross(normalize(worldTangent), normalize(worldNormal)) * v.tangent.w;

                //这个就是我们 切线空间到世界空间的 转换矩阵
                //       |    |     |
                //      切线 副切线 法线
                //       |    |     |
                //v2f.rotation = float3x3( worldTangent.x, worldBinormal.x,  worldNormal.x,
                //                          worldTangent.y, worldBinormal.y,  worldNormal.y,
                //                          worldTangent.z, worldBinormal.z,  worldNormal.z);
                v2f.TtoW0 = float4(worldTangent.x, worldBinormal.x, worldNormal.x, worldPos.x);
                v2f.TtoW1 = float4(worldTangent.y, worldBinormal.y, worldNormal.y, worldPos.y);
                v2f.TtoW2 = float4(worldTangent.z, worldBinormal.z, worldNormal.z, worldPos.z);

                //坐标转换宏
                TRANSFER_SHADOW(v2f)

                return v2f;
            }

            fixed4 frag(v2f v2f) : SV_Target
            {
                //世界空间下光的方向
                fixed3 lightDir = normalize(_WorldSpaceLightPos0.xyz);

                //世界空间下视角方向
                // fixed3 viewDir = normalize(UnityWorldSpaceViewDir(v2f.worldPos));
                float3 worldPos = float3(v2f.TtoW0.w, v2f.TtoW1.w, v2f.TtoW2.w);
                fixed3 viewDir = normalize(UnityWorldSpaceViewDir(worldPos));

                //纹理采样函数tex2D
                //通过纹理采样函数 取出法线纹理贴图当中的数据
                float4 packedNormal = tex2D(_BumpMap, v2f.uv.zw);

                //利用内置的UnpackNormal函数对法线信息进行逆运算以及可能的解压
                //将我们取出来的法线数据 进行逆运算并且可能会进行解压缩的运算,最终得到切线空间下的法线数据
                float3 tangentNormal = UnpackNormal(packedNormal);

                //乘以凹凸程度的系数
                tangentNormal.xy *= _BumpScale;
                tangentNormal.z = sqrt(1.0 - saturate(dot(tangentNormal.xy, tangentNormal.xy)));


                //把计算完毕后的切线空间下的法线转换到世界空间下
                // float3 worldNormal = mul(v2f.rotation, tangentNormal);

                //以下两种写法等价 但是可以一步到位
                //float3x3 rotation = float3x3(v2f.TtoW0.xyz, v2f.TtoW1.xyz, v2f.TtoW2.xyz );
                //float3 worldNormal = mul(rotation, tangentNormal);
                //本质 就是在进行矩阵运算
                float3 worldNormal = float3(dot(v2f.TtoW0.xyz, tangentNormal), dot(v2f.TtoW1.xyz, tangentNormal),
                                                dot(v2f.TtoW2.xyz, tangentNormal));


                //接下来就来处理 带颜色纹理的 布林方光照模型计算

                //颜色纹理和漫反射颜色的 叠加
                fixed3 albedo = tex2D(_MainTex, v2f.uv.xy) * _MainColor.rgb;

                //兰伯特漫反射颜色 = 光的颜色 * 漫反射材质的颜色 * max(0, dot(世界坐标系下的法线, 光的方向))
                fixed3 lambertColor = _LightColor0.rgb * albedo.rgb * max(0, dot(worldNormal, normalize(lightDir)));

                //不使用高光了
                // //半角向量 = 视角方向 + 光的方向
                // float3 halfA = normalize(normalize(viewDir) + normalize(lightDir));
                //
                // //高光反射的颜色 = 光的颜色 * 高光反射材质的颜色 * pow(max(0, dot(世界坐标系下的法线, 半角向量)), 光泽度)
                // fixed3 specularColor = _LightColor0.rgb * _SpecularColor.rgb * pow(
                //     max(0, dot(worldNormal, halfA)), _SpecularNum);

                //计算光照衰减和阴影衰减的宏
                UNITY_LIGHT_ATTENUATION(atten, v2f, worldPos);

                // //布林方光照颜色 = 环境光颜色 + 兰伯特漫反射颜色 + 高光反射的颜色
                // fixed3 color = UNITY_LIGHTMODEL_AMBIENT.rgb * albedo + lambertColor + specularColor;

                //布林方光照颜色 = 环境光颜色 + 兰伯特漫反射颜色*衰减值
                fixed3 color = UNITY_LIGHTMODEL_AMBIENT.rgb * albedo + lambertColor * atten;

                return fixed4(color.rgb, 1);
            }
            ENDCG
        }

        //Additional Pass
        Pass
        {
            //设置我们的光照模式 ForwardAdd这种向前渲染模式 主要是用来处理 附加光照渲染的
            Tags
            {
                "LightMode"="ForwardAdd"
            }

            //线性减淡的效果 进行 光照颜色混合
            Blend One One

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            //这个编译指令让Unity生成多个包括支持和不支持阴影的Shader变体
            //从而为额外的逐像素光源计算阴影,并传递给Shader了
            #pragma multi_compile_fwdadd_fullshadows

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

            //使用阴影宏内置文件
            #include "AutoLight.cginc"

            float4 _MainColor; //漫反射颜色
            sampler2D _MainTex; //颜色纹理
            float4 _MainTex_ST; //颜色纹理的缩放和平移
            sampler2D _BumpMap; //法线纹理
            float4 _BumpMap_ST; //法线纹理的缩放和平移
            float _BumpScale; //凹凸程度
            // float4 _SpecularColor; //高光颜色
            // fixed _SpecularNum; //光泽度

            struct v2f
            {
                //裁剪空间下坐标
                float4 pos:SV_POSITION;

                //纹理uv

                //我们可以单独的声明两个float2的成员用于记录 颜色和法线纹理的uv坐标
                //float2 uvTex:TEXCOORD0;//颜色纹理
                //float2 uvBump:TEXCOORD1;//法线纹理

                //也可以直接声明一个float4的成员 xy用于记录颜色纹理的uv,zw用于记录法线纹理的uv
                float4 uv:TEXCOORD0; //纹理变量


                //顶点相对于世界坐标的位置 主要用于 之后的 视角方向的计算
                //float3 worldPos:TEXCOORD1;

                //切线 到 世界空间的 变换矩阵
                //float3x3 rotation:TEXCOORD2;

                //代表我们切线空间到世界空间的 变换矩阵的3行 三个变量的w存世界坐标
                float4 TtoW0:TEXCOORD1;
                float4 TtoW1:TEXCOORD2;
                float4 TtoW2:TEXCOORD3;


                //阴影坐标宏
                //n为下一个可用的插值寄存器的索引值(结构体前面有几个TEXCOORD就填几)
                //比如这里前面有四个
                SHADOW_COORDS(4)
            };

            v2f vert(appdata_full v)
            {
                v2f v2f;

                //把模型空间下的顶点转到裁剪空间下
                v2f.pos = UnityObjectToClipPos(v.vertex);

                //计算纹理的缩放偏移
                v2f.uv.xy = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;
                v2f.uv.zw = v.texcoord.xy * _BumpMap_ST.xy + _BumpMap_ST.zw;

                //得到世界空间下的 顶点位置 用于之后在片元函数中计算视角方向(世界空间下的)
                //v2f.worldPos = mul(unity_ObjectToWorld, appdata_full.vertex);
                float3 worldPos = mul(unity_ObjectToWorld, v.vertex);

                //把模型空间下的法线、切线转换到世界空间下
                float3 worldNormal = UnityObjectToWorldNormal(v.normal);
                float3 worldTangent = UnityObjectToWorldDir(v.tangent);

                //计算副切线 计算叉乘结果后 垂直与切线和法线的向量有两条 通过乘以 切线当中的w,就可以确定是哪一条
                //cross是叉乘 appdata_full.tangent.w代表叉乘结果方向
                float3 worldBinormal = cross(normalize(worldTangent), normalize(worldNormal)) * v.tangent.w;

                //这个就是我们 切线空间到世界空间的 转换矩阵
                //       |    |     |
                //      切线 副切线 法线
                //       |    |     |
                //v2f.rotation = float3x3( worldTangent.x, worldBinormal.x,  worldNormal.x,
                //                          worldTangent.y, worldBinormal.y,  worldNormal.y,
                //                          worldTangent.z, worldBinormal.z,  worldNormal.z);
                v2f.TtoW0 = float4(worldTangent.x, worldBinormal.x, worldNormal.x, worldPos.x);
                v2f.TtoW1 = float4(worldTangent.y, worldBinormal.y, worldNormal.y, worldPos.y);
                v2f.TtoW2 = float4(worldTangent.z, worldBinormal.z, worldNormal.z, worldPos.z);

                //坐标转换宏
                TRANSFER_SHADOW(v2f)

                return v2f;
            }

            fixed4 frag(v2f v2f) : SV_Target
            {
                //世界空间下光的方向
                fixed3 lightDir = normalize(_WorldSpaceLightPos0.xyz);

                //世界空间下视角方向
                // fixed3 viewDir = normalize(UnityWorldSpaceViewDir(v2f.worldPos));
                float3 worldPos = float3(v2f.TtoW0.w, v2f.TtoW1.w, v2f.TtoW2.w);
                fixed3 viewDir = normalize(UnityWorldSpaceViewDir(worldPos));

                //纹理采样函数tex2D
                //通过纹理采样函数 取出法线纹理贴图当中的数据
                float4 packedNormal = tex2D(_BumpMap, v2f.uv.zw);

                //利用内置的UnpackNormal函数对法线信息进行逆运算以及可能的解压
                //将我们取出来的法线数据 进行逆运算并且可能会进行解压缩的运算,最终得到切线空间下的法线数据
                float3 tangentNormal = UnpackNormal(packedNormal);

                //乘以凹凸程度的系数
                tangentNormal.xy *= _BumpScale;
                tangentNormal.z = sqrt(1.0 - saturate(dot(tangentNormal.xy, tangentNormal.xy)));


                //把计算完毕后的切线空间下的法线转换到世界空间下
                // float3 worldNormal = mul(v2f.rotation, tangentNormal);

                //以下两种写法等价 但是可以一步到位
                //float3x3 rotation = float3x3(v2f.TtoW0.xyz, v2f.TtoW1.xyz, v2f.TtoW2.xyz );
                //float3 worldNormal = mul(rotation, tangentNormal);
                //本质 就是在进行矩阵运算
                float3 worldNormal = float3(dot(v2f.TtoW0.xyz, tangentNormal), dot(v2f.TtoW1.xyz, tangentNormal),
                                                dot(v2f.TtoW2.xyz, tangentNormal));


                //接下来就来处理 带颜色纹理的 布林方光照模型计算

                //颜色纹理和漫反射颜色的 叠加
                fixed3 albedo = tex2D(_MainTex, v2f.uv.xy) * _MainColor.rgb;

                //兰伯特漫反射颜色 = 光的颜色 * 漫反射材质的颜色 * max(0, dot(世界坐标系下的法线, 光的方向))
                fixed3 lambertColor = _LightColor0.rgb * albedo.rgb * max(0, dot(worldNormal, normalize(lightDir)));

                //不使用高光了
                // //半角向量 = 视角方向 + 光的方向
                // float3 halfA = normalize(normalize(viewDir) + normalize(lightDir));
                //
                // //高光反射的颜色 = 光的颜色 * 高光反射材质的颜色 * pow(max(0, dot(世界坐标系下的法线, 半角向量)), 光泽度)
                // fixed3 specularColor = _LightColor0.rgb * _SpecularColor.rgb * pow(
                //     max(0, dot(worldNormal, halfA)), _SpecularNum);

                //计算光照衰减和阴影衰减的宏
                UNITY_LIGHT_ATTENUATION(atten, v2f, worldPos);

                // //布林方光照颜色 = 环境光颜色 + 兰伯特漫反射颜色 + 高光反射的颜色
                // fixed3 color = UNITY_LIGHTMODEL_AMBIENT.rgb * albedo + lambertColor + specularColor;

                //布林方光照颜色 = 环境光颜色 + 兰伯特漫反射颜色*衰减值
                fixed3 color = UNITY_LIGHTMODEL_AMBIENT.rgb * albedo + lambertColor * atten;

                return fixed4(color.rgb, 1);
            }
            ENDCG
        }
    }

    //Unity自带的漫反射Shader,里面实现了对应投射阴影的Pass
    Fallback "Diffuse"
}

应用雾相关到shader,包括编译指令,结构体宏,顶点转换宏,片元应用宏等

Shader "Unlit/Lesson76_BumpedDiffuse"
{
    Properties
    {
        //...
    }
    SubShader
    {
        //...

        //Base Pass
        Pass
        {
            //...
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            // 雾的编译指令 make fog work
            #pragma multi_compile_fog

            // 用于帮助我们编译所有变体 并且保证衰减相关光照变量能够正确赋值到对应的内置变量中
            #pragma multi_compile_fwdbase

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

            //...

            struct v2f
            {
                //...

                //雾的宏
                UNITY_FOG_COORDS(4)
                
                    //阴影坐标宏
                //n为下一个可用的插值寄存器的索引值(结构体前面有几个TEXCOORD就填几)
                //比如这里前面有四个
                SHADOW_COORDS(5)
            };

            v2f vert(appdata_full v)
            {
                v2f v2f;

                //...
                //雾顶点转换宏
                UNITY_TRANSFER_FOG(v2f,v2f.pos);
                
                //坐标转换宏
                TRANSFER_SHADOW(v2f)

                return v2f;
            }

            fixed4 frag(v2f v2f) : SV_Target
            {
                //...

                // 片元应用雾的宏 apply fog
                UNITY_APPLY_FOG(v2f.fogCoord, color);
                
                return fixed4(color.rgb, 1);
            }
            ENDCG
        }

        //Additional Pass
        Pass
        {
            //...

            //线性减淡的效果 进行 光照颜色混合
            Blend One One

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            // 雾的编译指令 make fog work
            #pragma multi_compile_fog

            //...

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

            //...

            struct v2f
            {
                //...

                //雾的宏
                UNITY_FOG_COORDS(4)
                
                //阴影坐标宏
                //n为下一个可用的插值寄存器的索引值(结构体前面有几个TEXCOORD就填几)
                //比如这里前面有四个
                SHADOW_COORDS(5)
            };

            v2f vert(appdata_full v)
            {
                v2f v2f;

                //...

                //雾顶点转换宏
                UNITY_TRANSFER_FOG(v2f,v2f.pos);
                
                //坐标转换宏
                TRANSFER_SHADOW(v2f)

                return v2f;
            }

            fixed4 frag(v2f v2f) : SV_Target
            {
                //...

                // 片元应用雾的宏 apply fog
                UNITY_APPLY_FOG(v2f.fogCoord, color);
                
                return fixed4(color.rgb, 1);
            }
            ENDCG
        }
    }

    //Unity自带的漫反射Shader,里面实现了对应投射阴影的Pass
    Fallback "Diffuse"
}

创建材质赋值shader给场景中的墙和立方体,修改雾最远距离可以明显看到效果

总结


76.2 知识点代码

Lesson76_BumpedDiffuse.shader

Shader "Unlit/Lesson76_BumpedDiffuse"
{
    Properties
    {
        _MainColor("MainColor", Color) = (1,1,1,1)//  漫反射颜色 
        _MainTex("MainTex", 2D) = ""{}//  单张纹理
        _BumpMap("BumpMap", 2D) = ""{}//  法线纹理
        _BumpScale("BumpScale", Range(0,1)) = 1//  凹凸程度
        //        _SpecularColor("SpecularColor", Color) = (1,1,1,1)//  高光反射颜色
        //        _SpecularNum("SpecularNum", Range(0,20)) = 18//  光泽度
    }
    SubShader
    {
        //  加入渲染标签Tags { "RenderType"="Opaque" "Queue"="Geometry"}
        //  渲染类型设置为不透明的、渲染队列设置为几何队列(不透明的几何体通常使用该队列)
        Tags
        {
            "RenderType"="Opaque" "Queue"="Geometry"
        }

        //Base Pass
        Pass
        {
            Tags
            {
                "LightMode"="ForwardBase"
            }
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            // 雾的编译指令 make fog work
            #pragma multi_compile_fog

            // 用于帮助我们编译所有变体 并且保证衰减相关光照变量能够正确赋值到对应的内置变量中
            #pragma multi_compile_fwdbase

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

            //使用阴影宏内置文件
            #include "AutoLight.cginc"

            float4 _MainColor; //漫反射颜色
            sampler2D _MainTex; //颜色纹理
            float4 _MainTex_ST; //颜色纹理的缩放和平移
            sampler2D _BumpMap; //法线纹理
            float4 _BumpMap_ST; //法线纹理的缩放和平移
            float _BumpScale; //凹凸程度
            // float4 _SpecularColor; //高光颜色
            // fixed _SpecularNum; //光泽度

            struct v2f
            {
                //裁剪空间下坐标
                float4 pos:SV_POSITION;

                //纹理uv

                //我们可以单独的声明两个float2的成员用于记录 颜色和法线纹理的uv坐标
                //float2 uvTex:TEXCOORD0;//颜色纹理
                //float2 uvBump:TEXCOORD1;//法线纹理

                //也可以直接声明一个float4的成员 xy用于记录颜色纹理的uv,zw用于记录法线纹理的uv
                float4 uv:TEXCOORD0; //纹理变量


                //顶点相对于世界坐标的位置 主要用于 之后的 视角方向的计算
                //float3 worldPos:TEXCOORD1;

                //切线 到 世界空间的 变换矩阵
                //float3x3 rotation:TEXCOORD2;

                //代表我们切线空间到世界空间的 变换矩阵的3行 三个变量的w存世界坐标
                float4 TtoW0:TEXCOORD1;
                float4 TtoW1:TEXCOORD2;
                float4 TtoW2:TEXCOORD3;

                //雾的宏
                UNITY_FOG_COORDS(4)
                
                //阴影坐标宏
                //n为下一个可用的插值寄存器的索引值(结构体前面有几个TEXCOORD就填几)
                //比如这里前面有四个
                SHADOW_COORDS(5)
            };

            v2f vert(appdata_full v)
            {
                v2f v2f;

                //把模型空间下的顶点转到裁剪空间下
                v2f.pos = UnityObjectToClipPos(v.vertex);

                //计算纹理的缩放偏移
                v2f.uv.xy = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;
                v2f.uv.zw = v.texcoord.xy * _BumpMap_ST.xy + _BumpMap_ST.zw;

                //得到世界空间下的 顶点位置 用于之后在片元函数中计算视角方向(世界空间下的)
                //v2f.worldPos = mul(unity_ObjectToWorld, appdata_full.vertex);
                float3 worldPos = mul(unity_ObjectToWorld, v.vertex);

                //把模型空间下的法线、切线转换到世界空间下
                float3 worldNormal = UnityObjectToWorldNormal(v.normal);
                float3 worldTangent = UnityObjectToWorldDir(v.tangent);

                //计算副切线 计算叉乘结果后 垂直与切线和法线的向量有两条 通过乘以 切线当中的w,就可以确定是哪一条
                //cross是叉乘 appdata_full.tangent.w代表叉乘结果方向
                float3 worldBinormal = cross(normalize(worldTangent), normalize(worldNormal)) * v.tangent.w;

                //这个就是我们 切线空间到世界空间的 转换矩阵
                //       |    |     |
                //      切线 副切线 法线
                //       |    |     |
                //v2f.rotation = float3x3( worldTangent.x, worldBinormal.x,  worldNormal.x,
                //                          worldTangent.y, worldBinormal.y,  worldNormal.y,
                //                          worldTangent.z, worldBinormal.z,  worldNormal.z);
                v2f.TtoW0 = float4(worldTangent.x, worldBinormal.x, worldNormal.x, worldPos.x);
                v2f.TtoW1 = float4(worldTangent.y, worldBinormal.y, worldNormal.y, worldPos.y);
                v2f.TtoW2 = float4(worldTangent.z, worldBinormal.z, worldNormal.z, worldPos.z);

                //雾顶点转换宏
                UNITY_TRANSFER_FOG(v2f,v2f.pos);
                
                //坐标转换宏
                TRANSFER_SHADOW(v2f)

                return v2f;
            }

            fixed4 frag(v2f v2f) : SV_Target
            {
                //世界空间下光的方向
                fixed3 lightDir = normalize(_WorldSpaceLightPos0.xyz);

                //世界空间下视角方向
                // fixed3 viewDir = normalize(UnityWorldSpaceViewDir(v2f.worldPos));
                float3 worldPos = float3(v2f.TtoW0.w, v2f.TtoW1.w, v2f.TtoW2.w);
                fixed3 viewDir = normalize(UnityWorldSpaceViewDir(worldPos));

                //纹理采样函数tex2D
                //通过纹理采样函数 取出法线纹理贴图当中的数据
                float4 packedNormal = tex2D(_BumpMap, v2f.uv.zw);

                //利用内置的UnpackNormal函数对法线信息进行逆运算以及可能的解压
                //将我们取出来的法线数据 进行逆运算并且可能会进行解压缩的运算,最终得到切线空间下的法线数据
                float3 tangentNormal = UnpackNormal(packedNormal);

                //乘以凹凸程度的系数
                tangentNormal.xy *= _BumpScale;
                tangentNormal.z = sqrt(1.0 - saturate(dot(tangentNormal.xy, tangentNormal.xy)));


                //把计算完毕后的切线空间下的法线转换到世界空间下
                // float3 worldNormal = mul(v2f.rotation, tangentNormal);

                //以下两种写法等价 但是可以一步到位
                //float3x3 rotation = float3x3(v2f.TtoW0.xyz, v2f.TtoW1.xyz, v2f.TtoW2.xyz );
                //float3 worldNormal = mul(rotation, tangentNormal);
                //本质 就是在进行矩阵运算
                float3 worldNormal = float3(dot(v2f.TtoW0.xyz, tangentNormal), dot(v2f.TtoW1.xyz, tangentNormal),
                                             dot(v2f.TtoW2.xyz, tangentNormal));


                //接下来就来处理 带颜色纹理的 布林方光照模型计算

                //颜色纹理和漫反射颜色的 叠加
                fixed3 albedo = tex2D(_MainTex, v2f.uv.xy) * _MainColor.rgb;

                //兰伯特漫反射颜色 = 光的颜色 * 漫反射材质的颜色 * max(0, dot(世界坐标系下的法线, 光的方向))
                fixed3 lambertColor = _LightColor0.rgb * albedo.rgb * max(0, dot(worldNormal, normalize(lightDir)));

                //不使用高光了
                // //半角向量 = 视角方向 + 光的方向
                // float3 halfA = normalize(normalize(viewDir) + normalize(lightDir));
                //
                // //高光反射的颜色 = 光的颜色 * 高光反射材质的颜色 * pow(max(0, dot(世界坐标系下的法线, 半角向量)), 光泽度)
                // fixed3 specularColor = _LightColor0.rgb * _SpecularColor.rgb * pow(
                //     max(0, dot(worldNormal, halfA)), _SpecularNum);

                //计算光照衰减和阴影衰减的宏
                UNITY_LIGHT_ATTENUATION(atten, v2f, worldPos);

                // //布林方光照颜色 = 环境光颜色 + 兰伯特漫反射颜色 + 高光反射的颜色
                // fixed3 color = UNITY_LIGHTMODEL_AMBIENT.rgb * albedo + lambertColor + specularColor;

                //布林方光照颜色 = 环境光颜色 + 兰伯特漫反射颜色*衰减值
                fixed3 color = UNITY_LIGHTMODEL_AMBIENT.rgb * albedo + lambertColor * atten;

                // 片元应用雾的宏 apply fog
                UNITY_APPLY_FOG(v2f.fogCoord, color);
                
                return fixed4(color.rgb, 1);
            }
            ENDCG
        }

        //Additional Pass
        Pass
        {
            //设置我们的光照模式 ForwardAdd这种向前渲染模式 主要是用来处理 附加光照渲染的
            Tags
            {
                "LightMode"="ForwardAdd"
            }

            //线性减淡的效果 进行 光照颜色混合
            Blend One One

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            // 雾的编译指令 make fog work
            #pragma multi_compile_fog

            //这个编译指令让Unity生成多个包括支持和不支持阴影的Shader变体
            //从而为额外的逐像素光源计算阴影,并传递给Shader了
            #pragma multi_compile_fwdadd_fullshadows

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

            //使用阴影宏内置文件
            #include "AutoLight.cginc"

            float4 _MainColor; //漫反射颜色
            sampler2D _MainTex; //颜色纹理
            float4 _MainTex_ST; //颜色纹理的缩放和平移
            sampler2D _BumpMap; //法线纹理
            float4 _BumpMap_ST; //法线纹理的缩放和平移
            float _BumpScale; //凹凸程度
            // float4 _SpecularColor; //高光颜色
            // fixed _SpecularNum; //光泽度

            struct v2f
            {
                //裁剪空间下坐标
                float4 pos:SV_POSITION;

                //纹理uv

                //我们可以单独的声明两个float2的成员用于记录 颜色和法线纹理的uv坐标
                //float2 uvTex:TEXCOORD0;//颜色纹理
                //float2 uvBump:TEXCOORD1;//法线纹理

                //也可以直接声明一个float4的成员 xy用于记录颜色纹理的uv,zw用于记录法线纹理的uv
                float4 uv:TEXCOORD0; //纹理变量


                //顶点相对于世界坐标的位置 主要用于 之后的 视角方向的计算
                //float3 worldPos:TEXCOORD1;

                //切线 到 世界空间的 变换矩阵
                //float3x3 rotation:TEXCOORD2;

                //代表我们切线空间到世界空间的 变换矩阵的3行 三个变量的w存世界坐标
                float4 TtoW0:TEXCOORD1;
                float4 TtoW1:TEXCOORD2;
                float4 TtoW2:TEXCOORD3;

                //雾的宏
                UNITY_FOG_COORDS(4)
                
                //阴影坐标宏
                //n为下一个可用的插值寄存器的索引值(结构体前面有几个TEXCOORD就填几)
                //比如这里前面有四个
                SHADOW_COORDS(5)
            };

            v2f vert(appdata_full v)
            {
                v2f v2f;

                //把模型空间下的顶点转到裁剪空间下
                v2f.pos = UnityObjectToClipPos(v.vertex);

                //计算纹理的缩放偏移
                v2f.uv.xy = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;
                v2f.uv.zw = v.texcoord.xy * _BumpMap_ST.xy + _BumpMap_ST.zw;

                //得到世界空间下的 顶点位置 用于之后在片元函数中计算视角方向(世界空间下的)
                //v2f.worldPos = mul(unity_ObjectToWorld, appdata_full.vertex);
                float3 worldPos = mul(unity_ObjectToWorld, v.vertex);

                //把模型空间下的法线、切线转换到世界空间下
                float3 worldNormal = UnityObjectToWorldNormal(v.normal);
                float3 worldTangent = UnityObjectToWorldDir(v.tangent);

                //计算副切线 计算叉乘结果后 垂直与切线和法线的向量有两条 通过乘以 切线当中的w,就可以确定是哪一条
                //cross是叉乘 appdata_full.tangent.w代表叉乘结果方向
                float3 worldBinormal = cross(normalize(worldTangent), normalize(worldNormal)) * v.tangent.w;

                //这个就是我们 切线空间到世界空间的 转换矩阵
                //       |    |     |
                //      切线 副切线 法线
                //       |    |     |
                //v2f.rotation = float3x3( worldTangent.x, worldBinormal.x,  worldNormal.x,
                //                          worldTangent.y, worldBinormal.y,  worldNormal.y,
                //                          worldTangent.z, worldBinormal.z,  worldNormal.z);
                v2f.TtoW0 = float4(worldTangent.x, worldBinormal.x, worldNormal.x, worldPos.x);
                v2f.TtoW1 = float4(worldTangent.y, worldBinormal.y, worldNormal.y, worldPos.y);
                v2f.TtoW2 = float4(worldTangent.z, worldBinormal.z, worldNormal.z, worldPos.z);

                //雾顶点转换宏
                UNITY_TRANSFER_FOG(v2f,v2f.pos);
                
                //坐标转换宏
                TRANSFER_SHADOW(v2f)

                return v2f;
            }

            fixed4 frag(v2f v2f) : SV_Target
            {
                //世界空间下光的方向
                fixed3 lightDir = normalize(_WorldSpaceLightPos0.xyz);

                //世界空间下视角方向
                // fixed3 viewDir = normalize(UnityWorldSpaceViewDir(v2f.worldPos));
                float3 worldPos = float3(v2f.TtoW0.w, v2f.TtoW1.w, v2f.TtoW2.w);
                fixed3 viewDir = normalize(UnityWorldSpaceViewDir(worldPos));

                //纹理采样函数tex2D
                //通过纹理采样函数 取出法线纹理贴图当中的数据
                float4 packedNormal = tex2D(_BumpMap, v2f.uv.zw);

                //利用内置的UnpackNormal函数对法线信息进行逆运算以及可能的解压
                //将我们取出来的法线数据 进行逆运算并且可能会进行解压缩的运算,最终得到切线空间下的法线数据
                float3 tangentNormal = UnpackNormal(packedNormal);

                //乘以凹凸程度的系数
                tangentNormal.xy *= _BumpScale;
                tangentNormal.z = sqrt(1.0 - saturate(dot(tangentNormal.xy, tangentNormal.xy)));


                //把计算完毕后的切线空间下的法线转换到世界空间下
                // float3 worldNormal = mul(v2f.rotation, tangentNormal);

                //以下两种写法等价 但是可以一步到位
                //float3x3 rotation = float3x3(v2f.TtoW0.xyz, v2f.TtoW1.xyz, v2f.TtoW2.xyz );
                //float3 worldNormal = mul(rotation, tangentNormal);
                //本质 就是在进行矩阵运算
                float3 worldNormal = float3(dot(v2f.TtoW0.xyz, tangentNormal), dot(v2f.TtoW1.xyz, tangentNormal),
                                             dot(v2f.TtoW2.xyz, tangentNormal));


                //接下来就来处理 带颜色纹理的 布林方光照模型计算

                //颜色纹理和漫反射颜色的 叠加
                fixed3 albedo = tex2D(_MainTex, v2f.uv.xy) * _MainColor.rgb;

                //兰伯特漫反射颜色 = 光的颜色 * 漫反射材质的颜色 * max(0, dot(世界坐标系下的法线, 光的方向))
                fixed3 lambertColor = _LightColor0.rgb * albedo.rgb * max(0, dot(worldNormal, normalize(lightDir)));

                //不使用高光了
                // //半角向量 = 视角方向 + 光的方向
                // float3 halfA = normalize(normalize(viewDir) + normalize(lightDir));
                //
                // //高光反射的颜色 = 光的颜色 * 高光反射材质的颜色 * pow(max(0, dot(世界坐标系下的法线, 半角向量)), 光泽度)
                // fixed3 specularColor = _LightColor0.rgb * _SpecularColor.rgb * pow(
                //     max(0, dot(worldNormal, halfA)), _SpecularNum);

                //计算光照衰减和阴影衰减的宏
                UNITY_LIGHT_ATTENUATION(atten, v2f, worldPos);

                // //布林方光照颜色 = 环境光颜色 + 兰伯特漫反射颜色 + 高光反射的颜色
                // fixed3 color = UNITY_LIGHTMODEL_AMBIENT.rgb * albedo + lambertColor + specularColor;

                //布林方光照颜色 = 环境光颜色 + 兰伯特漫反射颜色*衰减值
                fixed3 color = UNITY_LIGHTMODEL_AMBIENT.rgb * albedo + lambertColor * atten;

                // 片元应用雾的宏 apply fog
                UNITY_APPLY_FOG(v2f.fogCoord, color);
                
                return fixed4(color.rgb, 1);
            }
            ENDCG
        }
    }

    //Unity自带的漫反射Shader,里面实现了对应投射阴影的Pass
    Fallback "Diffuse"
}


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com

×

喜欢就点赞,疼爱就打赏