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中不同视角的独立光照逻辑

触发条件

该宏的激活需要同时满足以下条件:

  1. 项目设置启用

    • URP/HDRP Asset 中开启光源层支持:
      • URP: Lighting > Light Layers > Enable Light Layers
      • HDRP: Lighting > Light Layers
  2. Shader声明
    在着色器中通过#pragma指令声明支持:

    #pragma multi_compile _ _LIGHT_LAYERS
    
  3. 光源与物体配置

    • 光源的 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

配置步骤

  1. 启用光源层系统

    • URP: Project Settings > URP Global Settings > Lighting > Light Layers > Enable
    • HDRP: HDRP Asset > Lighting > Light Layers
  2. 设置光源层级

    • 选择光源,在Inspector中设置 Light Layer Mask
  3. 设置物体层级

    • 选择物体,在Mesh Renderer中设置 Rendering Layer Mask
  4. 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:层过滤不生效

  • 诊断步骤
    1. 确认项目设置中光源层系统已启用
    2. 检查光源和物体的层掩码是否有重叠位
    3. 验证Shader中是否正确定义了_LIGHT_LAYERS

问题2:层掩码显示异常

  • 现象:层选择UI显示错乱
  • 解决方案
    • 清除项目Library文件夹
    • 检查层名称定义是否冲突(Edit > Project Settings > Tags and Layers

问题3:跨平台兼容性问题

  • 表现:移动端层过滤失效
  • 修正方案
    • 确保Shader中正确使用UNITY_LIGHT_LAYERS宏(移动端可能需要特殊处理)
    • 检查图形API是否支持位运算(如OpenGL ES 2.0需降级实现)

进阶用法示例

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. 层级规划原则

    • 按功能划分:角色层(1)、环境层(2)、特效层(3)等
    • 预留扩展位(至少保留2-3个未使用层)
  2. 性能关键路径

    • 将高频光源(如玩家手电筒)设为独立层
    • 静态环境光源使用全层或专用层
  3. 团队协作规范

    • 建立层使用文档,明确各bit对应功能
    • 使用[RenderingLayerMask]属性自定义Inspector显示
// 自定义层标签显示
public class RenderingLayerMaskAttribute : PropertyAttribute {}

[CustomPropertyDrawer(typeof(RenderingLayerMaskAttribute))]
public class RenderingLayerMaskDrawer : PropertyDrawer {
    // 实现类似LayerMask的BitField UI
}

总结

_LIGHT_LAYERS 是Unity现代渲染管线中实现 精准光源控制 的核心机制,通过位掩码技术为复杂场景提供高效的光照筛选能力。开发者可通过:

  1. 合理规划光源与物体的层分配
  2. 编写条件敏感的光照着色器
  3. 结合动态脚本控制

实现从性能优化到高级视觉效果的多重目标,特别适用于MMO、开放世界等需要精细光照管理的项目类型。

kumakoko avatar
kumakoko
pure coder
comments powered by Disqus