Lv.2 Unity主线:添加多光源交互
前向渲染多光源必要代码
Shader Pass
一般是单Pass,也就是ForwardPass,进行处理主光源(平行光),多光源设置实际上就是添加一个新的Pass处理点光和射光,注意不包括面光。这个Pass就是ForwardAddPass
结构体的声明要求
顶点的命名必须固定为vertex和pos
原函数在生成平行光的阴影时不会访问物体空间顶点位置,因为平行光不会衰减而且单一方向,所以它直接使用裁剪空间下的位置计算出阴影图,但是对于点光,因为点光是一个球型范围衰减,它需要生成一个cubemap阴影图,所以需要使用顶点位置信息,因为通常情况是顶点数比片元更少,所以使用物体空间的顶点位置vertex进行矩阵变换到点光源空间计算得到cubemap阴影图。 
ForwardPass
Tags
1
2
3
4
5
Name "Forward"
Tags
{
"LightMode" = "ForwardBase"
}
pragma指令
1
#pragma multi_compile_fwdbase
include
1
2
3
#include "UnityCG.cginc"
#include "Lighting.cginc"
#include "AutoLight.cginc"
阴影和光照衰减函数
对于ForwardBasePass处理阴影的时候是使用SHADOW_ATTENUATION(i),多光源投射的阴影是另外的函数
1
2
// 计算阴影衰减(自动处理所有阴影类型)
fixed shadow = SHADOW_ATTENUATION(i);
ForwardAddPass
处理ForwardAddPass,可以直接将原Pass进行拷贝就行,大部分是一样的*
Tags
1
2
3
4
5
Name"ForwadAdd"
Tags
{
"LightMode" = "ForwardAdd"
}
pragma指令
这里多光源的指令有两条,按需使用
1
2
3
4
// 无阴影仅光源衰减需要的变体
#pragma multi_compile_fwdadd
// 使用带阴影的编译指令
#pragma multi_compile_fwdadd_fullshadows
带阴影指令实际上是给这里使用的,也就是点光的照射阴影,如果不使用fullshadows那么怎么调都是没有的
相当于额外的光照阴影
关键字
光照叠加使用加法混合
1
2
Blend One One // 加法混合是关键
ZWrite Off // 避免深度冲突
光源判断
使用宏判断
1
2
3
4
5
6
// 关键:附加光源的方向计算
#ifdef USING_DIRECTIONAL_LIGHT
float3 lightDir = normalize(_WorldSpaceLightPos0.xyz);
#else
float3 lightDir = normalize(_WorldSpaceLightPos0.xyz - i.posWS);
#endif
_WorldSpaceLightPos0.w灯光标记判断,0是平行光,1是非平行光
1
2
3
4
5
6
//平行光源向量
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);
如果想要手动控制衰减,可以判断分类即可,一般不推荐
1
2
3
4
5
6
7
8
9
#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
阴影和光照衰减函数
1
2
3
4
// 阴影和衰减 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
1
Tags { "LightMode" = "Deferred" }
pragma
1
2
3
4
5
#pragma exclude_renderers nomrt
// 意思是:
// "排除那些不支持MRT的渲染器(平台)"
// 也就是:只在支持MRT的平台上编译和运行这个Shader
额外的输出结构体
将需要输出的内容分别输出,最多8个,一般输出4个*
1
2
3
4
5
6
7
struct GBuffer
{
half4 diffuse : SV_Target0;
half4 spec : SV_Target1;
half4 normal : SV_Target2;
half4 emission : SV_Target3;
};
片元着色器
不需要计算光照,直接将对应数据输出就行,同时frag函数后面不需要加SV_Target 
代码参考
为了方便两种切换,所以就直接加上延迟渲染Pass了
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
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"
}
Enjoy Reading This Article?
Here are some more articles you might like to read next: