Shader中的光照模型(八)-- 实现阴影效果

【本系列文章系学习 唐福幸《Unity ShaderLab 新手宝典》的笔记,包含个人理解,如有错误欢迎批评指出

6.7.4 实现阴影效果

为了便于实现,方便理解实现逻辑,本案例在Lambert光照模型(逐像素光照渲染)基础上进行改进。
原代码

Shader "Chapter6/LambertFrag"
{
    Properties
    {
        _MainCol ("Main Color", Color) = (1,1,1,1)
    }
    SubShader
    {
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"
            #include "UnityLightingCommon.cginc"

            struct v2f
            {
                float4 pos : SV_POSITION;
                float3 normal : TEXCOORD0;//这里不能是NORMAL,会跟appdata_base冲突
                float4 vertex : TEXCOORD1;
            };

            fixed4 _MainCol;

            v2f vert (appdata_base v)
            {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                o.normal = v.normal;
                o.vertex = v.vertex;
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                //法线向量
                float3 n = UnityObjectToWorldNormal(i.normal);
                //n = normalize(n);

                //灯光方向向量
                fixed3 l = normalize(_WorldSpaceLightPos0.xyz);

                //计算漫反射
                fixed ndotl = dot(n,l);
                fixed4 color = _LightColor0 * _MainCol *saturate(ndotl);

                return color;
            }
            ENDCG
        }
    }
}

6.7.4.1 一个投射阴影

修改灯光的阴影开关(并把图中的Sphere球 和Plane地面 都附上相应材质),如下
打开阴影
在这里插入图片描述

多个灯光下,只有一个平行光产生阴影效果在这里插入图片描述
附上代码:

Shader "Chapter6/LambertFrag"
{
    Properties
    {
        _MainColor ("Main Color", Color) = (1,1,1,1)
    }
    SubShader
    {
        Pass
        {
            Tags { "LightMode"="ForwardBase" }//第一步:添加标签,计算主要灯光模式
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            //第二步:调用多重编译指令,为当前pass中渲染的每个灯光编译出不同变体
            #pragma multi_compile_fwdbase

            #include "UnityCG.cginc"
            //第三步:引入相关文件,便于使用内置变量和预定义函数
            #include "Lighting.cginc"
            #include "AutoLight.cginc"

            struct v2f
            {
                float4 pos : SV_POSITION;
                float3 normal : TEXCOORD0;//这里不能是NORMAL,会跟appdata_base冲突
                float4 vertex: TEXCOORD1;
                SHADOW_COORDS(2)//第四步:使用预定义宏保存阴影贴图坐标,2表示texcoord后的序号,前面两套已经被使用了
            };

            fixed4 _MainColor;

            v2f vert (appdata_base v)
            {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                o.normal = v.normal;
                o.vertex = v.vertex;
                //第五步:TRANSFER_SHADOW
                //1:如果当前平台可以使用屏幕空间的阴影映射技术(SCREENSPACE_SHADOWS),则会调用内置的ComputeScreenPos函数计算屏幕空间的uv坐标,存储在_ShadowCoord,后续直接用屏幕uv坐标采样屏幕阴影贴图;
                //2:如果不支持则会使用传统的阴影映射技术,TRANSFER_SHADOW会把顶点坐标从模型空间转换到光源空间后存储到_ShadowCoord中,后续根据坐标信息对ShadowMap采样。
                TRANSFER_SHADOW(o)
                //我的理解是,如果使用屏幕空间的阴影映射技术,会计算物体在光照下阴影对应的屏幕空间的uv坐标
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                //法线向量
                float3 n = UnityObjectToWorldNormal(i.normal);
                //n = normalize(n);

                //灯光方向向量
                //fixed3 l = normalize(_WorldSpaceLightPos0.xyz);//意思是第一个平行光的方向
                float3 l = WorldSpaceLightDir(i.vertex);//第六步:会根据不同类型的灯光计算灯光方向,没有归一化
                l = normalize(l);
                float4 worldPos = mul(unity_ObjectToWorld,i.vertex);//第七步:获取世界空间顶点坐标

                //计算漫反射Lambert光照
                fixed ndotl = dot(n,l);
                fixed4 color = _LightColor0 * _MainColor *saturate(ndotl);

                //第八步:加上4个点光源的光照
                //Shade4PointLights: 从4个点光源计算漫反射照明,并以特殊方式打包数据。
                //unity_4LightPosX0,unity_4LightPosY0,unity_4LightPosZ0,4个坐标的向量数据
                color.rgb += Shade4PointLights(
                unity_4LightPosX0,unity_4LightPosY0,unity_4LightPosZ0,
                unity_LightColor[0].rgb,unity_LightColor[1].rgb,
                unity_LightColor[2].rgb,unity_LightColor[3].rgb,
                unity_4LightAtten0,worldPos.rgb,n) * _MainColor;

                //第九步:加上环境光照
                color += unity_AmbientSky;

                //第十步:使用宏定义计算阴影系数
                //用于计算光照衰减系数。参数一为返回值(光照衰减系数),参数二用于阴影计算,参数三是世界坐标
                UNITY_LIGHT_ATTENUATION(shadowmask,i,worldPos.rgb)

                //第十一步:阴影合成
                color.rgb *= shadowmask;
                return color;
            }
            ENDCG
        }

    }
    FallBack "Diffuse"//第十二步:这里的Fallback并不是备胎,而是必要的保存投影的方式 https://blog.youkuaiyun.com/shenmifangke/article/details/50466798
}

