Files
XCEngine/docs/used/3DGS渲染集成测试与Renderer正式接入计划_阶段归档_2026-04-11.md

12 KiB
Raw Blame History

3DGS 渲染集成测试与 Renderer 正式接入计划

日期2026-04-10

1. 文档定位

这份计划承接已经完成的 GaussianSplat 资源 / Artifact / ResourceManager / GPU cache 正式化工作,正式进入下一阶段:

  1. tests/Rendering/integration 中把 3DGS 正式渲染出来
  2. 为引擎 renderer 增加一条正式的 3DGS 场景渲染路径

这份计划的首要目标不是 editor 接入,也不是 3DGS 编辑工具,而是:

  1. room.ply -> GaussianSplat -> Scene -> Renderer -> GT 比对 成为一条正式、可回归、可维护的链路

这份计划明确不做的事:

  1. 不做 editor 里的 3DGS Inspector / gizmo / 交互编辑
  2. 不做 cutout、selection、导出、编辑态 GPU buffer
  3. 不在这一轮提前做 streaming / LOD / chunk 压缩升级
  4. 不把 3DGS 临时塞进 MeshRendererVolumeRenderer 路径里凑合出图

2. 当前基线与已具备能力

当前已完成的正式基础:

  1. .ply -> GaussianSplatImporter -> main.xcgsplat -> GaussianSplatLoader -> ResourceManager 已打通
  2. RenderResourceCache 已具备 GaussianSplat 静态 GPU 驻留能力
  3. room.ply 已可稳定导入、复用 artifact、并通过测试验证

当前 renderer / RHI 已具备的关键能力:

  1. StructuredBuffer / RawBuffer / RWStructuredBuffer / RWRawBuffer shader 资源描述能力已具备
  2. compute shader 与 Dispatch 已具备
  3. Draw(vertexCount, instanceCount, ...) 已具备,可以走 instanced quad 而不需要先补新的 RHI 接口
  4. renderer 已有成熟的 integration test 模式:
    1. 一个场景一个 target
    2. 真正创建 Scene + SceneRenderer + RenderSurface
    3. 输出 PPM 并和 GT.ppm 比对
  5. 当前已有可借鉴的参考:
    1. mvs/3DGS-Unity/Runtime/GaussianSplatRenderer.cs
    2. tests/Rendering/integration/volume_scene
    3. tests/Rendering/integration/backpack_lit_scene

3. 当前还缺什么

虽然资源链已经完整,但 renderer 侧现在仍然没有正式的 3DGS 路径:

  1. 没有 GaussianSplatRendererComponent
  2. 没有 VisibleGaussianSplatItem
  3. RenderSceneData 没有 visibleGaussianSplats
  4. RenderSceneExtractor / RenderSceneUtility 不会提取 3DGS 场景对象
  5. BuiltinForwardPipeline 没有 3DGS 专用 pass
  6. 还没有用于 3DGS 的 transient GPU working buffers
    1. sort distance / key
    2. view-data
    3. offscreen accumulation / compose target
  7. tests/Rendering/integration 还没有 3DGS 场景测试

所以这一步不是“写个测试 main.cpp 就行”,而是要正式把 3DGS 纳入 renderer 架构。


4. 本轮核心架构决策

4.1 必须引入专用场景组件 GaussianSplatRendererComponent

不允许:

  1. MeshRendererComponent 直接持有 GaussianSplat
  2. 复用 VolumeRendererComponent
  3. 在测试里绕过组件层,直接把 GaussianSplat 塞进 pass

正式方案应为:

  1. 新增 GaussianSplatRendererComponent
  2. 它持有:
    1. ResourceHandle<GaussianSplat>
    2. Material* 或正式 Material 句柄
    3. 运行时参数例如缩放、透明度倍率、SH 阶数、排序策略等

原因:

  1. 这条路径是专用渲染 primitive不是 mesh也不是 volume
  2. 场景提取、渲染排序、后续 editor 支持,都需要一个正式组件边界
  3. 这和参考项目中单组件 GaussianSplatRenderer 的职责边界是一致的

4.2 3DGS 必须走专用 VisibleGaussianSplatItem

应新增:

  1. Rendering/FrameData/VisibleGaussianSplatItem.h
  2. RenderSceneData.visibleGaussianSplats
  3. AppendVisibleGaussianSplatsForGameObject(...)

原因:

  1. 3DGS 与 mesh、volume 的排序依据和 GPU 资源都不同
  2. 把它塞到 visibleItemsvisibleVolumes 里只会制造更多特判

4.3 3DGS 必须作为 BuiltinForwardPipeline 中的一条正式 pass

