Add 3DGS D3D12 MVS bootstrap and PLY loader

This commit is contained in:
2026-04-13 00:36:50 +08:00
parent 6f876678f5
commit 64212a53c7
8 changed files with 1534 additions and 0 deletions

View File

@@ -0,0 +1,245 @@
# 3DGS-D3D12 最小可行系统计划
日期2026-04-12
## 1. 文档定位
这份计划用于指导 `D:\Xuanchi\Main\XCEngine\mvs\3DGS-D3D12` 的最小可行系统落地。
当前任务边界已经明确:
1. 只允许在 `mvs/3DGS-D3D12` 目录内开发与新增文件。
2. `engine` 代码只能引用,禁止修改。
3. 图形抽象层只能使用现有 `engine` 提供的 `RHI` 接口。
4. 本轮目标是先把 3DGS 的最小可行系统跑通,而不是把它正式并入引擎主线。
5. 本轮明确不引入 `chunk` 机制。
因此,这份计划不是“继续修补引擎内已有的 GaussianSplat 路径”,而是“在 MVS 目录内重新搭一条干净的、可验证的、无 chunk 的 3DGS-D3D12 最小链路”。
## 2. 参考实现与职责拆分
本轮只参考两条现有实现,各自承担不同职责:
### 2.1 `mvs/3DGS Unity Renderer`
用途:
1. 参考 `.ply` 的读取方式。
2. 参考如何从 PLY 属性中提取 3DGS 所需原始数据。
3. 参考资源准备阶段的数据布局与字段含义。
注意:
1. 这里只借鉴导入与数据准备思路。
2. 不照搬 Unity Editor 资产工作流。
3. 不引入 chunk。
### 2.2 `mvs/3DGS-Unity`
用途:
1. 参考“干净的无 chunk 渲染路径”。
2. 参考 `prepare -> sort -> draw -> composite` 的最小闭环。
3. 参考 3DGS 在屏幕空间椭圆展开、混合与合成时的核心着色器语义。
注意:
1. 这里重点参考渲染过程,而不是 Unity 的宿主框架。
2. 不把 Unity 的 `ScriptableRenderPass`、资产导入器、Inspector 等编辑器逻辑带入本轮实现。
## 3. 首轮目标
首轮只完成以下闭环:
1.`mvs/3DGS-D3D12` 内部加载 `room.ply`
2. 将 PLY 中的高斯数据转换为无 chunk 的运行时缓冲。
3. 通过现有 `RHI` 完成 D3D12 路径下的最小渲染。
4. 输出一张稳定可观察的结果图,证明房间场景已经被正确绘制。
首轮验收标准:
1. 程序能独立编译与运行。
2. 不依赖修改 `engine` 才能成立。
3. 渲染结果不再是纯黑、纯白或明显错误的撕裂图。
4. 渲染链路中不存在任何 chunk 数据结构、chunk 缓冲或 chunk 可见性阶段。
## 4. 非目标
本轮明确不做:
1. 不并入 editor。
2. 不接入引擎现有 Renderer 主线。
3. 不做 OpenGL / Vulkan 多后端对齐。
4. 不做正式资源缓存格式。
5. 不做 chunk、cluster、LOD、streaming 等优化层。
6. 不做 compute 之外的额外架构扩展。
7. 不为迎合当前 MVS 去修改 `engine``RHI`、Renderer、Resources。
## 5. 约束与执行原则
### 5.1 目录约束
本轮新增内容应尽量收敛在 `mvs/3DGS-D3D12` 内,例如:
1. `src/`:程序入口、渲染器、相机、数据上传。
2. `include/`:本地头文件。
3. `shaders/`:本地 HLSL 或中间 shader 资源。
4. `assets/`:本地测试资源或生成物描述。
5. `third_party/`:仅当 MVS 自己确实需要额外小型依赖时使用。
### 5.2 RHI 使用原则
1. 只调用现有 `engine``RHI` 公共接口。
2. 如果发现最小系统缺失某项能力,优先在 MVS 内通过更简单的组织方式规避。
3. 若确实被 `RHI` 能力边界阻塞,先记录问题并汇报,不允许直接改 `engine`
### 5.3 数据路径原则
1. 整个系统只保留 positions、other、color、SH 等无 chunk 基础数据。
2. 不生成 chunk header。
3. 不上传 chunk buffer。
4. 不做 visible chunk 标记。
5. 不保留任何为了兼容旧 chunk 方案而加的临时分支。
## 6. 技术路线
### Phase 1梳理 3DGS-D3D12 的骨架与构建入口
目标:
1. 确认 `mvs/3DGS-D3D12` 当前是否只有 `room.ply`
2. 建立最小的可执行工程骨架。
3. 打通对 `engine``RHI` 的引用与链接。
任务:
1. 规划 `CMakeLists.txt` 与目录结构。
2. 建立窗口、设备、交换链、命令提交、离屏或在屏渲染的最小入口。
3. 跑通一个“清屏可显示”的基础版本。
验收:
1. `3DGS-D3D12` 可独立编译。
2. 程序能启动并输出基础画面。
### Phase 2实现无 chunk 的 PLY 读取与运行时数据打包
目标:
1. 参考 `mvs/3DGS Unity Renderer`,在 MVS 内部完成 PLY 读取。
2. 输出适合渲染阶段直接上传的高斯原始数组。
任务:
1. 解析 PLY header 与顶点属性。
2. 提取 position、rotation、scale、opacity、color/SH 等字段。
3. 明确字段的排列顺序、类型与归一化方式。
4. 生成 MVS 本地 `GaussianSplatSceneData`
验收:
1. `room.ply` 可被成功读取。
2. 点数量、字段长度、边界盒等基础统计合理。
3. 整条导入链中完全没有 chunk 概念。
### Phase 3对齐无 chunk 的 GPU 数据布局与上传
目标:
1. 把导入结果上传到 GPU。
2. 数据布局尽量贴近 `mvs/3DGS-Unity` 的渲染输入。
任务:
1. 设计 positions / other / color / SH 的 GPU buffer。
2. 建立与着色器绑定一致的 SRV/UAV 视图。
3. 为排序与绘制准备 index、distance、view-data 等工作缓冲。
验收:
1. GPU 侧所有关键缓冲都能正确创建。
2. 每个绑定槽位与 shader 语义一一对应。
3. 不包含 chunk buffer / visible chunk buffer。
### Phase 4先打通 prepare 与 sort
目标:
1. 先验证“高斯数据被相机看到并被正确排序”。
2. 在 draw 之前把中间结果可视化或可检查化。
任务:
1. 参考 `mvs/3DGS-Unity` 的 prepare 语义,计算每个 splat 的 view-space 信息。
2. 生成排序距离。
3. 跑通最小排序路径。
4. 必要时增加调试输出,用于检查 order / distance / 计数是否异常。
验收:
1. prepare 结果不是全零或明显错误。
2. sort 输出索引顺序稳定。
### Phase 5对齐 draw 与 composite
目标:
1. 参考 `mvs/3DGS-Unity` 的“干净无 chunk 渲染路径”把核心画面先画出来。
任务:
1. 对齐 Gaussian splat draw shader 的主要输入输出语义。
2. 对齐椭圆展开、覆盖范围与透明混合约定。
3. 建立 accumulation target。
4. 建立 composite pass把 accumulation 结果合回最终颜色。
验收:
1. 输出图中能看出 `room.ply` 对应的房间结构。
2. 不出现整屏纯黑、纯白、随机撕裂。
### Phase 6收口与验证
目标:
1. 固化最小系统结果,形成稳定基线。
任务:
1. 规范运行命令与资源路径。
2. 输出一张固定命名的结果图作为检查基线。
3. 清理调试残留与临时分支。
4. 补一份 MVS 内局部说明文档。
验收:
1. 代码、资源、着色器都收敛在 `mvs/3DGS-D3D12`
2. 运行方式清晰。
3. 基线截图稳定。
## 7. 关键风险
### 7.1 PLY 属性语义与当前样例不一致
如果 `room.ply` 的字段命名、顺序或编码方式与参考加载器假设不一致,最先要修的是导入器映射,不是渲染器。
### 7.2 RHI 能力与参考实现存在接口差
如果 Unity 参考依赖的某些资源绑定或 compute 流程不能直接一比一照搬,本轮优先在 `mvs/3DGS-D3D12` 内部重排执行方式,而不是去动 `engine`
### 7.3 排序与混合契约不一致
3DGS 最容易出错的不是“有没有画出来”而是排序方向、alpha 累积与 composite 契约是否一致。本轮必须把这三者当成同一个问题处理,禁止分开打补丁。
## 8. 本轮完成后的下一步
`mvs/3DGS-D3D12` 的无 chunk 最小系统跑通后,下一步才有意义讨论:
1. 是否把这条路径收编进引擎正式 Renderer。
2. 是否把 PLY 导入升级成正式资源导入器。
3. 是否在引擎层补 structured buffer / compute / renderer pass 抽象。
4. 是否接入 editor 与资产缓存。
在这之前,所有工作都应服务于一个目标:
先在 `mvs/3DGS-D3D12` 内证明“无 chunk 的 3DGS-D3D12 + 现有 RHI”这条路是通的。

