Plan post-process and final-output closure

This commit is contained in:
2026-04-06 03:35:33 +08:00
parent 3547597bd2
commit 5e489b61d2

View File

@@ -0,0 +1,296 @@
# 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 contract
- `CameraRenderer` 仍然以 `request.surface` 作为主场景直接输出目标
- 没有 renderer 级别的 fullscreen pass 基础设施
- 没有“只有需要时才分配 intermediate color target”的正式策略
- 也没有一个专门验证 runtime 后处理闭环的 `post_process_scene`
如果这个阶段不先收口,后续无论做 tone mapping、color grading、exposure、更多环境特性都会继续以临时拼接的方式往上堆架构会重新变脏。
## 3. 目标
本阶段只做一件事:
**把 runtime 相机输出链路正式化。**
完成后应达到:
1. renderer 具备正式的 post-process 请求与执行入口。
2. renderer 具备 backend-neutral 的 fullscreen pass 基础设施。
3. 相机默认仍可直接渲染到最终目标,只有在确实需要后处理或最终合成时才切换 intermediate color target。
4. final output 的 copy / blit / resolve 规则明确,不再隐式散落在具体 pass 中。
5. object id 与 editor overlay 明确保持在 runtime final-color 主链之外。
6. 增加 `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` 已有:
- `PreScenePasses`
- `ShadowCaster`
- `DepthOnly`
- `MainScene`
- `ObjectId`
- `PostScenePasses`
- `OverlayPasses`
下一步要补的是“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` 的方式,需要升级成可判断的策略:
1. 如果本帧不需要 post-process / final copy则 direct-to-surface。
2. 如果本帧需要 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_scene`
- `rendering_integration_offscreen_scene`
- `rendering_integration_transparent_material_scene`
- `rendering_integration_camera_stack_scene`
- `rendering_integration_object_id_scene`
必要时再补:
- `material_state_scene`
- `depth_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_scene` GT 通过
- `skybox_scene` / `offscreen_scene` / `transparent_material_scene` 不回退
### Phase D文档与测试收口
目标:
- 把当前阶段的 contract、测试矩阵、限制条件写回文档
完成标准:
- `tests/TEST_SPEC.md` 如有必要同步更新
- 当前计划可归档到 `docs/used`
## 9. 收口判定
满足以下条件时,本阶段可以视为完成:
1. runtime renderer 已具备正式 post-process 入口。
2. final output 路径不再依赖隐式直写逻辑。
3. intermediate color target 为按需启用,而不是一刀切常驻。
4. `post_process_scene` 在 D3D12 / OpenGL / Vulkan 三后端通过。
5. `skybox_scene``offscreen_scene``camera_stack_scene``transparent_material_scene` 不回退。
6. object id 与 editor overlay 没有被错误卷入 runtime final-color 链。
## 10. 这一阶段之后再做什么
等这一阶段收口之后renderer 主线再继续往下走,顺序才是合理的:
1. 更正式的 shader / material runtime pass contract
2. 更成熟的 renderer feature / Unity 风格可扩展点
3. GPU object id / picking 正式化
4. 更复杂的 environment、tone mapping 与后处理能力
也就是说,**现在下一步不是做更多“效果”,而是先把相机输出主链做完整。**