前向渲染多光源必要代码

Shader Pass

一般是单Pass,也就是ForwardPass,进行处理主光源(平行光),多光源设置实际上就是添加一个新的Pass处理点光和射光,注意不包括面光。这个Pass就是ForwardAddPass

结构体的声明要求

顶点的命名必须固定为vertex和pos

图片
图片

原函数在生成平行光的阴影时不会访问物体空间顶点位置,因为平行光不会衰减而且单一方向,所以它直接使用裁剪空间下的位置计算出阴影图,但是对于点光,因为点光是一个球型范围衰减,它需要生成一个cubemap阴影图,所以需要使用顶点位置信息,因为通常情况是顶点数比片元更少,所以使用物体空间的顶点位置vertex进行矩阵变换到点光源空间计算得到cubemap阴影图。

图片
图片

ForwardPass

Tags

Name "Forward"
Tags
{
    "LightMode" = "ForwardBase"
}

pragma指令

#pragma multi_compile_fwdbase

include

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

阴影和光照衰减函数

对于ForwardBasePass处理阴影的时候是使用SHADOW_ATTENUATION(i),多光源投射的阴影是另外的函数

// 计算阴影衰减(自动处理所有阴影类型)
fixed shadow = SHADOW_ATTENUATION(i);

ForwardAddPass

处理ForwardAddPass,可以直接将原Pass进行拷贝就行,大部分是一样的*

Tags

Name"ForwadAdd"
Tags
{
    "LightMode" = "ForwardAdd"
}

pragma指令

这里多光源的指令有两条,按需使用

// 无阴影仅光源衰减需要的变体
#pragma multi_compile_fwdadd
// 使用带阴影的编译指令
#pragma multi_compile_fwdadd_fullshadows

带阴影指令实际上是给这里使用的,也就是点光的照射阴影,如果不使用fullshadows那么怎么调都是没有的

图片
图片

相当于额外的光照阴影

关键字

光照叠加使用加法混合

Blend One One  // 加法混合是关键
ZWrite Off     // 避免深度冲突

光源判断

使用宏判断

 // 关键:附加光源的方向计算
#ifdef USING_DIRECTIONAL_LIGHT
    float3 lightDir = normalize(_WorldSpaceLightPos0.xyz);
#else
    float3 lightDir = normalize(_WorldSpaceLightPos0.xyz - i.posWS);
#endif

_WorldSpaceLightPos0.w灯光标记判断,0是平行光,1是非平行光

//平行光源向量
half3 light_dir = normalize(_WorldSpaceLightPos0.xyz);
//点光源向量
half3 light_dir_point = normalize(_WorldSpaceLightPos0.xyz - i.pos_world);   
//_WorldSpaceLightPos0.w可以判断光源类型
light_dir = lerp(light_dir, light_dir_point, _WorldSpaceLightPos0.w); 

如果想要手动控制衰减,可以判断分类即可,一般不推荐

#if defined (DIRECTIONAL)
half3 light_dir = normalize(_WorldSpaceLightPos0.xyz);   //主光源向量
half attuenation = 1.0;
#elif defined (POINT)
half3 light_dir = normalize(_WorldSpaceLightPos0.xyz - i.pos_world);   //主光源向量
half distance = length(_WorldSpaceLightPos0.xyz - i.pos_world);
half range = 1.0 / unity_WorldToLight[0][0];//获取点光源的range属性
half attuenation = saturate((range - distance)/range);//光照衰减系数,平行光无衰减
#endif

阴影和光照衰减函数

// 阴影和衰减 atten是创建接收的变量,i是指输入的v2f结构
UNITY_LIGHT_ATTENUATION(atten, i, i.posWS);
//atten不需要声明,直接乘法混合就行
fixed3 finalCol = col.rgb * nol * atten * _LightColor0.rgb;

前向渲染和延迟渲染的光源数量和渲染处理

前向渲染灯光处理

这里我在场景中加了很多光源,材质就是默认的standard材质

图片
图片

