第18章 Shader Library详解

第18章 Shader Library详解

理论讲解

18.1 Core.hlsl、Lighting.hlsl结构

Unity的Shader Library是渲染管线的核心组成部分,包含了大量预定义的函数和宏,用于处理各种渲染任务。这些库文件为开发者提供了标准化的渲染函数,简化了着色器编写过程。

Core.hlsl结构:

Core.hlsl是URP中最基础的着色器库文件,包含了渲染管线中通用的函数和定义:

  1. 数据结构定义:顶点输入、片元输入、表面数据等结构
  2. 变换函数:世界空间、视图空间、投影空间变换
  3. 光照计算:基础光照模型和BRDF函数
  4. 纹理采样:各种纹理采样和过滤函数
  5. 数学工具:常用的数学运算和向量操作

Lighting.hlsl结构:

Lighting.hlsl专门处理光照相关的计算,包含:

  1. 光照模型:Lambert、Blinn-Phong、PBR等光照模型
  2. BRDF函数:双向反射分布函数的实现
  3. 阴影计算:阴影采样和衰减函数
  4. 环境光照:全局光照和反射计算
  5. 光源处理:主光源和附加光源的处理

18.2 BRDF函数实现

BRDF(Bidirectional Reflectance Distribution Function)是PBR渲染的核心,描述了光线在表面的反射行为。

BRDF组成部分:

  1. 漫反射项(Diffuse):Lambert漫反射模型
  2. 镜面反射项(Specular):微表面模型(GGX/Trowbridge-Reitz)
  3. 菲涅尔项(Fresnel):Schlick近似
  4. 几何项(Geometry):Smith几何遮蔽函数

BRDF实现原理:

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
// 简化的BRDF实现
half3 BRDF(half3 L, half3 V, half3 N, half3 albedo, half metallic, half smoothness)
{
half3 H = normalize(L + V);
half NdotL = saturate(dot(N, L));
half NdotV = saturate(dot(N, V));
half NdotH = saturate(dot(N, H));
half VdotH = saturate(dot(V, H));

// 微表面分布函数(GGX)
half alpha = (1.0 - smoothness) * (1.0 - smoothness);
alpha = alpha * alpha;
half alpha2 = alpha * alpha;
half denom = NdotH * NdotH * (alpha2 - 1.0) + 1.0;
half D = alpha2 / (UNITY_PI * denom * denom);

// 几何函数
half NH2 = 2.0 * NdotH;
half G = (NdotL * NH2) / (NdotL * NH2 - NdotL + 1.0) *
(NdotV * NH2) / (NdotV * NH2 - NdotV + 1.0);

// 菲涅尔函数(Schlick近似)
half3 F = lerp(half3(1.0, 1.0, 1.0), albedo, metallic);
F = F + (1.0 - F) * pow(1.0 - VdotH, 5.0);

// 漫反射项
half3 kD = (1.0 - F) * (1.0 - metallic);

// 最终BRDF
half3 specular = (D * G * F) / (4.0 * NdotL * NdotV);
half3 diffuse = kD * albedo / UNITY_PI;

return (diffuse + specular) * NdotL;
}

18.3 Shadow采样函数

阴影是渲染中的重要组成部分,URP提供了多种阴影采样函数来处理不同类型的阴影。

主要阴影函数:

  1. MainLightShadow:主光源阴影采样
  2. AdditionalLightShadow:附加光源阴影采样
  3. ScreenSpaceShadow:屏幕空间阴影
  4. CascadedShadow:级联阴影贴图

阴影采样流程:

  1. 坐标变换:将世界坐标转换为阴影贴图坐标
  2. 深度比较:与阴影贴图中的深度值比较
  3. 过滤处理:应用PCF或VSM等过滤算法
  4. 衰减计算:计算阴影衰减值

18.4 Fog计算

雾效是渲染中常用的视觉效果,用于模拟大气散射和距离衰减。

雾效类型:

  1. Linear Fog:线性雾效
  2. Exponential Fog:指数雾效
  3. Exponential Squared Fog:指数平方雾效

雾效计算公式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 线性雾效
half LinearFog(half distance, half fogStart, half fogEnd)
{
return saturate((fogEnd - distance) / (fogEnd - fogStart));
}