不允许:

  1. 直接把 3DGS 逻辑硬塞进 DrawVisibleItems
  2. 把 3DGS 伪装成普通 forward lit / unlit surface
  3. 在测试代码里直接手写 command list 绕过 renderer

正式方案应为:

  1. 新增 BuiltinGaussianSplatPass
  2. 它在 BuiltinForwardPipeline 中作为一条显式阶段执行
  3. 先按当前 pipeline 约束放在:
    1. Opaque
    2. Skybox
    3. GaussianSplat
    4. Volumetric
    5. Transparent

这一步的理由是:

  1. 参考 Unity MVS 的落点本质上属于 BeforeForwardAlpha
  2. 3DGS 需要依赖 opaque depth但又不应混入普通 mesh transparent 队列
  3. 当前 pipeline 还没有更通用的 feature graph因此先做一条正式内建 pass 是合理路径

4.4 静态 GPU 资产与每帧 working set 必须分层

已经完成的静态层:

  1. RenderResourceCache::CachedGaussianSplat
    1. positions
    2. other
    3. color
    4. sh
    5. chunks

下一步必须新增的是每帧 / 每相机 working set而不是污染静态 cache

  1. sort distance buffer
  2. sort key / order buffer
  3. view-data buffer
  4. splat accumulation render target

结论:

  1. RenderResourceCache 继续负责静态 asset residency
  2. BuiltinGaussianSplatPass 或其专属 surface cache 负责 transient working resources

4.5 第一张图必须先在 rendering integration test 里落地

这一轮的第一个正式验收点不是 editor 里看到 3DGS而是

  1. 新增 tests/Rendering/integration/gaussian_splat_scene
  2. 使用真实 room.ply
  3. 跑通过至少 D3D12 / OpenGL / Vulkan 的 GT 比对

原因:

  1. 这能把资源链问题、scene 提取问题、shader 问题、pass 问题一次性锁进正式回归
  2. editor 接入如果先做,调试成本会更高,问题边界也更模糊

5. 渲染技术路线

5.1 第一轮渲染方案

当前最合适的正式方案是:

  1. 使用 instanced quad 绘制 splat
  2. 顶点着色器通过 SV_InstanceID / gl_InstanceIDStructuredBuffer 读取 per-splat 数据
  3. compute 负责:
    1. 计算相机空间深度或排序 key
    2. 生成 draw 顺序
    3. 预计算 view-data
  4. graphics pass 负责:
    1. 深度测试
    2. splat 展开
    3. 累积到专用 render target
  5. fullscreen compose pass 负责把结果混合回主场景颜色

这条路线的优点:

  1. 与参考 GaussianSplatRenderer.cs 的总体思路一致
  2. 与现有 RHI 能力匹配,不需要新发明 RHI 接口
  3. 可以自然落到当前 built-in pipeline 中

5.2 关于排序

第一轮正式实现就应该有排序,不建议无排序硬上 GT。

原因:

  1. 3DGS 本质上依赖近似正确的后向前顺序
  2. 如果先做无排序integration test 的 GT 价值会很差
  3. 后面再补排序,很可能把第一版 shader / pass 全打碎

因此建议:

  1. 第一张 integration 图就包含 compute 排序链
  2. 如果排序系统需要简化,也只能简化为“全局每帧排序”,不能退化到“不排序”

5.3 关于 SH 与颜色

当前 cooked-v1 中已有:

  1. color = SH0ToColor(dc0) + opacity
  2. sh = f_rest_0..44

正式阶段建议:

  1. 第一版 pass 即支持当前 artifact 语义
  2. 不为了追求和 Unity 一模一样的颜色纹理缓存形态,先回头推翻当前 cooked-v1
  3. 如果实际实现中发现 color buffer 明显不适合后续长期演进,再做正式 schema 升级,而不是临时特判

也就是说:

  1. 当前目标是先让 room.ply 通过正式 renderer 路径稳定出图
  2. 不是在出第一张图之前就重做第二版 artifact 格式

6. Renderer 需要做的正式改动

Phase 1场景对象与提取层正式接入

目标:

  1. 让 Scene 能正式携带 3DGS 渲染对象

任务:

  1. 新增 GaussianSplatRendererComponent
  2. 新增 VisibleGaussianSplatItem
  3. 扩展 RenderSceneData
  4. 扩展 RenderSceneUtility / RenderSceneExtractor
  5. 补对应 unit tests覆盖
    1. 提取
    2. culling mask
    3. render queue
    4. 排序稳定性

验收标准:

  1. RenderSceneExtractor 能输出 visibleGaussianSplats
  2. 不污染 mesh / volume 路径

Phase 23DGS pass 的 GPU working set 正式化

