UnityShader-灯光与阴影

要实现灯光阴影,首先了解一下unity的渲染路径

同的渲染路径在光照和阴影方面会有不同的功能特点,应该根据游戏内容、目标平台以及硬件类型选择适合的渲染路径。

当显卡不能使用选定的渲染路径进行渲染的时候,Unity会自动选择一个较低精度的渲染路径。例如,当设置的延迟着色(Deferred Shading)不能被执行,前向渲染(Forward Rendering)将会被采用。

延迟着色渲染路径

灯光基于G-buffer和深度信息计算光照

优点:最逼真光影效果的渲染路径,即使场景中有成百上千个实时灯光,依然可以保持比较流畅的渲染帧率。但是它需要硬件达到一定的级别。

缺点:不支持抗锯齿,不支持半透明(自动使用前向渲染),使用延迟着色的不支持阴影投射,Culling Mask只能在限定条件下使用,且最多只能使用4个,

减少性能消耗办法:减小照射范围

前向渲染路径

最亮的逐像素照明,4个逐顶点,剩下的球谐(更快地渲染)

LightMode(灯光模式)

LightMode(灯光模式)标签定义了Pass在光照渲染流水线中的渲染规则

多重编译

unity 默认只支持一个投射阴影的而平行光,多重编译可以处理多灯光,阴影和材质(多个灯光,多个阴影)

(1)multi_compile_fwdbase:编译ForwardBase Pass中的所有变体,用于处理不同类型的光照贴图,并为主要平行光开启或者关闭阴影。
(2)multi_compile_fwdadd:编译ForwardAdd Pass中的所有变体,用于处理平行光、聚光灯和点光源,以及它们的cookie纹理。
(3)multi_compile_fwdadd_fullshadows:与multi_compile_fwdadd类似,但是增加了灯光投射实时阴影的效果

参考代码

Shader "Custom/shadow"
{
    Properties
    {
        _MainColor("MainColor",Color)=(1,1,1,1)
    }
    SubShader
    {
        //第一个Pass为平行光投射阴影

        pass
        {

            Tags{"LightMode"="ForwardBase"}
            CGPROGRAM
            #pragma vertex vert;
            #pragma fragment frag;
            #pragma mult_compile_fwdbase;

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

            struct v2f
            {
                float4 pos:SV_POSITION;
                float3 normal:TEXCOORD0;
                float4 vertex:TEXCOORD1;
                
                SHADOW_COORDS(2) //预定义宏 保存阴影坐标

            };
            fixed4 _MainColor;

            v2f vert(appdata_base v)
            {
                v2f o;
                o.pos=UnityObjectToClipPos(v.vertex);
                o.normal=v.normal;
                o.vertex=v.vertex;
                TRANSFER_SHADOW(o) //预定义宏变换阴影坐标
                return o;
            }

            fixed4 frag(v2f i):SV_TARGET
            {
                float3 n=UnityObjectToWorldNormal(i.normal);
                n=normalize(n);

                float3  light=WorldSpaceLightDir(i.vertex);
                light=normalize(light);
                float4 worldPos=mul(unity_ObjectToWorld,i.vertex);

                //Lambert 光照
                fixed ndotL=saturate(dot(n,light));
                fixed4 color=_LightColor0*_MainColor*ndotL;

                //加上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"}//forwardAdd与forwardBase区别?
            
            //与上一个pass 混合模式
            Blend one one
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile_fwdadd_fullshadows
            #include "UnityCG.cginc"
            #include "Lighting.cginc"
            #include "AutoLight.cginc"

            struct v2f
            {
                float4 pos:SV_POSITION;
                float3 normal:TEXCOORD0;
                float4 vertex:TEXCOORD1;
                SHADOW_COORDS(2)//使用预定义宏保存阴影图标
            };

            fixed4 _MainColor;

            v2f vert(appdata_base v)
            {
                v2f o;
                o.pos=UnityObjectToClipPos(v.vertex);
                o.normal=v.normal;
                o.vertex=v.vertex;
                TRANSFER_SHADOW(o) //预定义宏变换阴影坐标
                return o;

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

                //灯光
                float3 light=WorldSpaceLightDir(i.vertex);
                light=normalize(light);

                float4 worldPos=mul(unity_ObjectToWorld,i.vertex);

                //Lambert 光照
                fixed ndotL=saturate(dot(n,light));
                fixed4 color=_LightColor0*_MainColor*ndotL;

                //加上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;

                
                //计算阴影系数
                UNITY_LIGHT_ATTENUATION(shadowmask,i,worldPos.rgb)
                //阴影合成
                color.rgb*=shadowmask;
                return color;
            }
            ENDCG
        }

    }

    Fallback "Diffuse"
}

搭建场景

设置点光源,聚光灯,设置为Important,设置阴影模式为Hrd或者Soft,
设置物体接收阴影

最终效果

问题

如果地板是我们所写的Shadow Shader,则地板无法出现平行光的阴影,地板设置为Standard 则可以接收平行光的阴影,怀疑是Shadow 还缺少什么

留下评论