// 指数雾效
half ExponentialFog(half distance, half density)
{
return exp(-distance * density);
}

// 指数平方雾效
half ExponentialSquaredFog(half distance, half density)
{
half exponent = distance * density;
return exp(-exponent * exponent);
}

18.5 GI数据获取

全局光照(Global Illumination)数据的获取是实现间接光照的关键。

GI数据来源:

  1. Lightmap:烘焙的光照贴图
  2. Light Probe:光照探针
  3. Reflection Probe:反射探针
  4. Ambient Light:环境光

GI数据处理:

  • 烘焙GI:预计算的间接光照
  • 实时GI:运行时计算的间接光照
  • 混合GI:烘焙与实时GI的混合

18.6 常用工具函数库

Shader Library中包含大量实用的工具函数,用于简化着色器开发。

数学工具函数:

  • 向量运算:点积、叉积、归一化等
  • 矩阵运算:变换矩阵、投影矩阵等
  • 插值函数:线性插值、球面插值等

颜色处理函数:

  • 颜色空间转换:RGB到HSV、YUV等
  • 色调映射:HDR到LDR转换
  • 颜色校正:伽马校正、色调调整等

代码示例

18.7 自定义BRDF实现

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
// 自定义BRDF实现示例
Shader "Custom/CustomBRDF"
{
Properties
{
_BaseColor("Base Color", Color) = (0.5, 0.5, 0.5, 1)
_Metallic("Metallic", Range(0, 1)) = 0
_Smoothness("Smoothness", Range(0, 1)) = 0.5
_NormalMap("Normal Map", 2D) = "bump" {}
_NormalScale("Normal Scale", Range(0, 2)) = 1
}
SubShader
{
Tags { "RenderType" = "Opaque" "RenderPipeline" = "UniversalPipeline" }
LOD 300

Pass
{
Name "ForwardLit"
Tags { "LightMode" = "UniversalForward" }

HLSLPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile _ _MAIN_LIGHT_SHADOWS
#pragma multi_compile _ _MAIN_LIGHT_SHADOWS_CASCADE
#pragma multi_compile _ _SHADOWS_SOFT

#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Color.hlsl"
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/UnityInstancing.hlsl"

struct Attributes
{
float4 positionOS : POSITION;
float3 normalOS : NORMAL;
float4 tangentOS : TANGENT;
float2 uv : TEXCOORD0;
};

struct Varyings
{
float4 positionCS : SV_POSITION;
float3 positionWS : TEXCOORD0;
float3 normalWS : TEXCOORD1;
float4 tangentWS : TEXCOORD2;
float2 uv : TEXCOORD3;
float4 shadowCoord : TEXCOORD4;
};

CBUFFER_START(UnityPerMaterial)
half4 _BaseColor;
half _Metallic;
half _Smoothness;
half _NormalScale;
CBUFFER_END

TEXTURE2D(_NormalMap);
SAMPLER(sampler_NormalMap);
float4 _NormalMap_ST;

// 自定义BRDF函数
half3 CustomBRDF(half3 L, half3 V, half3 N, half3 albedo, half metallic, half smoothness)
{
// 计算半向量
half3 H = normalize(L + V);

// 计算角度
half NdotL = saturate(dot(N, L));
half NdotV = saturate(dot(N, V));
half NdotH = saturate(dot(N, H));
half VdotH = saturate(dot(V, H));

// 微表面分布函数(GGX)
half alpha = (1.0 - smoothness) * (1.0 - smoothness);
alpha = alpha * alpha;
half alpha2 = alpha * alpha;
half denom = NdotH * NdotH * (alpha2 - 1.0) + 1.0;
half D = alpha2 / (UNITY_PI * denom * denom);

// 几何函数(Smith GGX)
half k = (smoothness + 1.0) * (smoothness + 1.0) / 8.0;
half G1L = NdotL / (NdotL * (1.0 - k) + k);
half G1V = NdotV / (NdotV * (1.0 - k) + k);
half G = G1L * G1V;

// 菲涅尔函数(Schlick近似)
half3 F0 = lerp(half3(0.04, 0.04, 0.04), albedo, metallic);
half3 F = F0 + (1.0 - F0) * pow(1.0 - VdotH, 5.0);

// 漫反射项(使用Disney BRDF)
half3 kD = (1.0 - F) * (1.0 - metallic);

// 镜面反射项
half3 specular = (D * G * F) / (4.0 * NdotL * NdotV + 0.0001);

// 漫反射项(Lambert)
half3 diffuse = kD * albedo / UNITY_PI;

return (diffuse + specular) * NdotL;
}

Varyings vert(Attributes input)
{
Varyings output;
VertexPositionInputs vertexInput = GetVertexPositionInputs(input.positionOS.xyz);
VertexNormalInputs normalInput = GetVertexNormalInputs(input.normalOS, input.tangentOS);

output.positionCS = vertexInput.positionCS;
output.positionWS = vertexInput.positionWS;
output.normalWS = normalInput.normalWS;
output.tangentWS = half4(normalInput.tangentWS, normalInput.sign);
output.uv = TRANSFORM_TEX(input.uv, _NormalMap);

output.shadowCoord = TransformWorldToShadowCoord(output.positionWS);

return output;
}

half4 frag(Varyings input) : SV_Target
{
// 获取法线贴图
half4 normalMap = SAMPLE_TEXTURE2D(_NormalMap, sampler_NormalMap, input.uv);
half3 normalTS = UnpackNormalScale(normalMap, _NormalScale);
half3 normalWS = TransformTangentToWorld(normalTS,
half3x3(input.tangentWS.xyz,
cross(input.normalWS, input.tangentWS.xyz) * input.tangentWS.w,
input.normalWS));
normalWS = NormalizeNormalPerPixel(normalWS);

// 获取主光源
Light mainLight = GetMainLight(input.shadowCoord);

// 计算光照方向和视角方向
half3 lightDir = normalize(mainLight.direction);
half3 viewDir = normalize(_WorldSpaceCameraPos - input.positionWS);

// 应用自定义BRDF
half3 color = CustomBRDF(lightDir, viewDir, normalWS, _BaseColor.rgb, _Metallic, _Smoothness);

// 添加环境光
half3 ambient = SampleSH(normalWS);
color += ambient * _BaseColor.rgb * (1.0 - _Metallic);

// 应用光照强度
color *= mainLight.color * mainLight.distanceAttenuation;

return half4(color, 1.0);
}
ENDHLSL
}
}
Fallback "Universal Render Pipeline/Lit"
}