目标:

  1. 让 3DGS 渲染时所需的每帧资源有正式边界

任务:

  1. BuiltinGaussianSplatPass 设计 transient resource 集合
  2. 明确静态 cache 与 transient working set 的所有权
  3. 实现:
    1. sort distances / keys
    2. order buffer
    3. view-data buffer
    4. accumulation target
  4. 补 cache / pass smoke tests

验收标准:

  1. pass 在没有 editor 的情况下独立准备好运行所需 GPU 资源
  2. 不把每帧资源塞回 RenderResourceCache

Phase 3BuiltinGaussianSplatPass 正式落地

目标:

  1. 在 renderer 中画出第一张 3DGS 正式图

任务:

  1. 新增内建 shader / material 资产:
    1. sort compute
    2. view-data compute
    3. splat draw
    4. compose
  2. 新增 BuiltinGaussianSplatPass
  3. 接入 BuiltinForwardPipeline
  4. 定义与主深度、主颜色的交互规则

验收标准:

  1. pass 不绕过 SceneRenderer
  2. pass 不绕过 RenderSceneData
  3. BuiltinForwardPipeline 的阶段顺序清晰且可测试

Phase 4rendering integration test 正式落地

目标:

  1. 把 3DGS 第一张图纳入 GT 回归

任务:

  1. 新增 tests/Rendering/integration/gaussian_splat_scene
  2. 使用真实样本:
    1. room.ply
  3. 复制运行所需资源到测试输出目录
  4. 建立 GT 基线图
  5. 至少覆盖:
    1. D3D12
    2. OpenGL
    3. Vulkan

建议场景内容:

  1. 相机
  2. 黑或深灰背景
  3. 单个 GaussianSplatRendererComponent
  4. 不引入外部灯光依赖作为第一张图前提

原因:

  1. 3DGS 的基础显示不应依赖当前主光 / 多光源系统是否正确
  2. 第一张图应尽量减少无关变量

Phase 5测试收口与回归闭环

任务:

  1. 跑通:
    1. gaussian_splat_tests
    2. rendering_unit_tests
    3. rendering_integration_gaussian_splat_scene
    4. 相关既有 rendering integration tests
  2. 修复跨后端 shader / 资源绑定差异
  3. 输出阶段总结

验收标准:

  1. 3DGS 进入正式 rendering regression 集
  2. 不存在“只有 editor 里能看,测试里没有”的非正式路径

7. 需要提前注意的风险

7.1 当前 command list 没有 DrawProcedural

这不是 blocker。

当前可行正式方案是:

  1. 使用固定 6 顶点 quad
  2. Draw(6, splatCount, 0, 0)
  3. 顶点阶段通过 instance id 读 buffer

也就是说,不需要先为了 3DGS 强行扩展新的 RHI draw API。

7.2 3DGS 与 volume 都属于“专用透明路径”

这意味着当前 built-in pipeline 中将出现:

  1. VolumetricPass
  2. GaussianSplatPass

两者在这一轮可以并存,但必须保证:

  1. 顺序明确
  2. 资源边界明确
  3. 不相互复用错误的数据结构

如果过程中发现当前 BuiltinForwardPipeline 已经开始过载,就应该正式抽出更清晰的内建 feature 组织层,而不是继续堆 if 分支。

7.3 integration test 先用真实 room.ply

这意味着:

  1. 样本量不小
  2. shader / sort / view-data 任何一步出错,画面都会直接坏掉

但这恰恰是好事,因为:

  1. 第一张图就能真实暴露架构问题
  2. 不会被“用一个简化假样本先糊过去”的临时路线误导

8. 本轮完成标志

当以下条件同时成立,这份计划才算完成:

  1. GaussianSplatRendererComponent 已存在且被 SceneRenderer 正式消费
  2. RenderSceneData.visibleGaussianSplats 已成为正式帧数据
  3. BuiltinGaussianSplatPass 已正式接入 BuiltinForwardPipeline
  4. room.ply 能在 tests/Rendering/integration/gaussian_splat_scene 中稳定渲染
  5. 至少 D3D12 / OpenGL / Vulkan 的 GT 回归通过
  6. 现有 gaussian_splat_tests 与相关 rendering tests 不被破坏
  7. 不存在任何 render pass 直接读取 source .ply 的旁路

9. 一句话结论

3DGS 的下一步不是直接去 editor 里“先看见再说”,而是先在 renderer 正式增加一条 GaussianSplatRendererComponent -> VisibleGaussianSplatItem -> BuiltinGaussianSplatPass -> rendering integration GT 的闭环路径;
只有这条闭环成立,后面的 editor 显示、选择、材质面板和 SRP 演进才有可靠基础。