FrameDebuger

使用FrameDebuger可以看到每个光源对于每个物体Mesh都需要单独计算渲染一次,那么理所当然光源越多,消耗越大,灯光数量需要进行严格的控制,对于大场景自然不能使用前向渲染

图片
图片

同时当灯光超过规定数量时,会自动降级为顶点灯光,也就是不在片元计算,像素一般来说是比顶点多的多的,所以顶点光照质量就会非常差

图片
图片

延迟渲染灯光处理

开启延迟渲染

找到MainCamera里面的渲染设置更改为Deferred

图片
图片

FrameDebuger

延迟渲染下,因为是先统一输出Guffer,然后再统一计算,所以可以看出绘制数明显降低,相当于把物体合批在绘制灯光了

图片
图片

延迟渲染必要代码

延迟渲染是灯光都在一个Pass中处理

Tags

Tags { "LightMode" = "Deferred" }

pragma

#pragma exclude_renderers nomrt

// 意思是:
// "排除那些不支持MRT的渲染器(平台)"
// 也就是:只在支持MRT的平台上编译和运行这个Shader

额外的输出结构体

将需要输出的内容分别输出,最多8个,一般输出4个*

struct GBuffer
            {
                half4 diffuse : SV_Target0;
                half4 spec : SV_Target1;
                half4 normal : SV_Target2;
                half4 emission : SV_Target3;
            };

片元着色器

不需要计算光照,直接将对应数据输出就行,同时frag函数后面不需要加SV_Target

图片
图片

代码参考

为了方便两种切换,所以就直接加上延迟渲染Pass了

Shader "Unlit/AddLight_Deferred"