18.8 阴影处理工具

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
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;

public class ShadowProcessingTool : MonoBehaviour
{
[Header("Shadow Processing Settings")]
public bool enableShadowProcessing = true;
public bool visualizeShadows = true;
public bool logShadowData = true;

[Header("Shadow Configuration")]
public float shadowDistance = 50.0f;
public float shadowBias = 0.05f;
public float shadowNormalBias = 0.4f;
public float shadowSoftness = 0.5f;
public float shadowSoftnessFade = 0.0f;

[Header("Performance Settings")]
public int shadowResolution = 1024;
public float shadowUpdateInterval = 0.1f;
public bool enableAsyncShadowProcessing = true;

private CommandBuffer m_ShadowCommandBuffer;
private Material m_ShadowVisualizationMaterial;
private Light m_MainLight;
private Camera m_Camera;
private float m_LastShadowUpdate = 0f;

[System.Serializable]
public class ShadowAnalysisData
{
public float averageShadowDistance;
public float maxShadowDistance;
public float minShadowDistance;
public int shadowCascades;
public float cascadeSplit0;
public float cascadeSplit1;
public float cascadeSplit2;
public float shadowBias;
public float shadowNormalBias;
public float shadowSoftness;
public bool hasShadowCasters;
public bool hasShadowReceivers;
public System.DateTime lastUpdate;
}

[System.Serializable]
public class ShadowPerformanceData
{
public int shadowDrawCalls;
public float shadowUpdateTimeMS;
public int shadowTextureMemoryKB;
public float shadowRenderTimeMS;
public bool isOptimal;
}

public ShadowAnalysisData analysisData = new ShadowAnalysisData();
public ShadowPerformanceData performanceData = new ShadowPerformanceData();

void Start()
{
if (enableShadowProcessing)
{
InitializeShadowProcessing();
}
}

void Update()
{
if (enableShadowProcessing && Time.time - m_LastShadowUpdate >= shadowUpdateInterval)
{
UpdateShadowProcessing();
m_LastShadowUpdate = Time.time;
}
}

private void InitializeShadowProcessing()
{
// 查找主光源
var lights = FindObjectsOfType<Light>();
foreach (var light in lights)
{
if (light.type == LightType.Directional && light.shadows != LightShadows.None)
{
m_MainLight = light;
break;
}
}

// 查找相机
m_Camera = Camera.main;
if (m_Camera == null)
{
m_Camera = FindObjectOfType<Camera>();
}

CreateShadowCommandBuffer();
CreateShadowVisualizationMaterial();

if (logShadowData)
{
Debug.Log("[ShadowProcessingTool] Initialized shadow processing system");
}
}

private void CreateShadowCommandBuffer()
{
if (m_ShadowCommandBuffer != null)
{
m_ShadowCommandBuffer.Clear();
}
else
{
m_ShadowCommandBuffer = new CommandBuffer();
m_ShadowCommandBuffer.name = "Shadow Processing";
}
}

private void CreateShadowVisualizationMaterial()
{
// 创建阴影可视化材质
string shaderSource = @"
Shader ""Hidden/ShadowVisualization""
{
Properties
{
_MainTex (""Texture"", 2D) = ""white"" {}
_ShadowMapTexture (""Shadow Map"", 2D) = ""black"" {}
_ShadowStrength (""Shadow Strength"", Range(0, 1)) = 1.0
}
SubShader
{
Tags { ""RenderType""=""Opaque"" }
LOD 100

Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include ""UnityCG.cginc""

struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};

struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
};

sampler2D _MainTex;
sampler2D _ShadowMapTexture;
float4 _ShadowMapTexture_TexelSize;
float _ShadowStrength;

v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = v.uv;
return o;
}

fixed4 frag (v2f i) : SV_Target
{
fixed4 col = tex2D(_MainTex, i.uv);

// 简单的阴影可视化
float shadow = tex2D(_ShadowMapTexture, i.uv).r;
col.rgb *= lerp(1.0, shadow, _ShadowStrength);

return col;
}
ENDCG
}
}
}";

