8.5 KiB
Renderer 当前主线下一阶段:PostProcess 与 FinalOutput 收口计划
日期:2026-04-06
1. 阶段结论
当前 Rendering 主线的下一个阶段,不应该跳去做:
render graph- deferred / clustered
- GPU picking 替换
- 更复杂的 editor-only 特效
现在真正还没收口的,是 runtime 相机帧合成链路。
更具体地说,就是把当前 renderer 从:
Scene -> 直接打到最终 surface
收口成:
Scene Color -> 可选 PostProcess -> Final Output
只有这一步补齐之后,现阶段的 renderer 才算真正具备了稳定的 runtime frame composition 能力,也才适合继续往 Unity 风格的 renderer feature / C# SRP 承接点推进。
2. 为什么现在必须先做这个
当前仓库已经具备:
- 正式的 forward runtime scene renderer
- directional / point / spot 多光源
- main directional shadow
- skybox
- object id
- editor overlay / gizmo / grid
但真正缺的仍然是下面这条 runtime 正式主链:
Opaque -> Skybox -> Transparent -> PostProcess -> Final Output
现在的主要缺口是:
CameraRenderRequest还没有正式的 post-process / final-output contractCameraRenderer仍然以request.surface作为主场景直接输出目标- 没有 renderer 级别的 fullscreen pass 基础设施
- 没有“只有需要时才分配 intermediate color target”的正式策略
- 也没有一个专门验证 runtime 后处理闭环的
post_process_scene
如果这个阶段不先收口,后续无论做 tone mapping、color grading、exposure、更多环境特性,都会继续以临时拼接的方式往上堆,架构会重新变脏。
3. 目标
本阶段只做一件事:
把 runtime 相机输出链路正式化。
完成后应达到:
- renderer 具备正式的 post-process 请求与执行入口。
- renderer 具备 backend-neutral 的 fullscreen pass 基础设施。
- 相机默认仍可直接渲染到最终目标,只有在确实需要后处理或最终合成时才切换 intermediate color target。
- final output 的 copy / blit / resolve 规则明确,不再隐式散落在具体 pass 中。
- object id 与 editor overlay 明确保持在 runtime final-color 主链之外。
- 增加
post_process_scene集成测试,验证三后端一致性。
4. 非目标
本阶段明确不做:
render graph- HDR 全量管线
- bloom / SSAO / DOF / motion blur 等完整后处理套件
- cubemap IBL / reflection probe
- GPU picking 替换 CPU picking
- editor SceneView 的额外视觉增强
- 新一轮 renderer 大重构
5. 设计原则
5.1 继续遵循当前分层
RHI只提供资源、render target、pipeline、draw/dispatch 抽象。Renderer负责相机帧规划、runtime pass 调度、final output 规则。Editor仍然只是宿主与 overlay 使用方,不接管 runtime 后处理。
5.2 runtime 主链与 editor 辅助链继续隔离
下列内容不进入 runtime final-color 主链:
ObjectId- grid
- gizmo
- selection outline
- scene icon
它们仍然属于 editor 专用辅助链路。
5.3 默认直出,按需离屏
默认情况:
- 没有 post-process
- 没有额外 final composition 需求
则继续允许相机直接渲染到最终 surface。
只有满足下面条件之一时,才启用 intermediate color target:
- 存在 runtime post-process
- final output 需要一次 fullscreen copy / resolve / color transform
- 后续扩展的 runtime frame composition 明确要求 scene color 先落到中间目标
这个判断必须由 CameraRenderer 或 request planning 统一控制,不能散落到单个 pass 内部。
6. 核心方案
6.1 补齐 CameraRenderRequest 的正式 contract
当前 CameraRenderRequest 已有:
PreScenePassesShadowCasterDepthOnlyMainSceneObjectIdPostScenePassesOverlayPasses
下一步要补的是“runtime final-color 链”的明确语义,而不是继续把 PostScenePasses 当一个模糊兜底桶。
建议演进方向:
- 保留现有 stage 枚举兼容当前结构
- 在 request 数据层新增更明确的 runtime 输出描述
- 显式区分:
- scene color source
- post-process chain
- final output target
换句话说,本阶段的重点不是改名字,而是把数据契约补完整。
6.2 增加 Fullscreen Pass 基础设施
需要新增 backend-neutral 的 fullscreen pass helper,用于:
- 绑定 source color texture
- 绑定 sampler / descriptor set
- 设置 fullscreen triangle 或等价路径
- 输出到指定 color target
首版要求:
- 三后端可复用同一套 renderer 侧调用方式
- shader 输入输出约束简单、稳定、可测试
- 不依赖 editor 私有绘制路径
6.3 Final Output 明确化
当前 main scene 直接打到 request.surface 的方式,需要升级成可判断的策略:
- 如果本帧不需要 post-process / final copy,则 direct-to-surface。
- 如果本帧需要 post-process,则:
- main scene 输出到 intermediate color target
- post-process 读取 intermediate
- final pass 输出到
request.surface
这里要把以下规则写死:
- intermediate 的格式、尺寸与生命周期
- final blit / copy / fullscreen draw 由谁负责
- MSAA / resolve 如果暂时没有正式支持,要在 contract 里先写清当前限制
6.4 第一阶段验证效果只做最小闭环
本阶段不追求复杂后处理效果。
第一阶段 builtin effect 建议只做一种确定性极强、三后端最容易对齐的效果:
ColorScale
例如对 scene color 统一乘一个常量因子。
这样做的原因:
- 易于写 GT
- 不依赖复杂数学或 LUT
- 不容易受不同后端精度差异影响
- 能直接证明 post-process infrastructure 已闭环
7. 测试策略
7.1 新增集成测试
新增:
tests/Rendering/integration/post_process_scene
建议测试场景:
- 结构尽量简单
- 使用确定性颜色块或单个模型
- 主场景颜色在经过
ColorScale后能明显与未处理版本区分 - 三后端都输出
*_d3d12.ppm/*_opengl.ppm/*_vulkan.ppm - 统一对比单张
GT.ppm
7.2 必跑回归
本阶段至少回归:
rendering_integration_skybox_scenerendering_integration_offscreen_scenerendering_integration_transparent_material_scenerendering_integration_camera_stack_scenerendering_integration_object_id_scene
必要时再补:
material_state_scenedepth_sort_scene
7.3 单测方向
建议补或增强的 unit coverage:
- 何时启用 intermediate color target
- post-process chain 为空时的 direct path
- final output routing 决策
- request contract 的默认行为与 fallback
8. 分阶段执行
Phase A:补齐 contract
目标:
- 明确 runtime post-process / final-output 所需的数据结构与决策入口
完成标准:
CameraRenderRequest/ planner /CameraRenderer层的数据语义清晰- 现有场景渲染不回退
Phase B:引入 fullscreen pass 与 intermediate 策略
目标:
- 跑通 scene color -> fullscreen pass -> final surface
完成标准:
- 三后端都能稳定执行最小 fullscreen pass
- direct path 仍然保留
Phase C:接入最小 builtin post-process
目标:
- 用
ColorScale验证 runtime 后处理闭环
完成标准:
post_process_sceneGT 通过skybox_scene/offscreen_scene/transparent_material_scene不回退
Phase D:文档与测试收口
目标:
- 把当前阶段的 contract、测试矩阵、限制条件写回文档
完成标准:
tests/TEST_SPEC.md如有必要同步更新- 当前计划可归档到
docs/used
9. 收口判定
满足以下条件时,本阶段可以视为完成:
- runtime renderer 已具备正式 post-process 入口。
- final output 路径不再依赖隐式直写逻辑。
- intermediate color target 为按需启用,而不是一刀切常驻。
post_process_scene在 D3D12 / OpenGL / Vulkan 三后端通过。skybox_scene、offscreen_scene、camera_stack_scene、transparent_material_scene不回退。- object id 与 editor overlay 没有被错误卷入 runtime final-color 链。
10. 这一阶段之后再做什么
等这一阶段收口之后,renderer 主线再继续往下走,顺序才是合理的:
- 更正式的 shader / material runtime pass contract
- 更成熟的 renderer feature / Unity 风格可扩展点
- GPU object id / picking 正式化
- 更复杂的 environment、tone mapping 与后处理能力
也就是说,现在下一步不是做更多“效果”,而是先把相机输出主链做完整。