Files
XCEngine/docs/used/Renderer下一阶段_多光源正式化与Lighting闭环计划_完成归档_2026-04-05.md

15 KiB
Raw Blame History

Renderer 下一阶段:多光源正式化与 Lighting 闭环计划

日期:2026-04-05

1. 阶段定位

当前 rendering 主线已经完成了“默认 runtime forward renderer 第一阶段”的主体闭环:

  • SceneRenderer -> CameraRenderer -> RenderPipeline 主链已经落地
  • 主方向光阴影已经由 renderer 自动规划并接入 forward pass
  • SceneView / GameView 已统一接到 runtime renderer 主链
  • object-id 已经是正式的 GPU 路线
  • 三后端已有一批稳定的 rendering integration coverage

因此,当前阶段真正未收口的主线不再是:

  • render graph
  • editor 专属效果扩展
  • 更复杂的后处理系统

而是:

把当前“只支持主方向光”的 lighting runtime推进成一个正式可扩展的多光源 forward runtime。

这份计划只聚焦这一条主线。

2. 为什么现在必须先做多光源

当前源码真实状态已经说明问题:

  • RenderLightingData 仍然只有 mainDirectionalLightmainDirectionalShadow
  • RenderSceneExtractor 只会提取一个主方向光
  • BuiltinForwardPipeline 只消费这一套最小 lighting 数据
  • LightComponent 虽然已经有 Directional / Point / Spot 三种类型,但 runtime renderer 还没有把它们正式接通

这意味着现在的 renderer 已经不是“不能用”,而是:

单方向光路径可用,但正式多光源路径缺失。

如果这时去做 render graph,只是给尚未完全闭环的 runtime lighting 套上更复杂的调度壳。
如果这时去继续堆 editor 视觉功能,也是在建立在不完整 runtime 光照基础之上。

所以正确顺序必须是:

  1. 先把多光源正式化
  2. 再进入 skybox / environment / post-process seam
  3. 最后才考虑 render graph 这类更高层调度框架

3. 与 Unity 风格架构的对齐原则

本阶段继续严格遵守当前工程的核心分层:

  • RHI 只负责 GPU 抽象
  • Renderer 负责 scene extraction、lighting data、pass orchestration、shader/material contract
  • Editor 只作为 renderer 宿主与 overlay 使用方,不拥有独立 runtime lighting 主链

与 Unity 风格对齐时,本阶段遵守以下原则:

  1. 主方向光与 additional lights 属于 runtime renderer 正式能力
  2. editor 的 grid / outline / gizmo / icon 不是本阶段主线
  3. shader/material 仍然走统一 .shader + Pass + backend variants 路线,而不是退回后端硬编码
  4. 当前阶段先做“有限 additional lights 的前向提交”,不直接跳到 clustered / deferred

4. 当前真实现状

4.1 已完成的部分

  • 自动方向光阴影规划已经存在
  • CameraRenderer 已有正式 pass 顺序:
    • preScenePasses
    • shadowCaster
    • depthOnly
    • main pipeline
    • objectId
    • postScenePasses
    • overlayPasses
  • BuiltinForwardPipeline 已形成正式 shader/material/pass contract
  • editor Scene/Game viewport 已经走 runtime renderer 主链
  • object-id picking 已经是正式主路径

4.2 还没收口的部分

  • RenderLightingData 没有正式 additional light 数据模型
  • RenderSceneExtractor 没有 point / spot / additional directional 的提取与排序
  • BuiltinForwardPipeline 没有正式的 additional light GPU 提交路径
  • 当前 forward lit shader 常量组织仍偏“单主光最小路径”
  • integration matrix 中还没有 multi_light_scenespot_light_scene

5. 本阶段总目标

本阶段的目标只有一句话:

建立一个正式、稳定、可测试、三后端一致的 bounded forward multi-light runtime。

收口后应达到:

  1. Directional / Point / Spot 三类光源能通过 runtime renderer 正式影响画面
  2. 主方向光阴影能力保持不回退
  3. 三后端使用同一套 shader/material contract
  4. Scene/Game View 继续直接复用同一条 runtime renderer 光照路径
  5. 为下一阶段 skybox / environment / post-process 留出清晰接入点

