前向渲染多光源必要代码
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"
}