// 注意:在实际项目中,您需要创建一个真正的Shader资源
// 这里仅作示例,实际使用时请创建Shader文件
}

private void UpdateShadowProcessing()
{
if (m_MainLight != null)
{
// 更新阴影参数
m_MainLight.shadowBias = shadowBias;
m_MainLight.shadowNormalBias = shadowNormalBias;
m_MainLight.shadowNearPlane = 0.1f;

// 分析阴影数据
AnalyzeShadowData();
}
}

private void AnalyzeShadowData()
{
if (m_MainLight == null) return;

analysisData.shadowBias = m_MainLight.shadowBias;
analysisData.shadowNormalBias = m_MainLight.shadowNormalBias;
analysisData.shadowSoftness = shadowSoftness;
analysisData.lastUpdate = System.DateTime.Now;

// 分析场景中的阴影投射者和接收者
var shadowCasters = FindObjectsOfType<Renderer>();
int casterCount = 0;
int receiverCount = 0;

foreach (var renderer in shadowCasters)
{
if (renderer != null && renderer.enabled && renderer.shadowCastingMode != ShadowCastingMode.Off)
{
casterCount++;
}
if (renderer != null && renderer.enabled && renderer.receiveShadows)
{
receiverCount++;
}
}

analysisData.hasShadowCasters = casterCount > 0;
analysisData.hasShadowReceivers = receiverCount > 0;

// 如果是方向光,获取级联信息
if (m_MainLight.type == LightType.Directional)
{
var urpAsset = GraphicsSettings.renderPipelineAsset as UniversalRenderPipelineAsset;
if (urpAsset != null)
{
analysisData.shadowCascades = urpAsset.shadowCascadeOption == ShadowCascadeOption.FourCascades ? 4 :
urpAsset.shadowCascadeOption == ShadowCascadeOption.TwoCascades ? 2 : 1;
}
}

if (logShadowData)
{
Debug.Log($"[Shadow Analysis] Casters: {casterCount}, Receivers: {receiverCount}, Cascades: {analysisData.shadowCascades}");
}
}

// 获取阴影分析数据
public ShadowAnalysisData GetShadowAnalysisData()
{
return analysisData;
}

// 获取阴影性能数据
public ShadowPerformanceData GetShadowPerformanceData()
{
return performanceData;
}