6. 非目标

本阶段明确不做:

  • render graph
  • deferred rendering
  • tiled / clustered lighting
  • cascaded shadow maps
  • point / spot shadow
  • GI / IBL / probe / reflection probe
  • PBR 大重构
  • editor 专属渲染效果继续扩张

7. 设计方案

7.1 正式化 lighting 数据模型

建议把当前 RenderLightingData 从:

  • mainDirectionalLight
  • mainDirectionalShadow

扩展为:

  • mainDirectionalLight
  • mainDirectionalShadow
  • additionalLights

其中 additionalLights 的单项建议包含:

  • type
  • enabled
  • color
  • intensity
  • position
  • direction
  • range
  • spotAngle
  • castsShadows

这里的关键原则是:

  1. 主方向光继续单独建模
  2. additional lights 统一为一个正式数组,而不是分散塞进临时常量
  3. 当前阶段允许 additional lights 无阴影
  4. 数据模型要能自然承接后续 per-object light list 或 clustered light list而不是很快被推翻

7.2 限定本阶段的 light strategy

本阶段不追求“无限灯光”,而采用有限 additional lights 前向提交

建议首版规则:

  • 保留 1 个 main directional light
  • 额外提交最多 8additional lights
  • additional lights 可以包含:
    • 非主方向光的 directional
    • point
    • spot

之所以建议先定成 8

  • 与常见 forward runtime 的第一阶段预算接近
  • 常量布局、三后端一致性和 integration GT 更容易稳定
  • 以后即使扩到 16 或改成 per-object list也不会推翻整体 contract

7.3 CPU 侧提取、裁剪与排序策略

RenderSceneExtractor 需要正式负责 additional light 提取。

首版建议策略:

  1. 过滤掉无效灯光
    • component disabled
    • game object inactive
    • 不在 camera culling mask 内
  2. 选择主方向光
    • 仍沿用“最重要的 directional light 单独提升为 main light”的策略
  3. 其余光源进入 additional list
  4. 对 additional list 做稳定排序
  5. 截断到最大上限

为了保证输出稳定、便于 GT 固化,建议排序规则写死为:

  1. additional directional lights 优先
  2. point / spot 在后
  3. 同类型内按影响力降序
  4. 影响力相同则按 object/entity id 稳定排序

影响力计算不必一步到位做复杂物理模型,首版只需要稳定且可解释。建议:

  • directionalintensity
  • point / spotintensity / max(distance^2, epsilon) 的近似影响值

这里先按 camera 视角做 bounded list不在本阶段直接引入 per-object light assignment。

7.4 收紧 shader / material / pass contract

当前 forward path 的一个根本问题是:场景级 lighting 数据仍部分塞在 PerObjectConstants 里。
这对 additional lights 来说不是正式方案。

因此本阶段需要把 forward lit 资源合约收紧为:

  • PerObject
    • 只保留与单个 draw object 强相关的数据
    • 例如 model / inverse model / view / projection
  • PerMaterial
    • 继续承载材质数据
  • Lighting
    • 新增正式 scene/frame lighting constant buffer
    • 包含主光与 additional lights
  • ShadowReceiver
    • 继续承载主方向光阴影矩阵与 shadow 参数
  • BaseColorTexture
  • LinearClampSampler
  • ShadowMapTexture
  • ShadowMapSampler

这一步非常关键,因为它决定后续扩展是否正规:

  1. scene/frame lighting 不再随每个 draw 重复写入 PerObject
  2. additional light 数组有正式宿主
  3. 三后端可以共享同一份高层资源声明

7.5 三后端 shader 路线必须保持统一

本阶段继续沿用当前已经建立起来的 shader 路线:

  • 高层作者视图仍是统一 .shader
  • 每个 Pass 通过 backend variants 指向:
    • OpenGL GLSL
    • Vulkan GLSL
    • D3D12 HLSL