View File

@@ -1,248 +0,0 @@
# 3DGS 渲染路径对齐参考实现修复计划
日期2026-04-11
## 1. 文档定位
旧计划《3DGS渲染集成测试与Renderer正式接入计划》已经完成了主体接入工作
1. `GaussianSplat` 资源链路已经打通
2. `GaussianSplatRendererComponent` / `VisibleGaussianSplatItem` / `BuiltinGaussianSplatPass` 已经落地
3. `tests/Rendering/integration/gaussian_splat_scene` 已经建立
当前剩下的不是“是否接进 Renderer”而是“当前 Renderer 中的 3DGS 正式路径与参考实现仍有关键偏差,导致画面结果错误”。
因此这份新计划只聚焦当前的渲染正确性修复与测试收口,不再重复旧计划里已经完成的接入事项。
## 2. 已确认的根因
这轮问题已经不是相机摆放、PLY 导入或简单参数调节问题,而是渲染路径本身与参考实现不一致。
### 2.1 当前引擎缺少正式的 accumulation + composite 两段式路径
参考实现的关键流程是:
1. 先把 splat 绘制到单独的半浮点累积 RT
2. splat draw 使用前向累积专用 blend
3. 最后再把累积 RT composite 回主场景颜色缓冲
参考位置:
1. `mvs/3DGS-Unity/Shaders/RenderGaussianSplats.shader`
2. `mvs/3DGS-Unity/Shaders/GaussianComposite.shader`
3. `mvs/3DGS-Unity/Runtime/GaussianSplatRenderer.cs`
当前引擎却是:
1.`engine/assets/builtin/shaders/gaussian-splat.shader` 中直接输出到主场景颜色
2. `BuiltinGaussianSplatPass` 直接把 camera color attachment 作为 render target
3. 虽然 `BuiltinGaussianSplatPassResources` 已经有 `AccumulationSurface` 抽象,但执行路径没有真正使用它
这说明当前实现是“资源抽象已经开始正式化,但执行流仍停在临时路径上”,这是第一根因。
### 2.2 当前排序方向与当前 blend 方程不匹配
当前引擎:
1. `PrepareOrder` 阶段使用相机空间 `viewCenter.z` 作为排序距离
2. bitonic 结果本质上是按升序排列
3. draw shader 却使用普通透明混合 `Blend SrcAlpha OneMinusSrcAlpha`
如果保留当前普通透明混合,就应当使用严格的 back-to-front 语义;
如果要对齐参考实现的 front-to-back 累积,就必须同时切到参考的累积 blend 与独立 accumulation target。
也就是说,当前并不是“排序可能有一点不准”,而是“排序约定与 blend 合约本身冲突”。
### 2.3 当前测试参数只是在放大问题,不是问题本体
当前集成测试里:
1. 只取了 `65536` 个 splat 子集
2. `_PointScale = 3.0`
3. 最终输出直接落在主 backbuffer
这些会让错误更明显,但不会单独制造当前这种大面积拖影、糊片、黑色尖刺。
真正的问题仍然是 draw/composite 路径设计错误。
## 3. 本轮修复目标
本轮只做一件事:把当前引擎中的 3DGS 渲染主链,彻底对齐到参考实现所依赖的那组最小正确语义。
具体目标:
1. 建立正式的 accumulation render target
2. draw shader 改为服务 accumulation 的输出与 blend 合约
3. 新增正式的 composite pass把 accumulation 结果混回主场景
4. 统一排序方向与 blend 语义,不允许继续“排序一套、混合一套”
5. 保证 `gaussian_splat_scene` 在 D3D12 / Vulkan / OpenGL 三后端下都能稳定回归
## 4. 非目标
这轮明确不做:
1. 不接 editor 中的 3DGS 显示与交互
2. 不做 selection / cutout / 编辑工具链
3. 不做 3DGS 的 streaming / LOD / chunk 级高级优化
4. 不在这轮重做 `GaussianSplat` artifact schema
5. 不引入 compute shader 之外的新渲染架构分支
## 5. 执行原则
### 5.1 先对齐正确性,再谈进一步优化
这轮优先级必须是:
1. 先把“渲染方程”和“执行路径”对齐
2. 先让集成测试输出正确
3. 再考虑性能、压缩、子集规模、排序频率等问题
### 5.2 不允许继续保留半套正式化、半套临时方案
一旦正式启用 accumulation/composite就必须
1. 把 draw shader、pass 资源、pass 执行流一起接完整
2. 把当前直接画到 scene color 的临时路径清掉
3. 不保留两个语义不同但名字相同的渲染路线
### 5.3 修复必须由测试驱动收口
本轮所有核心改动都必须由以下验证闭环约束:
1. `gaussian_splat_scene` 的三后端输出
2. 中间调试图可视化检查
3. 现有 `GaussianSplat` 资源与缓存相关测试不回退
## 6. 分阶段计划
### Phase 1补齐 3DGS 中间结果观测面
目标:
1. 在不改渲染语义之前,先把中间状态可视化出来
任务:
1. 允许 `gaussian_splat_scene` 在调试模式下输出 accumulation RT
2. 允许单独检查 sort/order 与 view-data 是否为空或异常
3. 固化一套最小调试截图流程,避免后续继续靠猜
验收标准:
1. 可以直接看到 accumulation RT 的内容
2. 可以区分“prepare 阶段错误”和“draw/composite 阶段错误”
### Phase 2把 accumulation surface 正式接入 `BuiltinGaussianSplatPass`
目标:
1.`BuiltinGaussianSplatPassResources::AccumulationSurface` 从未使用状态变成正式执行资源
任务:
1. 在 pass 执行前按 viewport 尺寸创建或复用 accumulation RT
2.`alpha = 0` 清空 accumulation RT
3. draw 阶段只向 accumulation RT 输出,不再直接写主场景颜色
4. 明确 accumulation RT 的资源状态流转
验收标准:
1. draw pass 不再直接绑定主场景颜色附件
2. accumulation RT 生命周期与 working set 生命周期边界清晰
### Phase 3对齐 draw shader 的输出语义与 blend 合约
目标:
1. 让 splat draw 输出和参考实现使用同一套前向累积语义
任务:
1. 对齐 draw shader 输出格式
2. 对齐 draw shader blend 状态
3. 确认当前相机空间 `z` 约定下,排序方向与累积方向一致
4. 不允许通过后端分支打补丁解决语义冲突
验收标准:
1. draw shader 的颜色输出、alpha 输出、blend 方程是自洽的
2. `PrepareOrder -> Sort -> Draw` 是同一套排序约定
### Phase 4新增正式的 composite pass
目标:
1. 把 accumulation RT 稳定地合成回主场景颜色缓冲
任务:
1. 新增 builtin composite shader
2.`BuiltinGaussianSplatPass` 内建立 draw + compose 的完整顺序
3. 明确主场景颜色、深度与 accumulation RT 的交互边界
4. 不通过测试侧手写 command list 绕过 renderer
验收标准:
1. 最终颜色由 composite pass 统一输出
2. 3DGS pass 对主场景颜色缓冲的写入语义单一清晰
### Phase 5回归基线与参数收口
目标:
1. 在正确渲染路径跑通后,再校正当前测试参数
任务:
1. 重新评估当前子集数量是否足够形成稳定 GT
2. 重新评估 `_PointScale` 的默认值
3. 必要时重建 `GT.ppm`
4. 三后端重新运行并固定基线
验收标准:
1. `gaussian_splat_scene` 的 GT 是基于正式渲染路径得到的
2. 不再把“错误输出截图”继续当成 GT
## 7. 风险与提前约束
### 7.1 不能把参考实现照搬成 Unity 特有依赖
本轮只吸收以下内容:
1. 累积 RT + composite 的渲染语义
2. 排序与 blend 的契约关系
3. 中间资源与 draw/composite 的分层方式
不吸收:
1. Unity 特有 `CommandBuffer` 接口设计
2. Unity 运行时生命周期组织方式
3. Unity 专有材质属性命名与 editor 逻辑
### 7.2 不能把 accumulation/composite 做成测试专用旁路
这条路径必须属于正式 renderer而不是
1. 只在 `gaussian_splat_scene` 里额外手写一套
2. 只在 D3D12 下工作
3. 通过测试宏临时切换
### 7.3 不允许继续保留错误 GT
如果当前 `GT.ppm` 来自错误渲染路径,就必须重建。
否则后续再怎么修,测试也会被旧基线误导。
## 8. 完成标志
当以下条件同时成立,这份计划才算完成:
1. `BuiltinGaussianSplatPass` 正式使用 accumulation surface
2. draw shader 与 sort 语义彻底自洽
3. composite pass 正式接回主场景颜色
4. `gaussian_splat_scene` 在 D3D12 / Vulkan / OpenGL 下都能稳定输出正确图像
5. 调试图证明 accumulation RT 本身干净且可解释
6. 当前 3DGS 渲染路径中不再残留“直接写 scene color 的临时旧路线”
## 9. 一句话结论
当前 3DGS 的问题不是“参数没调对”,而是“渲染执行链条还停在错误的临时语义上”。
下一步必须把 `Prepare/Sort -> Accumulation Draw -> Composite` 这条正式路径一次接完整,再重新建立三后端的 GT 基线。