Unity3D的内置宏 _LIGHT_LAYERS
Table of Contents
Unity3D的内置宏 ADDITIONAL_LIGHTS_VERTEX
请尊重原作者的工作,转载时请务必注明转载自:www.xionggf.com
在Unity URP/HDRP中,_LIGHT_LAYERS
是一个与 光源层级过滤(Light Layer Masking) 相关的内置变体关键字(Shader Variant Keyword)。它的核心作用是 实现基于层级的光源与物体交互控制,允许开发者通过层掩码(Layer Masks)精确控制哪些光源可以影响特定物体。
核心含义
1. 功能定位
- 光源筛选机制:激活该宏后,物体会根据自身的 Light Layer Mask 和光源的 Light Layer Mask 进行匹配,只有匹配层的光源才会对该物体产生光照影响。
- 渲染优化:通过层级过滤减少无效光源计算,提升复杂场景性能。
2. 技术目标
- 精细化光源控制:例如让装饰性灯光只影响场景道具,不影响角色
- 多世界渲染支持:用于分屏游戏或VR中不同视角的独立光照逻辑
触发条件
该宏的激活需要同时满足以下条件:
-
项目设置启用
- URP/HDRP Asset 中开启光源层支持:
- URP: Lighting > Light Layers > Enable Light Layers
- HDRP: Lighting > Light Layers
- URP/HDRP Asset 中开启光源层支持:
-
Shader声明
在着色器中通过#pragma
指令声明支持:#pragma multi_compile _ _LIGHT_LAYERS
-
光源与物体配置
- 光源的 Light Layer Mask 设置为非"Everything"值
- 物体的 Renderer组件 > Rendering Layer Mask 设置为与光源匹配的层级
技术实现机制
1. 数据传递流程
// 在片元着色器中筛选有效光源
#if defined(_LIGHT_LAYERS)
// 获取物体的渲染层掩码(8位/32位,取决于设置)
uint renderingLayers = GetMeshRenderingLayer(); // 例如0b00000101
// 光源层掩码与物体层掩码进行按位与操作
if ((light.layerMask & renderingLayers) != 0) {
// 该光源影响当前物体
ApplyLighting(light);
}
#endif
2. 核心函数
GetMeshRenderingLayer()
:获取当前片元所属物体的层掩码GetLightLayerMask(lightIndex)
:获取指定光源的层掩码
应用场景
1. 选择性光照影响
物体类型 | 层掩码 | 光源类型 | 层掩码 | 交互结果 |
---|---|---|---|---|
角色模型 | 0b00000001 | 角色聚光灯 | 0b00000001 | 影响 |
环境装饰物 | 0b00000010 | 角色聚光灯 | 0b00000001 | 不影响 |
全场景物体 | 0b11111111 | 全局方向光 | 0b00000100 | 不影响(掩码不匹配) |
2. 多玩家分屏
- 玩家1视角:光源层=0b0001,角色层=0b0001
- 玩家2视角:光源层=0b0010,角色层=0b0010
- 实现各玩家只能看到自己角色的光照效果
3. 特效隔离
- 爆炸特效光源层=0b0100
- 重要NPC渲染层=0b0100
- 确保爆炸光效仅影响特定NPC
配置步骤
-
启用光源层系统
- URP: Project Settings > URP Global Settings > Lighting > Light Layers > Enable
- HDRP: HDRP Asset > Lighting > Light Layers
-
设置光源层级
- 选择光源,在Inspector中设置 Light Layer Mask
-
设置物体层级
- 选择物体,在Mesh Renderer中设置 Rendering Layer Mask
-
Shader适配
// 添加层筛选逻辑 Light mainLight = GetMainLight(input.positionWS); #if defined(_LIGHT_LAYERS) if (IsMatchingLayer(mainLight.layerMask, input.renderingLayer)) { color += CalculateLightContribution(mainLight); } #else color += CalculateLightContribution(mainLight); // 无条件应用 #endif
性能影响与优化
模式 | CPU开销 | GPU开销 | 内存占用 | 适用场景 |
---|---|---|---|---|
启用光源层 | 低 | 降低 | 轻微增加 | 多光源复杂场景 |
禁用光源层 | 最低 | 高 | 最低 | 简单场景/全光源影响需求 |
优化建议:
- 将高频变化光源设为全层级(0b11111111),避免频繁层检测
- 使用8位层掩码(默认)而非32位以节省带宽
- 通过脚本动态更新层掩码,而非每帧检测
常见问题与解决方案
问题1:层过滤不生效
- 诊断步骤:
- 确认项目设置中光源层系统已启用
- 检查光源和物体的层掩码是否有重叠位
- 验证Shader中是否正确定义了
_LIGHT_LAYERS
宏
问题2:层掩码显示异常
- 现象:层选择UI显示错乱
- 解决方案:
- 清除项目Library文件夹
- 检查层名称定义是否冲突(Edit > Project Settings > Tags and Layers)
问题3:跨平台兼容性问题
- 表现:移动端层过滤失效
- 修正方案:
- 确保Shader中正确使用
UNITY_LIGHT_LAYERS
宏(移动端可能需要特殊处理) - 检查图形API是否支持位运算(如OpenGL ES 2.0需降级实现)
- 确保Shader中正确使用
进阶用法示例
1. 动态层切换
// C#脚本控制物体层
void Update() {
Renderer renderer = GetComponent<Renderer>();
if (isStealthMode) {
renderer.renderingLayerMask = 0b00000001; // 只受潜行光源影响
} else {
renderer.renderingLayerMask = 0b11111111; // 受所有光源影响
}
}
2. 多层混合运算
// 允许同时匹配多个层级(如层1 OR 层3)
#if defined(_LIGHT_LAYERS)
uint requiredLayers = 0b00000101; // 层0和层2
if ((light.layerMask & requiredLayers) != 0) {
// 应用光源
}
#endif
3. 调试可视化
// 通过颜色显示当前层
half4 frag(v2f i) : SV_Target {
uint layers = GetMeshRenderingLayer();
return half4(
(layers & 0b00000001) ? 1 : 0, // 红色=层0
(layers & 0b00000010) ? 1 : 0, // 绿色=层1
(layers & 0b00000100) ? 1 : 0, // 蓝色=层2
1
);
}
与其他系统的协作
系统 | 协作方式 |
---|---|
Light Probes | 光照探针数据不受层过滤影响,需额外处理 |
Shadowmask | 阴影层级需单独通过_SHADOW_LAYERS 控制 |
Shader Graph | 通过Rendering Layer 节点访问层掩码 |
DOTS | 需通过ECS组件同步RenderingLayerMask |
最佳实践
-
层级规划原则
- 按功能划分:角色层(1)、环境层(2)、特效层(3)等
- 预留扩展位(至少保留2-3个未使用层)
-
性能关键路径
- 将高频光源(如玩家手电筒)设为独立层
- 静态环境光源使用全层或专用层
-
团队协作规范
- 建立层使用文档,明确各bit对应功能
- 使用
[RenderingLayerMask]
属性自定义Inspector显示
// 自定义层标签显示
public class RenderingLayerMaskAttribute : PropertyAttribute {}
[CustomPropertyDrawer(typeof(RenderingLayerMaskAttribute))]
public class RenderingLayerMaskDrawer : PropertyDrawer {
// 实现类似LayerMask的BitField UI
}
总结
_LIGHT_LAYERS
是Unity现代渲染管线中实现 精准光源控制 的核心机制,通过位掩码技术为复杂场景提供高效的光照筛选能力。开发者可通过:
- 合理规划光源与物体的层分配
- 编写条件敏感的光照着色器
- 结合动态脚本控制
实现从性能优化到高级视觉效果的多重目标,特别适用于MMO、开放世界等需要精细光照管理的项目类型。