1. URP全局光照技术全景解析
在Unity的通用渲染管线(URP)中实现高质量的全局光照(GI)效果,是每个3D项目提升视觉品质的关键环节。作为从内置管线迁移过来的开发者,我花了整整三个月时间才完全摸清URP中GI系统的运作机制。与内置管线相比,URP的GI实现既有继承也有创新,主要体现在三个核心模块:
- 烘焙光照贴图(Lightmap):通过预计算将静态物体的光照信息存储在纹理中
- 光照探针(Light Probes):为动态物体提供基于球谐函数的间接光照采样
- 反射探针(Reflection Probes):捕捉环境反射信息,实现表面材质的环境反射
重要提示:URP 12+版本开始支持Enlighten和Progressive Lightmapper两种烘焙器,但2023年起官方已移除Enlighten支持,建议统一使用Progressive Lightmapper以获得更精确的光照烘焙效果。
1.1 光照贴图的工作原理
当我们在场景中放置好光源和物体后,Unity的光照烘焙系统会执行以下计算流程:
- 场景体素化:将3D空间划分为均匀的体素网格,每个体素存储几何和材质信息
- 光线发射:从每个光源发射数百万条光线,追踪其在场景中的传播路径
- 辐射度计算:根据光线与表面的交互(漫反射、镜面反射等)计算最终辐射亮度
- 纹理生成:将计算结果压缩存储为光照贴图纹理(通常是EXR格式)
在Shader中采样光照贴图时,需要特别注意UV2通道的使用。以下是标准工作流:
shader复制// 在顶点着色器中传递UV2坐标
struct Attributes {
float4 positionOS : POSITION;
float2 uv : TEXCOORD0;
float2 uv2 : TEXCOORD1; // 光照贴图UV
};
struct Varyings {
float2 uv : TEXCOORD0;
float2 lightmapUV : TEXCOORD1;
float4 positionCS : SV_POSITION;
};
Varyings vert(Attributes input) {
Varyings output;
// 传递UV2坐标
output.lightmapUV = input.uv2 * unity_LightmapST.xy + unity_LightmapST.zw;
// ...其他计算
return output;
}
1.2 实时GI的演进与局限
URP中的实时GI主要通过两种探针系统实现:
光照探针技术参数对比表
| 参数类型 | 低配方案 | 高配方案 |
|---|---|---|
| 探针密度 | 每5-8米一个 | 每2-3米一个 |
| 球谐阶数 | L2 (9个系数) | L3 (16个系数) |
| 更新频率 | 每帧位置更新 | 实时动态混合 |
| 内存占用 | 约0.5MB/100探针 | 约2MB/100探针 |
反射探针的配置黄金法则:
- 场景入口处必须放置探针
- 每个封闭空间至少1个探针
- 大型开放区域采用2-3层混合探针
- 重要反射表面(如水面)附近需增加探针密度
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. Lightmap高效烘焙实战
2.1 烘焙参数的科学配置
在URP项目中打开Lighting窗口(Window > Rendering > Lighting),关键参数设置如下:
-
Lightmapper选择:
- Progressive CPU:适合开发机快速迭代
- Progressive GPU:需要NVIDIA显卡,速度快5-10倍
-
烘焙质量预设:
csharp复制// 推荐的自定义设置(C#脚本自动配置) Lightmapping.lightingSettings = new LightingSettings { lightmapper = Lightmapper.ProgressiveGPU, indirectResolution = 2.0f, // 每单位2个texel lightmapResolution = 40, // 每单位40像素 padding = 4, // 4像素间隔 maxAtlasSize = 4096, // 4K光照贴图集 directSampleCount = 32, indirectSampleCount = 512, environmentSampleCount = 256 }; -
物体标记规范:
- 静态物体:勾选Contribute GI
- 动态物体:添加Light Probe Group组件
- 高光物体:额外添加Reflection Probe
避坑指南:遇到烘焙结果出现漏光或黑斑时,优先检查模型UV2是否重叠。可以使用Window > Analysis > Model Importer查看UV2分布。
2.2 光照贴图压缩优化
URP支持三种光照贴图压缩格式,性能对比:
- DXT1:4bpp,适合移动端,但会有色带问题
- BC6H:8bpp,HDR格式,PC/主机推荐
- RGBM:32bpp,最高质量,适合影视级项目
在Shader中解码时需要注意匹配压缩格式:
shader复制// BC6H解码示例
half3 DecodeLightmapBC6H(float4 encoded) {
return DecodeHDREnvironment(encoded, unity_Lightmap_HDR);
}
// RGBM解码示例
half3 DecodeLightmapRGBM(float4 encoded) {
encoded.rgb *= (encoded.a * 8.0);
return encoded.rgb * encoded.rgb;
}
3. 探针系统的Shader集成方案
3.1 光照探针的球谐函数实现
URP使用三阶球谐函数(L2)存储间接光照,在Shader中需要特殊处理:
shader复制// 球谐光照计算核心函数
half3 SampleSH(half3 normalWS) {
// L0项
half3 color = unity_SHAr.w * unity_SHAr.xyz;
color += unity_SHAg.w * unity_SHAg.xyz;
color += unity_SHAb.w * unity_SHAb.xyz;
// L1项
half3 v1 = unity_SHAr.xyz;
half3 v2 = unity_SHAg.xyz;
half3 v3 = unity_SHAb.xyz;
color += (v1 * normalWS.x + v2 * normalWS.y + v3 * normalWS.z);
// L2项
half3 r = normalWS.xyz * normalWS.xyz;
half3 g = normalWS * normalWS.yzx;
color += (unity_SHC.rgb * (r.x + r.y - r.z));
color += (unity_SHC.a * (g.x - g.y));
return max(0, color);
}
3.2 反射探针的混合策略
复杂场景中往往需要混合多个反射探针,推荐使用Box Projection技术:
shader复制// 盒投影反射采样
half3 SampleReflectionProbe(float3 positionWS, float3 normalWS) {
float3 boxMin = unity_SpecCube0_BoxMin;
float3 boxMax = unity_SpecCube0_BoxMax;
float3 probePos = unity_SpecCube0_ProbePosition;
// 计算盒投影向量
float3 dir = positionWS - probePos;
dir = lerp(dir, normalWS, unity_SpecCube0_BoxMin.w);
float3 factors = ((dir > 0 ? boxMax : boxMin) - positionWS) / dir;
float scalar = min(min(factors.x, factors.y), factors.z);
float3 uvw = positionWS + dir * scalar;
// 采样立方体贴图
half4 encoded = SAMPLE_TEXTURECUBE_LOD(unity_SpecCube0, samplerunity_SpecCube0, uvw, 0);
return DecodeHDREnvironment(encoded, unity_SpecCube0_HDR);
}
探针混合权重计算表
| 影响因素 | 计算公式 | 参数说明 |
|---|---|---|
| 距离衰减 | 1 - (d/d_max)^2 | d: 到探针中心的距离 |
| 角度衰减 | dot(N, L)^2 | N: 法线, L: 到探针向量 |
| 体积重叠 | 1 - (overlap_ratio)^0.5 | 多个探针重叠区域占比 |
4. 性能优化与疑难排解
4.1 常见问题速查手册
光照贴图问题排查表
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| 纹理接缝明显 | UV2展开不正确 | 检查Model Importer中的UV2 |
| 光照过暗 | 曝光补偿未设置 | 调整Post-processing Volume |
| 动态物体无光照 | 未放置光照探针 | 增加探针密度 |
| 反射闪烁 | 探针更新频率过高 | 改为OnAwake或手动更新 |
| 烘焙时间过长 | 间接光照采样数过高 | 降低indirectSampleCount |
4.2 移动端优化技巧
-
光照贴图降级策略:
csharp复制#if UNITY_IOS || UNITY_ANDROID lightingSettings.lightmapResolution = 20; lightingSettings.indirectResolution = 1.0f; lightingSettings.maxAtlasSize = 2048; #endif -
探针数据精简方案:
- 使用L1球谐代替L2(减少9→4个系数)
- 反射探针改用128x128分辨率
- 禁用实时更新功能
-
Shader变种控制:
shader复制#pragma multi_compile _ LIGHTMAP_ON #pragma multi_compile _ _MIXED_LIGHTING_SUBTRACTIVE #pragma multi_compile _ _REFLECTION_PROBE_BOX_PROJECTION
实战经验:在小米10上测试发现,启用Box Projection会使反射计算开销增加35%,建议中低端设备关闭此功能。
5. 高级技巧与未来演进
5.1 动态GI的替代方案
对于需要动态光照又希望保持GI效果的场景,可以考虑:
-
Light Probe Proxy Volume(LPPV):
csharp复制GameObject obj = new GameObject("LPPV"); var volume = obj.AddComponent<LightProbeProxyVolume>(); volume.resolutionMode = LightProbeProxyVolume.ResolutionMode.High; volume.gridResolutionX = 16; volume.gridResolutionY = 8; volume.gridResolutionZ = 16; -
屏幕空间全局光照(SSGI):
URP的SSGI后处理效果虽然性能消耗较大(约3-5ms @1080p),但能实现动态物体的间接光照交互。 -
光线追踪降级方案:
通过Raymarching实现简版光线追踪,适合高端移动设备:shader复制half3 TraceIndirectLight(float3 origin, float3 direction) { const int steps = 8; const float stepSize = 0.5; for(int i=0; i<steps; i++) { float3 p = origin + direction * i * stepSize; float d = SDF(p); // 有符号距离场查询 if(d < 0.01) { return SampleLightProbe(p); } } return 0; }
5.2 与Shader Graph的深度集成
URP 2022版本后,Shader Graph新增了多个GI相关节点:
-
Lightmap采样节点:
- 自动处理不同平台的光照贴图解码
- 支持动态切换烘焙/实时光照
-
探针混合节点:
- 提供可视化权重调节界面
- 内置盒投影开关控制
-
环境光遮蔽融合:
shader复制// 在Shader Graph中组合AO和GI float3 finalGI = SampleLightmap() * SampleOcclusion();
经过三个大型项目的实战验证,这套URP全局光照方案在PC端可达到接近HDRP的视觉效果,而在移动端也能保持60fps的稳定性能。关键是要根据目标平台合理配置烘焙参数和Shader特性,记住:没有最好的配置,只有最适合项目需求的配置。