{

    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}

    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100
        CGINCLUDE
        #include "UnityCG.cginc"
        sampler2D _MainTex;
        float4 _MainTex_ST;
        ENDCG
        Pass
        {
            Name "Deferred"
            Tags { "LightMode" = "Deferred" }
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma exclude_renderers nomrt
            #pragma target 3.0
            #include "UnityCG.cginc"

            struct appdata

            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
                float3 normal : NORMAL;
            };

            struct v2f
            {
                float4 pos : SV_POSITION;
                float2 uv : TEXCOORD0;
                float3 normalWS : TEXCOORD1;
            };

            struct GBuffer
            {
                half4 diffuse : SV_Target0;
                half4 spec : SV_Target1;
                half4 normal : SV_Target2;
                half4 emission : SV_Target3;
            };

            v2f vert(appdata v)
            {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                o.normalWS = UnityObjectToWorldNormal(v.normal);
                return o;
            }

            GBuffer frag(v2f i)
            {
                fixed4 albedo = tex2D(_MainTex, i.uv);
                float3 normalWS = normalize(i.normalWS);

                GBuffer output;
                output.diffuse = half4(albedo.rgb, 1.0);
                output.spec = half4(0.04, 0.04, 0.04, 0.5);
                output.normal = half4(normalWS * 0.5 + 0.5, 1.0);
                output.emission = half4(0, 0, 0, 1);
                return output;
            }
            ENDCG

        }

        Pass
        {
           Name "Forward"
           Tags{
            "LightMode" = "ForwardBase"
           }

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            // make fog work
            #pragma multi_compile_fwdbase

            #include "UnityCG.cginc"
            #include "Lighting.cginc"
            #include "AutoLight.cginc"             // 阴影宏定义在这里

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

  
            struct v2f
            {
                float4 pos : SV_POSITION;
                float2 texcoord : TEXCOORD0;
                float3 posWS : TEXCOORD1;
                float3 normalWS :TEXCOORD2;
                float3 tangentWS: TEXCOORD3;
                // 阴影坐标声明(自动处理多个变体)
                UNITY_SHADOW_COORDS(4)
            };

            //sampler2D _MainTex;
            //float4 _MainTex_ST;

            v2f vert (appdata v)
            {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                o.texcoord = TRANSFORM_TEX(v.uv, _MainTex);
                o.posWS = mul(unity_ObjectToWorld, v.vertex);
                o.normalWS = UnityObjectToWorldNormal(v.normal);
                o.tangentWS = UnityObjectToWorldDir(v.tangent);

                // 传输阴影数据(关键!)
                TRANSFER_SHADOW(o);

                return o;

            }

  

            fixed4 frag (v2f i) : SV_Target
            {
                // sample the texture
                fixed4 col = tex2D(_MainTex, i.texcoord);

                float3 lightDir = normalize(_WorldSpaceLightPos0.xyz);
                float nol = max(dot(i.normalWS, lightDir), 0);

                // 计算阴影衰减(自动处理所有阴影类型)
                fixed shadow = SHADOW_ATTENUATION(i);

                fixed3 finalCol = col.rgb * nol * shadow * _LightColor0.rgb;
                return fixed4(finalCol, 1);
            }

            ENDCG

        }

        Pass
        {
            Name"ForwadAdd"
            Tags
            {
                "LightMode" = "ForwardAdd"
            }

            Blend One One  // 加法混合是关键!
            ZWrite Off     // 避免深度冲突

            CGPROGRAM

            #pragma vertex vert
            #pragma fragment frag
            // 无阴影仅光源衰减需要的变体
            //#pragma multi_compile_fwdadd
            // 使用带阴影的编译指令
            #pragma multi_compile_fwdadd_fullshadows

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

  

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

  

            struct v2f
            {
                float4 pos      : SV_POSITION;
                float2 texcoord : TEXCOORD0;
                float3 posWS    : TEXCOORD1;
                float3 normalWS : TEXCOORD2;
                float3 tangentWS: TEXCOORD3;

                // 阴影坐标声明(自动处理多个变体)
                UNITY_SHADOW_COORDS(4)
            };

  

            // sampler2D _MainTex;
            // float4 _MainTex_ST;
            v2f vert (appdata v)
            {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                o.texcoord = TRANSFORM_TEX(v.uv, _MainTex);
                o.posWS = mul(unity_ObjectToWorld, v.vertex);
                o.normalWS = UnityObjectToWorldNormal(v.normal);
                o.tangentWS = UnityObjectToWorldDir(v.tangent);

                // 传输阴影数据(关键!)
                TRANSFER_SHADOW(o);
                return o;
            }

  

            fixed4 frag (v2f i) : SV_Target
            {

                // sample the texture
                fixed4 col = tex2D(_MainTex, i.texcoord);

                // 关键:附加光源的方向计算
                #ifdef USING_DIRECTIONAL_LIGHT
                    float3 lightDir = normalize(_WorldSpaceLightPos0.xyz);
                #else
                    float3 lightDir = normalize(_WorldSpaceLightPos0.xyz - i.posWS);
                #endif

                // 计算阴影衰减(自动处理所有阴影类型)
                //fixed shadow = SHADOW_ATTENUATION(i);

                float nol = max(dot(i.normalWS, lightDir), 0);

                // 阴影和衰减
                UNITY_LIGHT_ATTENUATION(atten, i, i.posWS);

  

                fixed3 finalCol = col.rgb * nol * atten * _LightColor0.rgb;
                return fixed4(finalCol, 1);

            }
            ENDCG
        }

        Pass
        {

            Name"ShadowCaster"
            Tags{
                "LightMode" = "ShadowCaster"
            }

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            // 阴影投射需要的变体
            #pragma multi_compile_shadowcaster
            #include "UnityCG.cginc"

            struct appdata

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

            struct v2f
            {
                V2F_SHADOW_CASTER;  // Unity预定义的结构
            };

            v2f vert(appdata v)
            {

                v2f o;
                TRANSFER_SHADOW_CASTER_NORMALOFFSET(o)  // 转换到阴影空间
                return o;
            }

            fixed4 frag(v2f i) : SV_Target
            {
                SHADOW_CASTER_FRAGMENT(i)  // 写入阴影深度
            }
            ENDCG
        }
    }
    FallBack "Diffuse"
}