6.7.4.2 多个投射阴影

最终效果
在这里插入图片描述



Shader "Chapter6/LambertFrag"
{
    Properties
    {
        _MainColor ("Main Color", Color) = (1,1,1,1)
    }
    SubShader
    {
        Pass
        {
            Tags { "LightMode"="ForwardBase" }//第一步:添加标签,计算主要灯光模式
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            //第二步:调用多重编译指令,为当前pass中渲染的每个灯光编译出不同变体
            #pragma multi_compile_fwdbase

            #include "UnityCG.cginc"
            //第三步:引入相关文件,便于使用内置变量和预定义函数
            #include "Lighting.cginc"
            #include "AutoLight.cginc"

            struct v2f
            {
                float4 pos : SV_POSITION;
                float3 normal : TEXCOORD0;//这里不能是NORMAL,会跟appdata_base冲突
                float4 vertex: TEXCOORD1;
                SHADOW_COORDS(2)//第四步:使用预定义宏保存阴影贴图坐标,2表示texcoord后的序号,前面两套已经被使用了
            };

            fixed4 _MainColor;

            v2f vert (appdata_base v)
            {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                o.normal = v.normal;
                o.vertex = v.vertex;
                //第五步:TRANSFER_SHADOW
                //1:如果当前平台可以使用屏幕空间的阴影映射技术(SCREENSPACE_SHADOWS),则会调用内置的ComputeScreenPos函数计算屏幕空间的uv坐标,存储在_ShadowCoord,后续直接用屏幕uv坐标采样屏幕阴影贴图;
                //2:如果不支持则会使用传统的阴影映射技术,TRANSFER_SHADOW会把顶点坐标从模型空间转换到光源空间后存储到_ShadowCoord中,后续根据坐标信息对ShadowMap采样。
                TRANSFER_SHADOW(o)
                //我的理解是,如果使用屏幕空间的阴影映射技术,会计算物体在光照下阴影对应的屏幕空间的uv坐标
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                //法线向量
                float3 n = UnityObjectToWorldNormal(i.normal);
                //n = normalize(n);

                //灯光方向向量
                //fixed3 l = normalize(_WorldSpaceLightPos0.xyz);//意思是第一个平行光的方向
                float3 l = WorldSpaceLightDir(i.vertex);//第六步:会根据不同类型的灯光计算灯光方向,没有归一化
                l = normalize(l);
                float4 worldPos = mul(unity_ObjectToWorld,i.vertex);//第七步:获取世界空间顶点坐标

                //计算漫反射Lambert光照
                fixed ndotl = dot(n,l);
                fixed4 color = _LightColor0 * _MainColor *saturate(ndotl);

                //第八步:加上4个点光源的光照
                //Shade4PointLights: 从4个点光源计算漫反射照明,并以特殊方式打包数据。
                //unity_4LightPosX0,unity_4LightPosY0,unity_4LightPosZ0,4个坐标的向量数据
                color.rgb += Shade4PointLights(
                unity_4LightPosX0,unity_4LightPosY0,unity_4LightPosZ0,
                unity_LightColor[0].rgb,unity_LightColor[1].rgb,
                unity_LightColor[2].rgb,unity_LightColor[3].rgb,
                unity_4LightAtten0,worldPos.rgb,n) * _MainColor;

                //第九步:加上环境光照
                color += unity_AmbientSky;

                //第十步:使用宏定义计算阴影系数
                //用于计算光照衰减系数。参数一为返回值(光照衰减系数),参数二用于阴影计算,参数三是世界坐标
                UNITY_LIGHT_ATTENUATION(shadowmask,i,worldPos.rgb)

                //第十一步:阴影合成
                color.rgb *= shadowmask;
                return color;
            }
            ENDCG
        }

        Pass
        {
            Tags { "LightMode"="ForwardAdd" }//第十二步:添加标签,为每一个逐像素灯光生成一个Pass进行光照计算
            Blend One One//第十三步:混合指令,避免当前Pass完全覆盖之前的Shader
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            //第十四步:调用多重编译指令,用于处理平行光、聚光灯、点光源、cookie纹理,以及灯光投射实时阴影
            #pragma multi_compile_fwdadd_fullshadows
            //#pragma multi_compile_fwdadd

            #include "UnityCG.cginc"
            //引入相关文件,便于使用内置变量和预定义函数
            #include "Lighting.cginc"
            #include "AutoLight.cginc"

            struct v2f
            {
                float4 pos : SV_POSITION;
                float3 normal : TEXCOORD0;//这里不能是NORMAL,会跟appdata_base冲突
                float4 vertex: TEXCOORD1;
                SHADOW_COORDS(2)//使用预定义宏保存阴影贴图坐标,2表示texcoord后的序号,前面两套已经被使用了
            };

            fixed4 _MainColor;

            v2f vert (appdata_base v)
            {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                o.normal = v.normal;
                o.vertex = v.vertex;
                //TRANSFER_SHADOW
                //1:如果当前平台可以使用屏幕空间的阴影映射技术(SCREENSPACE_SHADOWS),则会调用内置的ComputeScreenPos函数计算屏幕空间的uv坐标,存储在_ShadowCoord,后续直接用屏幕uv坐标采样屏幕阴影贴图;
                //2:如果不支持则会使用传统的阴影映射技术,TRANSFER_SHADOW会把顶点坐标从模型空间转换到光源空间后存储到_ShadowCoord中,后续根据坐标信息对ShadowMap采样。
                TRANSFER_SHADOW(o)
                //我的理解是,如果使用屏幕空间的阴影映射技术,会计算物体在光照下阴影对应的屏幕空间的uv坐标
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                //法线向量
                float3 n = UnityObjectToWorldNormal(i.normal);
                //n = normalize(n);

                //灯光方向向量
                //fixed3 l = normalize(_WorldSpaceLightPos0.xyz);//意思是第一个平行光的方向
                float3 l = WorldSpaceLightDir(i.vertex);//会根据不同类型的灯光计算灯光方向,没有归一化
                l = normalize(l);
                float4 worldPos = mul(unity_ObjectToWorld,i.vertex);//获取世界空间顶点坐标

                //计算漫反射Lambert光照
                fixed ndotl = dot(n,l);
                fixed4 color = _LightColor0 * _MainColor *saturate(ndotl);

                //加上4个点光源的光照
                //Shade4PointLights: 从4个点光源计算漫反射照明,并以特殊方式打包数据。
                //unity_4LightPosX0,unity_4LightPosY0,unity_4LightPosZ0,4个坐标的向量数据
                color.rgb += Shade4PointLights(
                unity_4LightPosX0,unity_4LightPosY0,unity_4LightPosZ0,
                unity_LightColor[0].rgb,unity_LightColor[1].rgb,
                unity_LightColor[2].rgb,unity_LightColor[3].rgb,
                unity_4LightAtten0,worldPos.rgb,n) * _MainColor;

                //加上环境光照
                //color += unity_AmbientSky;//第十五步:前面Pass已经计算过了,这里不再重复

                //使用宏定义计算阴影系数
                //用于计算光照衰减系数。参数一为返回值(光照衰减系数),参数二用于阴影计算,参数三是世界坐标
                UNITY_LIGHT_ATTENUATION(shadowmask,i,worldPos.rgb)

                //阴影合成
                color.rgb *= shadowmask;
                return color;
            }
            ENDCG
        }

    }
    FallBack "Diffuse"//这里的Fallback并不是备胎,而是必要的保存投影的方式 https://blog.youkuaiyun.com/shenmifangke/article/details/50466798
}

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值