// 更新阴影参数
public void UpdateShadowParameters(float bias, float normalBias, float softness)
{
shadowBias = Mathf.Clamp01(bias);
shadowNormalBias = Mathf.Clamp(normalBias, 0.1f, 1.0f);
shadowSoftness = Mathf.Clamp01(softness);

if (m_MainLight != null)
{
m_MainLight.shadowBias = shadowBias;
m_MainLight.shadowNormalBias = shadowNormalBias;
}
}

// 获取阴影统计信息
public string GetShadowStats()
{
var stats = new System.Text.StringBuilder();
stats.AppendLine("=== Shadow Statistics ===");
stats.AppendLine($"Shadow Distance: {shadowDistance}");
stats.AppendLine($"Shadow Bias: {analysisData.shadowBias}");
stats.AppendLine($"Shadow Normal Bias: {analysisData.shadowNormalBias}");
stats.AppendLine($"Shadow Softness: {analysisData.shadowSoftness}");
stats.AppendLine($"Shadow Cascades: {analysisData.shadowCascades}");
stats.AppendLine($"Has Casters: {analysisData.hasShadowCasters}");
stats.AppendLine($"Has Receivers: {analysisData.hasShadowReceivers}");
stats.AppendLine($"Last Update: {analysisData.lastUpdate}");

return stats.ToString();
}

// 优化阴影性能
public void OptimizeShadowPerformance()
{
if (m_MainLight != null)
{
// 根据性能需求调整阴影参数
var shadowCasters = FindObjectsOfType<Renderer>();
int casterCount = 0;

foreach (var renderer in shadowCasters)
{
if (renderer != null && renderer.shadowCastingMode != ShadowCastingMode.Off)
{
casterCount++;
}
}

// 如果阴影投射者过多,适当降低阴影质量
if (casterCount > 50)
{
m_MainLight.shadowResolution = LightShadowResolution.Medium;
shadowBias = Mathf.Max(shadowBias, 0.1f);
}
else if (casterCount > 20)
{
m_MainLight.shadowResolution = LightShadowResolution.High;
}
else
{
m_MainLight.shadowResolution = LightShadowResolution.VeryHigh;
}

if (logShadowData)
{
Debug.Log($"[Shadow Optimization] Adjusted shadow resolution based on {casterCount} casters");
}
}
}

// 获取当前阴影设置
public string GetCurrentShadowSettings()
{
if (m_MainLight != null)
{
return $"Type: {m_MainLight.type}, Shadows: {m_MainLight.shadows}, " +
$"Bias: {m_MainLight.shadowBias}, Normal Bias: {m_MainLight.shadowNormalBias}, " +
$"Resolution: {m_MainLight.shadowResolution}";
}
return "No main light found";
}

void OnDestroy()
{
if (m_ShadowCommandBuffer != null)
{
m_ShadowCommandBuffer.Dispose();
m_ShadowCommandBuffer = null;
}
}
}

18.9 雾效处理系统

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
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;