也就是说:

  1. 不在材质系统层面分裂成三套 API 专属 shader 入口
  2. forward lit pass 的资源声明只保留一份 canonical contract
  3. 三后端只在 stage variant 实现层分开

这点必须继续保持,因为它是后续 Unity-like shader authoring 的前提。

7.6 Forward shader 的首版光照职责边界

本阶段只要求把光照主路径打通,不把 PBR 一起打包进来。

建议职责边界:

  • main directional light
    • 支持现有阴影
  • additional directional lights
    • 无阴影
  • point lights
    • 范围衰减
  • spot lights
    • 范围衰减 + 锥角衰减

首版 shading model 以稳定为主:

  • 延续当前 forward lit 的 diffuse / lambert 风格
  • 不在本阶段引入新的 specular / BRDF 复杂度

7.7 Spot light 参数策略

当前 LightComponent 只有一个 spotAngle,没有 innerSpotAngle

为了避免本阶段扩散到组件/序列化/UI 全链路重构,建议首版这样处理:

  1. outer cone 直接使用 spotAngle
  2. inner cone 由 runtime 以固定比例推导
    • 例如 inner = outer * 0.8
  3. 等 shader/material 与 renderer 多光源闭环稳定后,再决定是否把 innerSpotAngle 正式上升为组件字段

这能保证当前阶段先把 runtime 多光源打通,而不是被 editor 数据建模拖慢。

7.8 BuiltinForwardPipeline 的正式改造方向

BuiltinForwardPipeline 本阶段要完成的不只是“shader 多写几行光照循环”,而是正式接管以下职责:

  1. 创建/维护 Lighting 常量资源
  2. 每帧更新一份 bounded additional light 数据
  3. 在 draw 时稳定绑定 LightingShadowReceiver
  4. 保持 Unlit / ObjectId / DepthOnly / ShadowCaster 不被污染

简化后目标是:

  • lit path 正式消费 multi-light 数据
  • unlit path 保持简单
  • object-id / shadow-caster 不承担 lighting 语义

8. 实施分阶段

8.1 Phase ALighting Contract 正式化

目标

把 scene/frame lighting 常量从 PerObject 里拆出来,建立正式 Lighting 合约。

具体工作

  • 重构 forward lit shader 的资源声明
  • 新增 Lighting 常量缓冲结构
  • 将主方向光数据迁移到 Lighting
  • 保留 ShadowReceiver 只处理阴影接收参数
  • 调整 BuiltinForwardPipeline 的 descriptor set 解析与绑定逻辑

验收标准

  • 主方向光现有画面不回退
  • 三后端 forward lit pass 都能正确解析新资源布局
  • unlit / object-id / shadow-caster 不受影响

8.2 Phase BCPU Additional Light Extraction

目标

RenderSceneExtractor 正式产出 bounded additional lights。

具体工作

  • 扩展 RenderLightingData
  • 增加 additional light 数据结构
  • 实现过滤、排序、截断策略
  • 补 unit tests覆盖
    • 主光选择
    • additional list 排序
    • 上限裁剪
    • point / spot 提取正确性

验收标准

  • extractor 可稳定输出 additional light 列表
  • 输出顺序稳定、可预测
  • 现有单主光场景输出保持一致

8.3 Phase CForward Multi-Light Consumption

目标

BuiltinForwardPipeline 和 builtin forward lit shader 正式消费 additional lights。

具体工作

  • 新增 additional light 常量布局
  • 在 shader 中加入 bounded light loop
  • 实现 point / spot 的首版 attenuation
  • 保持 main directional shadow 不回退
  • 为 additional directional / point / spot 明确统一数学路径

验收标准

  • 多灯光真实影响画面
  • 主方向光阴影仍正确
  • 三后端输出保持一致,没有明显分叉

8.4 Phase D正式集成测试补齐

目标

把 multi-light 路径钉死在 integration matrix 中。

新增场景

  • tests/Rendering/integration/multi_light_scene
  • tests/Rendering/integration/spot_light_scene

场景设计要求

multi_light_scene

  • 至少 1 个主方向光
  • 至少 2 个 additional lights
  • 场景几何要让不同灯光覆盖区域明显分离
  • GT 图必须一眼能看出 additional lights 是否生效

