在Unity引擎的发展历程中,光照贴图技术经历了多次重大迭代。作为项目中的核心渲染技术,理解其演进过程对开发者合理选择技术方案至关重要。
早期Unity版本采用的Enlighten光照系统存在明显的技术限制:
当时典型的解决方案是在Mesh Renderer组件中标记"Lightmap Static"属性,但这种方案无法满足现代游戏对动态光影交互的需求。
Universal Render Pipeline(URP)的引入带来了全新的轻量级烘焙管线:
关键配置参数:
csharp复制// URP中混合光源的典型设置
light.lightmapBakeType = LightmapBakeType.Mixed;
light.shadows = LightShadows.Soft;
High Definition Render Pipeline(HDRP)推动了光照贴图技术向更高精度发展:
URP随后吸收了HDRP的部分技术成果,例如:
技术参数对比表:
| 特性 | Enlighten | URP | HDRP |
|---|---|---|---|
| 动态物体支持 | 仅探针 | 混合模式 | 完整支持 |
| 烘焙时间 | 长 | 中等 | 较长 |
| 内存占用 | 高 | 低 | 非常高 |
| 适用平台 | 全平台 | 移动/中端 | 高端/PC |
完整的静态物体光照烘焙包含三个核心环节:
标记静态属性
UV准备
参数调整
csharp复制// 静态物体烘焙设置脚本
[ExecuteInEditMode]
public class StaticBaker : MonoBehaviour {
[Range(0.1f, 2.0f)]
public float lightmapScale = 1.0f;
void OnEnable() {
var renderer = GetComponent<Renderer>();
renderer.lightmapScaleOffset = new Vector4(lightmapScale, lightmapScale, 0, 0);
renderer.lightmapIndex = 0;
// 自动调整接收间接光照强度
renderer.reflectionProbeUsage = UnityEngine.Rendering.ReflectionProbeUsage.BlendProbes;
}
}
问题1:光照贴图出现接缝
问题2:烘焙后物体过亮/过暗
问题3:烘焙时间过长
现代Unity项目通常采用以下技术组合实现动态物体光照:
Light Probe网络
混合光照模式
反射探针补充
csharp复制// 动态物体光照适配组件
[RequireComponent(typeof(Renderer))]
public class DynamicLighting : MonoBehaviour {
private Renderer _renderer;
void Start() {
_renderer = GetComponent<Renderer>();
_renderer.lightProbeUsage = LightProbeUsage.BlendProbes;
}
void Update() {
// 获取当前位置的光照探针数据
LightProbes.GetInterpolatedProbe(
transform.position,
_renderer,
out var probe
);
// 应用探针数据
_renderer.additionalLightmapSettings.lightProbeVolume = probe;
// 动态调整接收光照强度
_renderer.lightmapScaleOffset = CalculateDynamicScale();
}
Vector4 CalculateDynamicScale() {
// 根据物体运动速度调整光照强度
float speedFactor = Mathf.Clamp(rigidbody.velocity.magnitude / 10f, 0.5f, 1.2f);
return new Vector4(speedFactor, speedFactor, 0, 0);
}
}
探针布置策略
实时阴影优化
材质优化
通过PrefabLightmapsData脚本实现跨场景光照数据保持:
csharp复制[ExecuteInEditMode]
public class PrefabLightmapsData : MonoBehaviour {
[System.Serializable]
struct RendererInfo {
public Renderer renderer;
public int lightmapIndex;
public Vector4 lightmapOffsetScale;
}
[SerializeField]
RendererInfo[] m_RendererInfo;
public void SaveLightmapData() {
var renderers = GetComponentsInChildren<Renderer>();
m_RendererInfo = new RendererInfo[renderers.Length];
for (int i = 0; i < renderers.Length; i++) {
m_RendererInfo[i] = new RendererInfo {
renderer = renderers[i],
lightmapIndex = renderers[i].lightmapIndex,
lightmapOffsetScale = renderers[i].lightmapScaleOffset
};
}
}
void Awake() {
if (m_RendererInfo == null || m_RendererInfo.Length == 0)
return;
foreach (var info in m_RendererInfo) {
info.renderer.lightmapIndex = info.lightmapIndex;
info.renderer.lightmapScaleOffset = info.lightmapOffsetScale;
}
}
}
通过Rendering Layer Mask实现精细的光照影响控制:
csharp复制// 设置光源只影响特定层级的物体
light.renderingLayerMask = 1 << layerIndex;
// 在Renderer上设置对应的接收层级
renderer.renderingLayerMask = 1 << layerIndex;
根据物体重要性实施分级控制:
| 物体类型 | Scale In Lightmap | 备注 |
|---|---|---|
| 主角周边 | 1.0-2.0 | 高细节区域 |
| 中景物体 | 0.5-1.0 | 适度降低 |
| 远景物体 | 0.1-0.5 | 最低配置 |
| 地形 | 0.3-0.8 | 根据面积调整 |
大场景分区域烘焙的工作流程:
csharp复制// 区域烘焙控制脚本
public class ChunkBaker : MonoBehaviour {
public GameObject[] chunks;
public int currentChunkIndex;
void BakeNextChunk() {
if (currentChunkIndex >= chunks.Length) return;
// 禁用非当前块的所有渲染器
foreach (var chunk in chunks) {
chunk.SetActive(false);
}
chunks[currentChunkIndex].SetActive(true);
// 执行烘焙
Lightmapping.BakeAsync();
currentChunkIndex++;
}
}
在实际项目中,我发现合理设置Scale In Lightmap参数对性能影响最大。一个2000个物体的场景,将远景物体的Scale值从1.0降到0.2,可以节省40%的光照贴图内存,而视觉质量损失几乎不可察觉。