public class FogProcessingSystem : MonoBehaviour
{
[Header("Fog Processing Settings")]
public bool enableFogProcessing = true;
public bool visualizeFog = true;
public bool logFogData = true;

[Header("Fog Configuration")]
public FogMode fogMode = FogMode.ExponentialSquared;
public Color fogColor = Color.gray;
public float fogDensity = 0.01f;
public float fogStartDistance = 0.0f;
public float fogEndDistance = 300.0f;
public AnimationCurve fogDistanceCurve = AnimationCurve.Linear(0, 0, 1, 1);
public float fogHeight = 0.0f;
public float fogHeightFalloff = 1.0f;

[Header("Performance Settings")]
public float fogUpdateInterval = 0.1f;
public bool enableFogCulling = true;
public bool enableFogLOD = true;

private CommandBuffer m_FogCommandBuffer;
private Material m_FogMaterial;
private Camera m_Camera;
private float m_LastFogUpdate = 0f;
private List<Renderer> m_FogAffectedRenderers = new List<Renderer>();

[System.Serializable]
public class FogAnalysisData
{
public FogMode fogMode;
public Color fogColor;
public float fogDensity;
public float fogStartDistance;
public float fogEndDistance;
public float fogHeight;
public float fogHeightFalloff;
public int affectedRenderers;
public float averageFogDistance;
public float maxFogDistance;
public bool isVolumetricFog;
public System.DateTime lastUpdate;
}

[System.Serializable]
public class FogPerformanceData
{
public int fogAffectedDrawCalls;
public float fogUpdateTimeMS;
public int fogAffectedObjects;
public float fogMemoryUsageKB;
public bool isOptimal;
}

public FogAnalysisData analysisData = new FogAnalysisData();
public FogPerformanceData performanceData = new FogPerformanceData();

void Start()
{
if (enableFogProcessing)
{
InitializeFogProcessing();
}
}

void Update()
{
if (enableFogProcessing && Time.time - m_LastFogUpdate >= fogUpdateInterval)
{
UpdateFogProcessing();
m_LastFogUpdate = Time.time;
}
}

private void InitializeFogProcessing()
{
m_Camera = Camera.main;
if (m_Camera == null)
{
m_Camera = FindObjectOfType<Camera>();
}

CreateFogCommandBuffer();
CreateFogMaterial();
FindFogAffectedRenderers();

if (logFogData)
{
Debug.Log("[FogProcessingSystem] Initialized fog processing system");
}
}

private void CreateFogCommandBuffer()
{
if (m_FogCommandBuffer != null)
{
m_FogCommandBuffer.Clear();
}
else
{
m_FogCommandBuffer = new CommandBuffer();
m_FogCommandBuffer.name = "Fog Processing";
}
}

private void CreateFogMaterial()
{
// 创建雾效材质
string shaderSource = @"
Shader ""Hidden/FogEffect""
{
Properties
{
_MainTex (""Texture"", 2D) = ""white"" {}
_FogColor (""Fog Color"", Color) = (0.5, 0.5, 0.5, 1)
_FogDensity (""Fog Density"", Range(0, 0.1)) = 0.01
_FogStart (""Fog Start"", Float) = 0.0
_FogEnd (""Fog End"", Float) = 300.0
_FogHeight (""Fog Height"", Float) = 0.0
_FogHeightFalloff (""Fog Height Falloff"", Range(0.01, 10)) = 1.0
}
SubShader
{
Tags { ""RenderType""=""Opaque"" }
LOD 100

Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include ""UnityCG.cginc""

struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
float3 worldPos : TEXCOORD1;
};

struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
float3 worldPos : TEXCOORD1;
};

sampler2D _MainTex;
float4 _MainTex_TexelSize;
fixed4 _FogColor;
float _FogDensity;
float _FogStart;
float _FogEnd;
float _FogHeight;
float _FogHeightFalloff;

v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = v.uv;
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
return o;
}

fixed4 frag (v2f i) : SV_Target
{
fixed4 col = tex2D(_MainTex, i.uv);

// 计算雾效强度
float distance = length(_WorldSpaceCameraPos - i.worldPos);
float fogFactor = 1.0;

if (_FogEnd > _FogStart)
{
float normalizedDistance = saturate((distance - _FogStart) / (_FogEnd - _FogStart));

// 根据雾效模式计算
if (unity_FogMode == 1) // Linear
{
fogFactor = 1.0 - normalizedDistance;
}
else if (unity_FogMode == 2) // Exp
{
fogFactor = exp(-_FogDensity * distance);
}
else // Exp2
{
float expDistance = _FogDensity * distance;
fogFactor = exp(-expDistance * expDistance);
}
}

// 应用高度雾效
float heightFog = 1.0 - saturate((i.worldPos.y - _FogHeight) * _FogHeightFalloff);
fogFactor *= heightFog;

fogFactor = saturate(fogFactor);

// 混合雾效
col.rgb = lerp(_FogColor.rgb, col.rgb, fogFactor);

return col;
}
ENDCG
}
}
}";

// 注意:在实际项目中,您需要创建一个真正的Shader资源
// 这里仅作示例,实际使用时请创建Shader文件
}

private void FindFogAffectedRenderers()
{
m_FogAffectedRenderers.Clear();

var allRenderers = FindObjectsOfType<Renderer>();
foreach (var renderer in allRenderers)
{
if (renderer != null && renderer.enabled &&
Vector3.Distance(renderer.transform.position, m_Camera.transform.position) <= fogEndDistance)
{
m_FogAffectedRenderers.Add(renderer);
}
}
}