spot_light_scene

  • 一个明显的 spot hot area
  • 同时能看到范围边界和角度边界
  • 最好包含一个立体物体和一个地面,避免只看平面亮斑

附加要求

  • 三后端都必须有 GT
  • 除整图 GT 比对外,可补少量像素采样断言,以降低误报成本
  • 必须把 directional_shadow_scenebackpack_lit_scene 一起纳入回归验证

验收标准

  • multi_light_scene 三后端全绿
  • spot_light_scene 三后端全绿
  • 现有 lighting 相关 integration 不回退

8.5 Phase E文档与阶段收口

目标

让本阶段结束后rendering 主线边界重新清晰。

具体工作

  • 更新 tests/TEST_SPEC.md
  • 更新 renderer 相关 API / guide 文档
  • 把本阶段已完成的旧计划归档到 docs/used
  • 输出“多光源阶段收口说明”

验收标准

  • 文档、测试矩阵、实现三者口径一致
  • 下一阶段可以自然承接 skybox / environment / post-process

9. 测试策略

本阶段测试必须分三层执行:

9.1 Unit

  • RenderSceneExtractor multi-light 提取
  • BuiltinForwardPipeline lighting resource layout / descriptor binding
  • light attenuation 数学辅助函数

9.2 Integration

  • multi_light_scene
  • spot_light_scene
  • 回归:
    • directional_shadow_scene
    • backpack_lit_scene
    • camera_stack_scene
    • transparent_material_scene
    • cull_material_scene
    • depth_sort_scene
    • material_state_scene
    • offscreen_scene

9.3 Editor Runtime Smoke

  • SceneView 光照结果与 GameView 主路径一致
  • object-id picking 不因 lighting contract 变更而回退
  • editor overlay 不污染 runtime lighting

10. 风险与控制

风险 1把 multi-light 直接塞进 PerObject

后果:

  • descriptor update 开销恶化
  • 语义继续混乱
  • 后续 skybox / environment / post-process 继续难收口

控制策略:

  • 本阶段第一步就强制建立 Lighting 常量资源

风险 2additional lights 排序不稳定导致 GT 抖动

后果:

  • 三后端图像对比不稳定
  • 回归测试容易出现“偶发错图”

控制策略:

  • 排序规则必须写死
  • 相同影响力要有稳定 tie-breaker

风险 3spot light 参数扩散到组件/UI/序列化全链路

后果:

  • 本阶段失焦
  • runtime 主线再次拖长

控制策略:

  • 首版先用单 spotAngle + 固定 inner ratio

风险 4三后端 shader 细节分叉

后果:

  • D3D12 / OpenGL / Vulkan 结果不一致

控制策略:

  • 高层资源 contract 只保留一份 canonical 声明
  • integration 以三后端同时跑为硬约束

11. 阶段完成判定

满足以下条件时,本阶段可以认为收口:

  1. RenderLightingData 已正式支持 additional lights
  2. BuiltinForwardPipeline 已正式消费 bounded additional lights
  3. multi_light_scenespot_light_scene 三后端通过
  4. directional_shadow_scenebackpack_lit_scene 无回退
  5. Scene/Game View 继续通过统一 runtime renderer 显示一致光照结果
  6. 文档与测试矩阵已同步

12. 本阶段之后的正确下一步

多光源阶段收口后renderer 的下一阶段才应该进入:

  1. skybox / environment seam
  2. post-process 正式入口
  3. 更完整的 renderer frame composition

而不是立即跳去:

  • render graph
  • deferred
  • editor-only 视觉堆叠

13. 与旧计划的关系

2026-04-04 的《Renderer 下一阶段:方向光阴影与光照闭环计划》已经完成了其中大部分更高优先级内容:

  • 自动方向光阴影
  • Scene/Game runtime renderer 接入
  • object-id 正式化
  • 当前 forward runtime 主链稳定化

因此它已经不适合作为当前执行入口,现已归档。
本文件从 2026-04-05 起接管 rendering 主线的正式下一阶段。