private void UpdateFogProcessing()
{
// 更新Unity内置雾效参数
RenderSettings.fog = true;
RenderSettings.fogMode = fogMode;
RenderSettings.fogColor = fogColor;
RenderSettings.fogDensity = fogDensity;
RenderSettings.fogStartDistance = fogStartDistance;
RenderSettings.fogEndDistance = fogEndDistance;

// 分析雾效数据
AnalyzeFogData();

// 优化雾效性能
OptimizeFogPerformance();
}

private void AnalyzeFogData()
{
analysisData.fogMode = fogMode;
analysisData.fogColor = fogColor;
analysisData.fogDensity = fogDensity;
analysisData.fogStartDistance = fogStartDistance;
analysisData.fogEndDistance = fogEndDistance;
analysisData.fogHeight = fogHeight;
analysisData.fogHeightFalloff = fogHeightFalloff;
analysisData.affectedRenderers = m_FogAffectedRenderers.Count;
analysisData.isVolumetricFog = fogHeightFalloff > 0.0f;
analysisData.lastUpdate = System.DateTime.Now;

// 计算平均和最大雾效距离
float totalDistance = 0f;
float maxDistance = 0f;

foreach (var renderer in m_FogAffectedRenderers)
{
float distance = Vector3.Distance(renderer.transform.position, m_Camera.transform.position);
totalDistance += distance;
maxDistance = Mathf.Max(maxDistance, distance);
}

analysisData.averageFogDistance = analysisData.affectedRenderers > 0 ?
totalDistance / analysisData.affectedRenderers : 0f;
analysisData.maxFogDistance = maxDistance;

if (logFogData)
{
Debug.Log($"[Fog Analysis] Affected renderers: {analysisData.affectedRenderers}, " +
$"Avg distance: {analysisData.averageFogDistance:F2}, Max distance: {analysisData.maxFogDistance:F2}");
}
}

private void OptimizeFogPerformance()
{
// 根据受影响对象数量调整性能设置
if (m_FogAffectedRenderers.Count > 100 && enableFogCulling)
{
// 如果受影响对象过多,启用更积极的剔除
foreach (var renderer in m_FogAffectedRenderers)
{
// 可以在这里实现更复杂的剔除逻辑
if (renderer != null)
{
// 降低远距离对象的细节
float distance = Vector3.Distance(renderer.transform.position, m_Camera.transform.position);
if (distance > fogEndDistance * 0.8f && enableFogLOD)
{
// 可以在这里调整LOD或渲染质量
}
}
}
}
}

// 获取雾效分析数据
public FogAnalysisData GetFogAnalysisData()
{
return analysisData;
}

// 获取雾效性能数据
public FogPerformanceData GetFogPerformanceData()
{
return performanceData;
}

// 更新雾效参数
public void UpdateFogParameters(FogMode mode, Color color, float density,
float startDistance, float endDistance, float height, float heightFalloff)
{
fogMode = mode;
fogColor = color;
fogDensity = Mathf.Clamp(density, 0.001f, 0.1f);
fogStartDistance = Mathf.Max(0f, startDistance);
fogEndDistance = Mathf.Max(fogStartDistance + 1f, endDistance);
fogHeight = height;
fogHeightFalloff = Mathf.Clamp(heightFalloff, 0.01f, 10f);

UpdateFogProcessing();
}

// 获取雾效统计信息
public string GetFogStats()
{
var stats = new System.Text.StringBuilder();
stats.AppendLine("=== Fog Statistics ===");
stats.AppendLine($"Fog Mode: {analysisData.fogMode}");
stats.AppendLine($"Fog Color: {analysisData.fogColor}");
stats.AppendLine($"Fog Density: {analysisData.fogDensity}");
stats.AppendLine($"Fog Start: {analysisData.fogStartDistance}");
stats.AppendLine($"Fog End: {analysisData.fogEndDistance}");
stats.AppendLine($"Affected Renderers: {analysisData.affectedRenderers}");
stats.AppendLine($"Average Distance: {analysisData.averageFogDistance:F2}");
stats.AppendLine($"Max Distance: {analysisData.maxFogDistance:F2}");
stats.AppendLine($"Is Volumetric: {analysisData.isVolumetricFog}");
stats.AppendLine($"Last Update: {analysisData.lastUpdate}");

return stats.ToString();
}

// 获取当前雾效设置
public string GetCurrentFogSettings()
{
return $"Mode: {fogMode}, Color: {fogColor}, Density: {fogDensity}, " +
$"Start: {fogStartDistance}, End: {fogEndDistance}, Height: {fogHeight}";
}

// 动态调整雾效强度
public void SetFogIntensity(float intensity)
{
float clampedIntensity = Mathf.Clamp01(intensity);
Color adjustedColor = new Color(
fogColor.r * clampedIntensity,
fogColor.g * clampedIntensity,
fogColor.b * clampedIntensity,
fogColor.a
);

UpdateFogParameters(fogMode, adjustedColor, fogDensity,
fogStartDistance, fogEndDistance, fogHeight, fogHeightFalloff);
}

// 添加雾效影响的渲染器
public void AddFogAffectedRenderer(Renderer renderer)
{
if (renderer != null && !m_FogAffectedRenderers.Contains(renderer))
{
m_FogAffectedRenderers.Add(renderer);
}
}

// 移除雾效影响的渲染器
public void RemoveFogAffectedRenderer(Renderer renderer)
{
if (renderer != null)
{
m_FogAffectedRenderers.Remove(renderer);
}
}

void OnDestroy()
{
if (m_FogCommandBuffer != null)
{
m_FogCommandBuffer.Dispose();
m_FogCommandBuffer = null;
}
}
}

实践练习

18.10 练习1:自定义BRDF实现

目标:创建一个自定义的BRDF着色器

步骤

  1. 编写自定义BRDF函数
  2. 集成到URP着色器中
  3. 实现PBR光照模型
  4. 测试不同材质参数
  5. 优化性能表现

实现要点

  • 正确的物理模型
  • 性能优化策略
  • 参数可调节性

18.11 练习2:阴影处理优化

目标:实现阴影处理优化系统

步骤

  1. 创建阴影分析工具
  2. 实现阴影参数调整
  3. 添加性能监控
  4. 测试不同场景下的表现
  5. 验证优化效果

优化方面

  • 阴影距离管理
  • 阴影偏移调整
  • 性能平衡

18.12 练习3:雾效系统扩展

目标:扩展雾效处理系统

步骤

  1. 实现多种雾效模式
  2. 添加高度雾效支持
  3. 集成性能优化
  4. 测试视觉效果
  5. 验证性能表现

扩展功能

  • 体积雾效
  • 动态雾效
  • 性能自适应

18.13 练习4:GI数据处理

目标:实现GI数据获取和处理

步骤

  1. 学习GI数据获取方法
  2. 实现Lightmap采样
  3. 集成Light Probe系统
  4. 测试间接光照效果
  5. 优化GI性能

处理策略

  • 烘焙GI集成
  • 实时GI处理
  • 性能权衡

18.14 练习5:综合Shader系统

目标:创建综合的Shader处理系统

步骤

  1. 集成所有Shader功能
  2. 实现统一管理接口
  3. 添加性能监控
  4. 测试各种效果
  5. 优化整体性能

系统特性

  • 模块化设计
  • 性能优化
  • 易用性

总结

第18章详细解析了URP的Shader Library,包括Core.hlsl和Lighting.hlsl的结构、BRDF函数的实现、阴影采样函数、雾效计算以及GI数据获取等核心组件。Shader Library是URP渲染系统的基础,提供了标准化的渲染函数和工具,使开发者能够专注于具体的渲染逻辑而无需重复实现基础功能。

关键要点总结:

  1. Core.hlsl:包含渲染管线通用函数和数据结构
  2. Lighting.hlsl:专门处理光照计算的函数库
  3. BRDF实现:PBR渲染的核心物理模型
  4. 阴影处理:多种阴影类型和采样方法
  5. 雾效系统:不同类型的雾效计算和应用
  6. GI数据:全局光照数据的获取和处理

理解Shader Library的内部实现有助于更好地利用URP提供的功能,并在需要时进行自定义扩展。掌握这些基础组件是进行高级渲染开发的前提。

至此,我们已经完成了URP底层原理与源码分析部分的所有章节。接下来将进入第四部分:URP高级定制与优化,学习如何在实际项目中应用这些知识进行定制和优化。