chore: checkpoint current workspace changes
This commit is contained in:
@@ -1,693 +0,0 @@
|
||||
# 3DGS 专用 PLY 导入器与 GaussianSplat 资源缓存正式化计划
|
||||
|
||||
日期:2026-04-10
|
||||
|
||||
## 1. 文档定位
|
||||
|
||||
这份计划只覆盖 3DGS 落地中的前两层基础设施:
|
||||
|
||||
1. `3DGS 专用 PLY importer`
|
||||
2. `GaussianSplat 资源 / Artifact / ResourceManager / 缓存层`
|
||||
|
||||
这份计划明确不讨论以下内容:
|
||||
|
||||
1. 不实现最终的 3DGS 渲染 pass
|
||||
2. 不实现 editor 里的 3DGS 编辑工具
|
||||
3. 不实现 cutout、selection、导出、相机激活等 Unity 参考项目中的高级功能
|
||||
4. 不提前把 3DGS 强行塞进现有 mesh / volume 路径
|
||||
|
||||
这份计划的目标不是“先把 `.ply` 读出来”,而是把 3DGS 资产链正式纳入引擎现有的资源系统,使它从一开始就是一条可缓存、可复用、可异步、可测试、可长期维护的正式路径。
|
||||
|
||||
---
|
||||
|
||||
## 2. 当前参考与现状
|
||||
|
||||
当前参考工程是:
|
||||
|
||||
1. [mvs/3DGS-Unity](D:/Xuanchi/Main/XCEngine/mvs/3DGS-Unity)
|
||||
|
||||
当前测试样本是:
|
||||
|
||||
1. [room.ply](D:/Xuanchi/Main/XCEngine/mvs/3DGS-Unity/room.ply)
|
||||
|
||||
当前已确认的事实:
|
||||
|
||||
1. `3DGS-Unity` 并不是运行时直接渲染 `.ply`,而是先把 `.ply` 转成更接近 GPU 消费形态的运行时资产。
|
||||
2. 它的导入工作流核心在 [GaussianSplatAssetCreator.cs](D:/Xuanchi/Main/XCEngine/mvs/3DGS-Unity/Editor/GaussianSplatAssetCreator.cs)。
|
||||
3. 它的 `.ply` 读取器 [PLYFileReader.cs](D:/Xuanchi/Main/XCEngine/mvs/3DGS-Unity/Editor/Utils/PLYFileReader.cs) 本质上是一个偏工程化的快速路径,不是健壮的通用 PLY 解析器。
|
||||
4. 它的运行时资产 [GaussianSplatAsset.cs](D:/Xuanchi/Main/XCEngine/mvs/3DGS-Unity/Runtime/GaussianSplatAsset.cs) 已经把数据拆成了 `pos / other / sh / color / chunk` 几类 GPU 资源。
|
||||
5. 它的运行时渲染 [GaussianSplatRenderer.cs](D:/Xuanchi/Main/XCEngine/mvs/3DGS-Unity/Runtime/GaussianSplatRenderer.cs) 依赖 compute、StructuredBuffer、RawBuffer、procedural draw、fullscreen composite。
|
||||
|
||||
对当前引擎的现状判断:
|
||||
|
||||
1. 引擎已经具备 `StructuredBuffer / RawBuffer / RWStructuredBuffer / RWRawBuffer` 级别的 shader 资源识别能力。
|
||||
2. 引擎已经具备 compute shader 与 `Dispatch` 的 RHI 基础能力。
|
||||
3. 引擎已经具备 runtime material buffer binding 能力。
|
||||
4. 引擎已经具备 `AssetDatabase -> Library/Artifacts -> ResourceManager` 的正式资源链。
|
||||
5. 引擎当前还没有 `GaussianSplat` 这种正式资源类型。
|
||||
6. 引擎当前还没有 3DGS 专用 importer、artifact schema、loader、GPU residency cache。
|
||||
|
||||
---
|
||||
|
||||
## 3. 本轮最核心的架构决策
|
||||
|
||||
### 3.1 不允许运行时直接消费 `.ply`
|
||||
|
||||
正式方案必须是:
|
||||
|
||||
1. `Assets/*.ply`
|
||||
2. 经过 `GaussianSplatImporter`
|
||||
3. 生成 `Library/Artifacts/.../main.xcgsplat`
|
||||
4. 运行时只加载 `xcgsplat`
|
||||
|
||||
不允许的错误方案:
|
||||
|
||||
1. renderer 首次遇到 `.ply` 时再现场解析
|
||||
2. component 里直接持有 `.ply` 文件句柄
|
||||
3. 把 `.ply` 读取逻辑塞进 render pass
|
||||
4. 为了尽快出图先做“临时直接加载 `.ply`”然后以后再回收
|
||||
|
||||
原因很明确:
|
||||
|
||||
1. `.ply` 是 source asset,不是 runtime-ready asset
|
||||
2. 直接 runtime 解析会破坏 `AssetDatabase / ResourceManager / Library` 体系
|
||||
3. 3DGS 数据量远大于普通 mesh,更不能把 source 解析和 GPU 上传压到 draw path 上
|
||||
|
||||
### 3.2 不做通用 PLY 导入器,先做 3DGS 专用 PLY 导入器
|
||||
|
||||
本轮 importer 的职责不是支持一切 PLY 变体,而是支持当前 3DGS 工作流所需的那类 PLY。
|
||||
|
||||
正式边界是:
|
||||
|
||||
1. 支持 `binary_little_endian`
|
||||
2. 只关心 `element vertex`
|
||||
3. 通过属性名映射识别 3DGS 语义字段
|
||||
4. 对不支持的属性布局给出明确错误
|
||||
|
||||
不做的事:
|
||||
|
||||
1. 不支持带面片索引的通用模型 PLY
|
||||
2. 不支持 ASCII PLY
|
||||
3. 不支持任意 list property
|
||||
4. 不支持“能读但语义不清晰”的模糊推断
|
||||
|
||||
这不是退让,而是边界明确。当前目标是 3DGS 正式化,不是通用点云 SDK。
|
||||
|
||||
### 3.3 运行时正式资源类型命名为 `GaussianSplat`
|
||||
|
||||
建议引入:
|
||||
|
||||
1. `ResourceType::GaussianSplat`
|
||||
2. `Resources::GaussianSplat`
|
||||
3. `GaussianSplatLoader`
|
||||
4. `GaussianSplatImporter`
|
||||
|
||||
不建议把运行时资源叫:
|
||||
|
||||
1. `GaussianSplatAsset`
|
||||
2. `GaussianAsset`
|
||||
3. `PLYAsset`
|
||||
|
||||
原因:
|
||||
|
||||
1. 引擎当前 `ResourceType` 命名体系都是运行时资源名,不是 editor 资产名
|
||||
2. `Asset` 更适合出现在导入流程和文档语义中,不适合塞进正式 runtime `ResourceType`
|
||||
|
||||
### 3.4 Artifact 采用单文件主 artifact,而不是 Unity 式多 TextAsset 拼装
|
||||
|
||||
建议正式主 artifact 为:
|
||||
|
||||
1. `main.xcgsplat`
|
||||
|
||||
不建议照抄 Unity MVS 的多文件拆分为:
|
||||
|
||||
1. `*_pos.bytes`
|
||||
2. `*_other.bytes`
|
||||
3. `*_sh.bytes`
|
||||
4. `*_col.bytes`
|
||||
5. `*_chk.bytes`
|
||||
|
||||
Unity 那样做是受 Unity 资产模型约束。我们自己的引擎不需要跟着它的工程妥协走。
|
||||
|
||||
本轮更合理的正式方案是:
|
||||
|
||||
1. 单个 `xcgsplat` 文件包含 header、section table、payload
|
||||
2. loader 一次读入 metadata,按 section 定位各 payload
|
||||
3. 后续如果要做分段流式或 memory mapping,再在 schema 上扩展,不先把文件形态做碎
|
||||
|
||||
这样做的好处:
|
||||
|
||||
1. artifact 边界清晰
|
||||
2. `ArtifactDB` 记录简单
|
||||
3. 依赖跟踪简单
|
||||
4. reimport 稳定
|
||||
5. 不会出现多 sidecar 丢失或部分过期的问题
|
||||
|
||||
---
|
||||
|
||||
## 4. 对参考 MVS 的正式吸收方式
|
||||
|
||||
`mvs/3DGS-Unity` 里真正值得吸收的是流程,不是实现细节原样照搬。
|
||||
|
||||
本轮吸收的内容:
|
||||
|
||||
1. 输入语义
|
||||
2. 数据重排思路
|
||||
3. 运行时数据拆分维度
|
||||
4. `chunk` 概念
|
||||
5. 颜色纹理化而不是纯 buffer 化
|
||||
6. 未来 compute 排序与 view-data 预处理对资源格式的需求
|
||||
|
||||
本轮不直接照搬的内容:
|
||||
|
||||
1. Unity 的 `TextAsset` 资产组织方式
|
||||
2. 依赖 `UnsafeUtility.SizeOf<InputSplatData>() == vertexStride` 的固定内存布局导入
|
||||
3. editor 窗口与工具链
|
||||
4. HDRP/URP feature 接入方式
|
||||
5. 所有编辑态 GPU buffer
|
||||
|
||||
简单说:
|
||||
|
||||
1. 流程借鉴
|
||||
2. 数据语义借鉴
|
||||
3. 工程架构不照抄 Unity 的壳
|
||||
|
||||
---
|
||||
|
||||
## 5. `room.ply` 的正式支持目标
|
||||
|
||||
当前基线样本 [room.ply](D:/Xuanchi/Main/XCEngine/mvs/3DGS-Unity/room.ply) 已确认包含如下字段:
|
||||
|
||||
1. `x y z`
|
||||
2. `nx ny nz`
|
||||
3. `f_dc_0..2`
|
||||
4. `f_rest_0..44`
|
||||
5. `opacity`
|
||||
6. `scale_0..2`
|
||||
7. `rot_0..3`
|
||||
|
||||
本轮 importer 至少必须把这类文件稳定导入。
|
||||
|
||||
本轮导入结果必须包含:
|
||||
|
||||
1. splat 数量
|
||||
2. bounds
|
||||
3. position
|
||||
4. rotation
|
||||
5. scale
|
||||
6. opacity
|
||||
7. color / dc0
|
||||
8. SH 数据
|
||||
9. 供后续 chunk 化、排序、view-data 预计算所需的稳定 layout
|
||||
|
||||
本轮不要求:
|
||||
|
||||
1. 用 room.ply 直接出图
|
||||
2. 完成 chunk 压缩优化后的最终视觉验证
|
||||
|
||||
但必须做到:
|
||||
|
||||
1. room.ply 可以稳定导入成正式 artifact
|
||||
2. runtime 可以正式加载该 artifact
|
||||
3. 资源缓存与异步链路已经为后续渲染阶段准备好
|
||||
|
||||
---
|
||||
|
||||
## 6. 目标资源模型设计
|
||||
|
||||
### 6.1 `Resources::GaussianSplat`
|
||||
|
||||
建议 `GaussianSplat` 运行时资源至少包含:
|
||||
|
||||
1. `splatCount`
|
||||
2. `boundsMin / boundsMax`
|
||||
3. `dataFormatVersion`
|
||||
4. `positionFormat`
|
||||
5. `otherFormat`
|
||||
6. `colorFormat`
|
||||
7. `shFormat`
|
||||
8. `chunkCount`
|
||||
9. `cameraInfoCount`
|
||||
10. 各 section 的只读数据视图
|
||||
|
||||
这里的 section 建议为:
|
||||
|
||||
1. `positions`
|
||||
2. `other`
|
||||
3. `color`
|
||||
4. `sh`
|
||||
5. `chunks`
|
||||
6. `cameras`
|
||||
|
||||
### 6.2 `other` 的语义边界
|
||||
|
||||
建议 `other` 区域承担:
|
||||
|
||||
1. rotation
|
||||
2. scale
|
||||
3. opacity
|
||||
4. 可选 SH 索引或 chunk 相关辅助字段
|
||||
|
||||
原因:
|
||||
|
||||
1. 这与参考 MVS 的消费模型更接近
|
||||
2. compute 阶段天然会把位置和其它数据拆开消费
|
||||
3. 将来做压缩时,位置和其它数据的量化策略也不同
|
||||
|
||||
### 6.3 `color` 保持纹理友好布局
|
||||
|
||||
建议 `color` section 不是简单“每 splat 一行 float4”,而是直接按运行时纹理消费友好的布局存储。
|
||||
|
||||
原因:
|
||||
|
||||
1. 参考 MVS 最终就是把颜色上传成纹理
|
||||
2. 对 3DGS 而言,颜色作为纹理读取是合理路径
|
||||
3. 如果导入期就固化好 texel 布局,运行时不必再做一次昂贵重排
|
||||
|
||||
### 6.4 `chunks` 作为正式字段预留
|
||||
|
||||
即使第一阶段先允许 `chunkCount == 0`,artifact schema 也要正式留出 chunk 区域。
|
||||
|
||||
因为:
|
||||
|
||||
1. chunk 数据不是可有可无的小优化,它影响后续压缩、解码和排序输入
|
||||
2. 后面一旦渲染 pass 接上,就会很自然依赖 chunk
|
||||
3. 现在先把 schema 打对,比后面再迁移 artifact 版本更划算
|
||||
|
||||
---
|
||||
|
||||
## 7. Importer 设计
|
||||
|
||||
### 7.1 引入 `GaussianSplatImporter`
|
||||
|
||||
`AssetDatabase` 对 `.ply` 的识别不应直接复用 `ModelImporter`。
|
||||
|
||||
建议规则:
|
||||
|
||||
1. `.ply` 不作为通用模型格式挂进 `ModelImporter`
|
||||
2. 本轮把 `.ply` 明确识别为 `GaussianSplatImporter`
|
||||
3. 后续如果将来要支持“通用点云 PLY”,再单独扩展,不污染当前 3DGS 主线
|
||||
|
||||
### 7.2 Header 解析不能依赖固定顺序
|
||||
|
||||
正式解析流程必须是:
|
||||
|
||||
1. 读取 header
|
||||
2. 收集 `element vertex`
|
||||
3. 收集每个 `property` 的名字、类型、偏移
|
||||
4. 建立 3DGS 语义字段到 property 的映射
|
||||
5. 校验必需字段是否完整
|
||||
|
||||
不允许的方案:
|
||||
|
||||
1. 直接假定 `InputSplatData` 与文件二进制布局完全一致
|
||||
2. 直接假定 `f_rest_*` 顺序永远固定且不校验
|
||||
3. 因为 room.ply 能过就默认所有训练器导出的 PLY 都一样
|
||||
|
||||
### 7.3 importer 输出的是 cooked runtime layout,不是 source mirror
|
||||
|
||||
导入器的正式职责不是把 `.ply` 原样搬进 artifact,而是做以下转换:
|
||||
|
||||
1. 按语义解包 source data
|
||||
2. 生成规范化内部 splat 记录
|
||||
3. 计算 bounds
|
||||
4. 可选做 Morton reorder
|
||||
5. 可选做 chunk 构建
|
||||
6. 输出运行时友好的 section layout
|
||||
|
||||
这一步就是 source -> cooked artifact,而不是 source -> source copy。
|
||||
|
||||
### 7.4 关于压缩策略
|
||||
|
||||
本轮计划分两步:
|
||||
|
||||
1. 第一阶段先实现无损或近无损基础 cooked 布局
|
||||
2. 第二阶段再把参考 MVS 的压缩格式体系正式移植进来
|
||||
|
||||
原因:
|
||||
|
||||
1. 先把 artifact、loader、cache 链路跑通
|
||||
2. 再叠加压缩和 chunking
|
||||
3. 避免 importer、artifact schema、runtime loader、未来 renderer 四件事同时出错
|
||||
|
||||
第一阶段允许:
|
||||
|
||||
1. `position = float32`
|
||||
2. `other = float32 / uint32 packed`
|
||||
3. `color = RGBA32F` 或 `RGBA16F`
|
||||
4. `sh = float32`
|
||||
5. `chunk = 0`
|
||||
|
||||
第二阶段再引入:
|
||||
|
||||
1. `Norm16 / Norm11 / Norm6`
|
||||
2. `BC7 / Norm8x4`
|
||||
3. `SH clustering`
|
||||
4. `chunk` 正式压缩路径
|
||||
|
||||
---
|
||||
|
||||
## 8. Artifact 设计
|
||||
|
||||
### 8.1 主文件
|
||||
|
||||
主文件建议:
|
||||
|
||||
1. `main.xcgsplat`
|
||||
|
||||
### 8.2 文件内容建议
|
||||
|
||||
建议 `xcgsplat` 文件包含:
|
||||
|
||||
1. 文件头
|
||||
2. schema version
|
||||
3. source metadata snapshot
|
||||
4. splat metadata
|
||||
5. section table
|
||||
6. payload blob
|
||||
|
||||
section table 至少描述:
|
||||
|
||||
1. section type
|
||||
2. byte offset
|
||||
3. byte size
|
||||
4. element count
|
||||
5. element stride
|
||||
6. format enum
|
||||
|
||||
### 8.3 version 策略
|
||||
|
||||
建议单独引入:
|
||||
|
||||
1. `kGaussianSplatArtifactSchemaVersion`
|
||||
|
||||
不要复用其它 importer 的 schema version。
|
||||
|
||||
版本提升触发条件:
|
||||
|
||||
1. section 布局改变
|
||||
2. chunk 编码改变
|
||||
3. 颜色纹理布局改变
|
||||
4. SH 格式或 camera 区块布局改变
|
||||
|
||||
### 8.4 `.meta` 设计
|
||||
|
||||
即使本轮先不做完整 Inspector,也应该为后续 importer settings 预留正式字段。
|
||||
|
||||
建议至少预留:
|
||||
|
||||
1. `reorderMorton`
|
||||
2. `buildChunks`
|
||||
3. `positionFormat`
|
||||
4. `otherFormat`
|
||||
5. `colorFormat`
|
||||
6. `shFormat`
|
||||
7. `importCameras`
|
||||
|
||||
第一阶段如果先不开放 UI,也要把默认设置结构体和 hash 纳入 artifact key 计算。
|
||||
|
||||
---
|
||||
|
||||
## 9. Loader 与 ResourceManager 接入
|
||||
|
||||
### 9.1 `GaussianSplatLoader`
|
||||
|
||||
需要新增:
|
||||
|
||||
1. `GaussianSplatLoader`
|
||||
|
||||
职责:
|
||||
|
||||
1. 读取 `xcgsplat`
|
||||
2. 构建 `Resources::GaussianSplat`
|
||||
3. 提供各 section 的稳定只读视图
|
||||
|
||||
### 9.2 `ResourceManager` 正式接入
|
||||
|
||||
正式链路应支持:
|
||||
|
||||
1. `Load<GaussianSplat>("Assets/.../room.ply")`
|
||||
2. `AssetDatabase::EnsureArtifact(...)`
|
||||
3. `ResourceManager` 实际加载 `main.xcgsplat`
|
||||
|
||||
也必须支持:
|
||||
|
||||
1. `Load<GaussianSplat>("Library/Artifacts/.../main.xcgsplat")`
|
||||
|
||||
### 9.3 不能把 GPU 上传塞进 loader
|
||||
|
||||
`GaussianSplatLoader` 只负责 CPU 运行时资源,不负责 GPU residency。
|
||||
|
||||
原因:
|
||||
|
||||
1. loader 属于资源层
|
||||
2. GPU residency 属于渲染缓存层
|
||||
3. 如果在 loader 里直接创 GPU 资源,会重复 volume 这条链已经暴露过的架构问题
|
||||
|
||||
---
|
||||
|
||||
## 10. 缓存与预热设计
|
||||
|
||||
### 10.1 资源缓存层必须提前设计 GPU residency 状态机
|
||||
|
||||
即使本轮还不接最终 render pass,也必须把状态机设计写进正式方案:
|
||||
|
||||
1. `Uninitialized`
|
||||
2. `CpuReady`
|
||||
3. `GpuUploading`
|
||||
4. `GpuReady`
|
||||
5. `Failed`
|
||||
|
||||
后续 `BuiltinGaussianSplatPass` 只能消费:
|
||||
|
||||
1. `GpuReady`
|
||||
|
||||
不允许 draw path 现场把 `CpuReady -> GpuReady` 做完。
|
||||
|
||||
### 10.2 建议新增 `CachedGaussianSplat`
|
||||
|
||||
建议未来挂在 `RenderResourceCache` 或其正式拆分后的 GPU 资源缓存模块中。
|
||||
|
||||
它至少应持有:
|
||||
|
||||
1. `posBuffer`
|
||||
2. `otherBuffer`
|
||||
3. `shBuffer`
|
||||
4. `colorTexture`
|
||||
5. `chunkBuffer`
|
||||
6. `sortKeyBuffer`
|
||||
7. `sortDistanceBuffer`
|
||||
8. `viewDataBuffer`
|
||||
9. runtime-ready flag / state
|
||||
|
||||
本轮即使还不把所有 GPU 辅助 buffer 全建出来,也要把正式边界写清楚:
|
||||
|
||||
1. asset static payload buffer/texture
|
||||
2. per-frame transient / reusable working buffer
|
||||
|
||||
### 10.3 首次可见前预热,而不是首次 draw 同步补做
|
||||
|
||||
这点必须作为硬约束写死:
|
||||
|
||||
1. `GaussianSplat` 被场景反序列化后,CPU artifact 加载完成就进入 GPU 预热队列
|
||||
2. GPU 上传在后台或明确的准备阶段完成
|
||||
3. 首次 draw 只允许跳过未 ready 对象,不允许同步创建大资源
|
||||
|
||||
原因:
|
||||
|
||||
1. 3DGS 资产通常很大
|
||||
2. room.ply 这种样本数据量已经足够把 draw path 压垮
|
||||
3. 当前 volume 这条链已经证明“首次绘制再上传”不是可接受正式方案
|
||||
|
||||
### 10.4 warm cache 验收标准
|
||||
|
||||
本轮资源 / 缓存层至少要达到:
|
||||
|
||||
1. 第二次加载 room.ply 时不重新解析 source `.ply`
|
||||
2. 直接命中 `Library/Artifacts`
|
||||
3. `ResourceManager` 不会因为 cache hit 又走 source importer
|
||||
4. 后续 GPU 预热可以稳定复用 artifact 输出
|
||||
|
||||
---
|
||||
|
||||
## 11. 测试计划
|
||||
|
||||
### 11.1 基线样本
|
||||
|
||||
统一使用:
|
||||
|
||||
1. [room.ply](D:/Xuanchi/Main/XCEngine/mvs/3DGS-Unity/room.ply)
|
||||
|
||||
它将承担:
|
||||
|
||||
1. importer 基线
|
||||
2. artifact 基线
|
||||
3. cache hit 基线
|
||||
4. future renderer 接入基线
|
||||
|
||||
### 11.2 Unit Tests
|
||||
|
||||
本轮至少要补齐以下单测:
|
||||
|
||||
1. `PLY header parser` 正确识别 `vertexCount / properties / offsets`
|
||||
2. `GaussianSplatImporter` 能正确识别 room.ply 的必需字段
|
||||
3. 缺字段时给出明确错误
|
||||
4. 非法格式时给出明确错误
|
||||
5. `xcgsplat` 写入 / 读取 roundtrip 正确
|
||||
6. `GaussianSplatLoader` 能读取 artifact 并恢复 metadata 与 section view
|
||||
7. `ResourceManager` 能从 `Assets/.../room.ply` 正式加载 `GaussianSplat`
|
||||
8. `AssetDatabase` 对 `.ply` 的 `EnsureArtifact` 能稳定复用
|
||||
|
||||
### 11.3 Integration Tests
|
||||
|
||||
本轮先做资源链集成测试,不做最终出图测试。
|
||||
|
||||
至少要有:
|
||||
|
||||
1. `room.ply -> EnsureArtifact -> Load<GaussianSplat>` 全链通过
|
||||
2. 二次加载命中 artifact,不触发 reimport
|
||||
3. 修改 source writeTime 后能触发 reimport
|
||||
4. 清库后能重建 artifact
|
||||
|
||||
### 11.4 为后续渲染阶段准备的 smoke test
|
||||
|
||||
虽然本轮不做 3DGS pass,但建议提前补一个 GPU 资源 smoke test:
|
||||
|
||||
1. 读取 `GaussianSplat`
|
||||
2. 构建最小 GPU cache entry
|
||||
3. 创建 `pos/other/sh/chunk` buffer 与 `color` texture
|
||||
4. 验证状态进入 `GpuReady`
|
||||
|
||||
这样后续 renderer 接入时,不会把“资源问题”和“渲染问题”混成一团。
|
||||
|
||||
---
|
||||
|
||||
## 12. 分阶段执行计划
|
||||
|
||||
### Phase 1:资源类型与 artifact schema 落地
|
||||
|
||||
目标:
|
||||
|
||||
1. 正式引入 `ResourceType::GaussianSplat`
|
||||
2. 正式引入 `Resources::GaussianSplat`
|
||||
3. 正式定义 `xcgsplat` artifact schema
|
||||
|
||||
任务:
|
||||
|
||||
1. 扩展 `ResourceType`
|
||||
2. 新增 `GaussianSplat` 运行时资源类
|
||||
3. 设计 artifact header 与 section table
|
||||
4. 新增 `WriteGaussianSplatArtifactFile / LoadGaussianSplatArtifact`
|
||||
|
||||
验收标准:
|
||||
|
||||
1. `xcgsplat` 可写可读
|
||||
2. 资源元数据可稳定 roundtrip
|
||||
|
||||
### Phase 2:3DGS 专用 PLY importer 正式化
|
||||
|
||||
目标:
|
||||
|
||||
1. 把 `.ply` 纳入 `GaussianSplatImporter`
|
||||
|
||||
任务:
|
||||
|
||||
1. 新增 header parser
|
||||
2. 新增 3DGS property mapping
|
||||
3. 读取 room.ply 并转换成规范化内部 splat 数据
|
||||
4. 输出基础 cooked artifact
|
||||
|
||||
验收标准:
|
||||
|
||||
1. room.ply 可稳定导入
|
||||
2. 不依赖固定 struct stride == 文件 stride
|
||||
3. 错误路径有清晰日志
|
||||
|
||||
### Phase 3:AssetDatabase / Library / ResourceManager 接入
|
||||
|
||||
目标:
|
||||
|
||||
1. 把 `GaussianSplat` 完整接进项目资源工作流
|
||||
|
||||
任务:
|
||||
|
||||
1. `.ply -> GaussianSplatImporter`
|
||||
2. `EnsureArtifact(..., ResourceType::GaussianSplat)`
|
||||
3. `GaussianSplatLoader`
|
||||
4. `Load<GaussianSplat>()`
|
||||
|
||||
验收标准:
|
||||
|
||||
1. 可以通过 `Assets/.../room.ply` 正式加载
|
||||
2. cache hit 时不重走 source parse
|
||||
|
||||
### Phase 4:资源缓存与 GPU residency 预热骨架
|
||||
|
||||
目标:
|
||||
|
||||
1. 正式建立 3DGS GPU 资源缓存的边界
|
||||
|
||||
任务:
|
||||
|
||||
1. 设计 `CachedGaussianSplat`
|
||||
2. 建立 GPU residency 状态机
|
||||
3. 实现最小 GPU 资源构建 smoke path
|
||||
4. 明确禁止 draw path 首次同步上传
|
||||
|
||||
验收标准:
|
||||
|
||||
1. room.ply 对应的 `GaussianSplat` 可以被 GPU cache 预热成 ready 状态
|
||||
2. 资源层与渲染层边界清晰
|
||||
|
||||
### Phase 5:测试补齐与收口
|
||||
|
||||
目标:
|
||||
|
||||
1. 让这条链路可回归、可持续演进
|
||||
|
||||
任务:
|
||||
|
||||
1. 补全 importer / loader / cache hit / reimport 单测与集成测试
|
||||
2. 输出阶段性说明
|
||||
3. 为后续 renderer 接入保留唯一正式资源路径
|
||||
|
||||
验收标准:
|
||||
|
||||
1. room.ply 全链路测试稳定
|
||||
2. 不存在“临时直接读 ply”的旁路
|
||||
|
||||
---
|
||||
|
||||
## 13. 明确不允许出现的临时方案
|
||||
|
||||
以下方案本轮禁止出现:
|
||||
|
||||
1. 为了尽快出图,先在 render pass 里直接解析 `.ply`
|
||||
2. 先做一个 `BinaryResource` 包 `.ply` 内容,后面再说
|
||||
3. 先把 `.ply` 当 `Mesh` 导入
|
||||
4. 把 3DGS 的 GPU buffer 直接挂在 `Material` 资源本体上作为持久化资产
|
||||
5. 首次 draw 时同步创建 `pos / other / sh / color` GPU 资源
|
||||
6. 把 `room.ply` 单独写死成特判
|
||||
|
||||
这些做法都会把本该正式化的主线重新拉回临时方案。
|
||||
|
||||
---
|
||||
|
||||
## 14. 本轮完成标志
|
||||
|
||||
当以下条件同时成立时,这份计划才算完成:
|
||||
|
||||
1. `.ply` 已正式被 `GaussianSplatImporter` 接管
|
||||
2. `GaussianSplat` 已成为正式 `ResourceType`
|
||||
3. `room.ply` 能稳定导入成 `xcgsplat`
|
||||
4. `ResourceManager` 能正式加载 `GaussianSplat`
|
||||
5. 二次加载能稳定命中 artifact
|
||||
6. GPU residency cache 骨架已经建立,不允许首次 draw 同步补做
|
||||
7. 资源层与缓存层测试已经覆盖 room.ply 主路径
|
||||
|
||||
---
|
||||
|
||||
## 15. 一句话结论
|
||||
|
||||
这条主线的第一步不是“做一个 ply 读取器”,而是把 3DGS 正式升级为引擎里的 `GaussianSplat` 资源体系:
|
||||
由 `GaussianSplatImporter` 把 `.ply` 转成 `xcgsplat` cooked artifact,由 `GaussianSplatLoader` 与 `ResourceManager` 正式接管加载,再由独立的 GPU residency cache 提前完成资源预热,为后续 3DGS 渲染 pass 提供唯一、稳定、无旁路的正式输入。
|
||||
248
docs/plan/3DGS渲染路径对齐参考实现修复计划_2026-04-11.md
Normal file
248
docs/plan/3DGS渲染路径对齐参考实现修复计划_2026-04-11.md
Normal file
@@ -0,0 +1,248 @@
|
||||
# 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 基线。
|
||||
@@ -1,186 +0,0 @@
|
||||
# Assets目录Watcher与自动导入刷新计划
|
||||
|
||||
日期: 2026-04-10
|
||||
|
||||
## 1. 背景
|
||||
|
||||
当前工程的资产导入链路已经具备以下能力:
|
||||
|
||||
- 打开项目时执行一次 `BootstrapProject`
|
||||
- 手动执行 `Refresh / Reimport / Reimport All`
|
||||
- 资源真正加载时通过 `EnsureArtifact` 懒导入
|
||||
|
||||
但它还缺少 Unity 风格里最关键的一层:
|
||||
|
||||
- `Assets/` 目录发生外部变化时,编辑器能自动感知并刷新资产数据库
|
||||
|
||||
这里的“外部变化”包括但不限于:
|
||||
|
||||
- 资源文件新增、删除、覆盖
|
||||
- `.meta` 文件新增、删除、修改
|
||||
- 文件夹新增、删除、重命名
|
||||
- DCC 工具重新导出同路径资源
|
||||
- `git pull`、脚本生成、资源同步工具写入
|
||||
|
||||
所以问题不在“拖进 editor 时是否导入”,而在“是否存在一条持续观察 `Assets` 目录变化并驱动资产系统刷新的通路”。
|
||||
|
||||
## 2. 现状判断
|
||||
|
||||
从当前代码结构看,这个功能是好加的,难度中等,不是重写级别:
|
||||
|
||||
- `ResourceManager` 已经有 `RefreshProjectAssets()`、`ReimportProjectAsset()`、`RebuildProjectAssetCache()`
|
||||
- `AssetImportService / AssetDatabase` 已经能扫描项目、维护 Source DB / Artifact DB
|
||||
- editor 已经有稳定的每帧更新入口 `EditorWorkspace::Update`
|
||||
- `ProjectManager` 已经支持目录树刷新
|
||||
|
||||
真正缺的是一个独立的 watcher 层,把“磁盘变化”转成“主线程上的资产刷新动作”。
|
||||
|
||||
## 3. 目标
|
||||
|
||||
第一阶段目标:
|
||||
|
||||
- 编辑器运行时自动感知 `Assets/` 目录外部变化
|
||||
- 自动刷新 `AssetDatabase` lookup / metadata 状态
|
||||
- 自动刷新 Project 面板当前目录
|
||||
- 对批量拷贝和连续覆盖做去抖,不要每个文件都立刻刷新一次
|
||||
|
||||
长期目标:
|
||||
|
||||
- 接近 Unity 的体验:外部改动几乎无需手动 `Refresh`
|
||||
- 为后续更细粒度的增量 reimport、缩略图刷新、场景引用修复提供基础设施
|
||||
|
||||
## 4. 非目标
|
||||
|
||||
第一阶段不做:
|
||||
|
||||
- 原生 Win32 `ReadDirectoryChangesW` 后台线程 watcher
|
||||
- 精确到单文件的增量 reimport 调度
|
||||
- 已经加载到场景里的资源热替换
|
||||
- 资产依赖图级别的自动级联重导
|
||||
- 跨平台 watcher 后端抽象
|
||||
|
||||
这些放到后续阶段。
|
||||
|
||||
## 5. 分阶段方案
|
||||
|
||||
### Phase 1: 轮询式 Assets Watcher
|
||||
|
||||
实现一个 editor 内部的 `ProjectAssetWatcher`:
|
||||
|
||||
- 只监听当前项目的 `Assets/`
|
||||
- 通过定时扫描递归快照检测新增/修改/删除
|
||||
- 包含普通资源文件、文件夹、`.meta`
|
||||
- 通过扫描间隔 + 去抖窗口合并一批变化
|
||||
- 在主线程触发:
|
||||
- `ResourceManager::RefreshProjectAssets()`
|
||||
- `ProjectManager::RefreshCurrentFolder()`
|
||||
|
||||
优点:
|
||||
|
||||
- 实现快,风险低
|
||||
- 不引入后台线程和 Win32 句柄生命周期问题
|
||||
- 先把“自动感知外部变化”补上
|
||||
|
||||
缺点:
|
||||
|
||||
- 不是事件驱动,超大项目下效率一般
|
||||
- 只能做到“自动刷新资产数据库”,还不是“精确增量重导”
|
||||
|
||||
### Phase 2: 原生 Win32 Directory Watcher
|
||||
|
||||
把 Phase 1 的扫描后端替换为 Win32 原生目录通知:
|
||||
|
||||
- `ReadDirectoryChangesW`
|
||||
- 递归监听 `Assets/`
|
||||
- 把原始事件推入线程安全队列
|
||||
- 主线程消费并做路径标准化、重命名配对、去抖合并
|
||||
|
||||
目标:
|
||||
|
||||
- 降低大项目轮询成本
|
||||
- 提高外部改动响应速度
|
||||
|
||||
### Phase 3: 增量 Reimport 调度
|
||||
|
||||
在 watcher 已经稳定后,再做更接近 Unity 的行为:
|
||||
|
||||
- 对新增/修改的 importable asset 调用增量 reimport
|
||||
- 删除时清理 lookup / orphan artifact
|
||||
- 重命名时保证 `.meta` / GUID / path snapshot 同步
|
||||
- 限制一次批量刷新中的 `UnloadAll()` 影响范围
|
||||
|
||||
这个阶段需要重新审视当前 `ResourceManager::ReimportProjectAsset()` 里 `UnloadAll()` 的语义,否则外部频繁改资源会让缓存抖动太大。
|
||||
|
||||
## 6. 代码落点
|
||||
|
||||
第一阶段建议改动:
|
||||
|
||||
- `editor/src/Core/ProjectAssetWatcher.h/.cpp`
|
||||
- watcher 快照、扫描、去抖、变更合并
|
||||
- `editor/src/Core/EditorWorkspace.h`
|
||||
- `Attach / Detach / Update` 接入 watcher 生命周期
|
||||
- `editor/CMakeLists.txt`
|
||||
- 编译新 watcher 文件
|
||||
|
||||
可能的辅助修改:
|
||||
|
||||
- 日志输出,方便确认自动刷新是否触发
|
||||
- 若后续需要 UI 提示,可再补 editor 事件或状态文本
|
||||
|
||||
## 7. 第一阶段执行细则
|
||||
|
||||
### 7.1 快照内容
|
||||
|
||||
每个 `Assets` 路径记录:
|
||||
|
||||
- 规范化相对路径 key
|
||||
- 是否目录
|
||||
- 文件大小
|
||||
- 最后写入时间
|
||||
|
||||
目录也要纳入快照,这样能检测外部新建/删除文件夹。
|
||||
|
||||
### 7.2 扫描策略
|
||||
|
||||
- 默认扫描间隔:`0.75s`
|
||||
- 默认去抖窗口:`0.35s`
|
||||
- 若扫描发现连续变动,则不断延长本轮刷新触发时间
|
||||
- 当一小批变化稳定下来,再统一执行一次刷新
|
||||
|
||||
### 7.3 主线程动作
|
||||
|
||||
统一执行:
|
||||
|
||||
- `ResourceManager::Get().RefreshProjectAssets()`
|
||||
- `context.GetProjectManager().RefreshCurrentFolder()`
|
||||
|
||||
这一步先只刷新资产数据库和 Project 面板,不直接做逐文件 reimport。
|
||||
|
||||
## 8. 风险点
|
||||
|
||||
- 递归扫描频率太高会拖慢超大项目
|
||||
- 当前 `RefreshProjectAssets()` 不会主动生成所有 artifact,只是同步资产数据库
|
||||
- `ProjectManager::RefreshCurrentFolder()` 是整树重建,不是局部刷新
|
||||
- editor 内部自己改文件时,watcher 也会看到这些变化,需要接受重复刷新
|
||||
|
||||
这些都在第一阶段可控范围内。
|
||||
|
||||
## 9. 验收标准
|
||||
|
||||
第一阶段完成后,应满足:
|
||||
|
||||
- 外部往 `Assets/` 复制一个资源文件,Project 面板自动出现
|
||||
- 外部删除 `Assets/` 下资源,Project 面板自动消失
|
||||
- 外部覆盖修改已有资源,资产数据库自动刷新
|
||||
- 不需要用户手点 `Refresh`
|
||||
- 批量复制多个资源时,不会每个文件都触发一次明显卡顿的刷新
|
||||
|
||||
## 10. 本次执行范围
|
||||
|
||||
本次开始执行 Phase 1:
|
||||
|
||||
- 先落地轮询式 watcher
|
||||
- 打通 editor 生命周期
|
||||
- 先让 `Assets` 外部变更自动刷新
|
||||
|
||||
Phase 2 的 Win32 原生 watcher 放在后续迭代。
|
||||
@@ -1,765 +0,0 @@
|
||||
# NanoVDB 稀疏体积渲染正式集成计划(Unity 对齐版)
|
||||
|
||||
文档日期:2026-04-08
|
||||
|
||||
适用范围:当前 `XCEngine` 的 `Resources / Shader / RHI / Rendering / Components / Editor` 体系,以及未来要对齐 Unity 的 C#、Shader、SRP 工作流。
|
||||
|
||||
文档目标:把 `MVS/VolumeRenderer` 中已经验证过的 `NanoVDB + GPU Buffer + Ray Marching` 原型,整理成一套能正式进入引擎主线、且从一开始就严格兼容 Unity 风格 shader/C# 语义的落地方案。
|
||||
|
||||
---
|
||||
|
||||
## 1. 先给结论
|
||||
|
||||
这条主线的正式方向不是:
|
||||
|
||||
1. 把体积数据退化成 `Texture3D`
|
||||
2. 给当前引擎临时塞一套 volume 专用 shader 语法
|
||||
3. 只在某个后端里私下打通一条 demo 路径
|
||||
|
||||
这条主线的正式方向必须是:
|
||||
|
||||
1. 保留 `NanoVDB` 的稀疏体积本质
|
||||
2. 把 buffer 资源能力补齐到引擎正式架构里
|
||||
3. 让高层 authoring 语义严格对齐 Unity
|
||||
4. 让低层 RHI 分类只存在于引擎内部
|
||||
5. 让 volumetric 成为 renderer / future SRP 中的正式 pass,而不是特例后处理
|
||||
|
||||
因此,这份计划默认坚持一条红线:
|
||||
|
||||
`StructuredBuffer / RawBuffer` 可以作为引擎内部资源分类存在,但作者可见语义、未来 C# API 语义、shader 写法语义,都必须对齐 Unity 既有做法,而不是引擎自创。
|
||||
|
||||
---
|
||||
|
||||
## 2. 设计总原则
|
||||
|
||||
### 2.1 Unity 对齐原则
|
||||
|
||||
后续所有设计都必须同时满足下面这些条件:
|
||||
|
||||
1. shader 文件继续朝 Unity 风格 `.shader + HLSLPROGRAM` 组织收口。
|
||||
2. 作者侧 buffer 语义必须沿用 HLSL/Unity 现成写法,例如:
|
||||
- `StructuredBuffer<T>`
|
||||
- `ByteAddressBuffer`
|
||||
- `RWStructuredBuffer<T>`
|
||||
- `RWByteAddressBuffer`
|
||||
3. 不允许为了体积渲染单独发明新的高层 buffer 关键字、声明块或材质 schema。
|
||||
4. `Properties` 块只用于可序列化作者参数,不用于声明或承载 GPU buffer。
|
||||
5. 未来 C# 层的 buffer 接口方向,必须对齐 Unity 的:
|
||||
- `ComputeBuffer`
|
||||
- `GraphicsBuffer`
|
||||
- `Material.SetBuffer`
|
||||
- `Shader.SetGlobalBuffer`
|
||||
- `CommandBuffer.SetGlobalBuffer`
|
||||
6. renderer 侧对 buffer 的真正绑定,必须是运行时 per-pass / per-object / global resource binding,而不是材质资产直接持有底层 view。
|
||||
7. `StructuredBuffer / RawBuffer` 在引擎里首先是底层编译和绑定分类,不是高层 authoring 概念。
|
||||
|
||||
### 2.2 稀疏体积原则
|
||||
|
||||
1. `NanoVDB` 必须按稀疏体积方案正式接入。
|
||||
2. 不允许把正式路线退化成 `Texture3D` 替代方案。
|
||||
3. `Texture3D` 可以作为引擎未来的独立能力预留,但不是这条主线的收口路径。
|
||||
|
||||
### 2.3 SRP 兼容原则
|
||||
|
||||
1. volumetric 必须被设计成正式 scene pass。
|
||||
2. 它的输入和生命周期要能自然映射到未来 SRP 的 renderer feature / render pass event。
|
||||
3. 不允许把它设计成只能在 builtin pipeline 中硬编码存在的特例逻辑。
|
||||
|
||||
---
|
||||
|
||||
## 3. Unity 参考基线
|
||||
|
||||
`参考/Unity NanoVDB` 已经给出了一条对本引擎非常重要的参考方向:
|
||||
|
||||
1. C# 侧使用 `ComputeBuffer`
|
||||
2. 运行时通过 `Material.SetBuffer("buf", gpuBuffer)` 绑定
|
||||
3. shader 侧通过 `StructuredBuffer<uint>` 读取
|
||||
4. `ComputeBufferType.Default` 对应的正是通用 GPU buffer 资源路径,而不是材质属性系统
|
||||
|
||||
这说明后续正式集成时,高层语义应该对齐的是:
|
||||
|
||||
1. Unity 的 buffer authoring / binding 模式
|
||||
2. Unity 的 shader 资源声明模式
|
||||
3. Unity/HDRP 中把体积渲染插入 render pass 的方式
|
||||
|
||||
而不是:
|
||||
|
||||
1. 直接照抄 `MVS` 的 D3D12 私有代码
|
||||
2. 用引擎内部 `RawBuffer` 这个名词去污染高层 shader/C# 接口
|
||||
3. 为了先出图,临时开 volume 专用旁路
|
||||
|
||||
---
|
||||
|
||||
## 4. 本轮明确不做什么
|
||||
|
||||
为了防止架构被 demo 反向拖偏,本轮明确不做下面这些事:
|
||||
|
||||
1. 不把 `NanoVDB` 退化成正式 `Texture3D` 方案。
|
||||
2. 不把 `MVS/VolumeRenderer` 的 D3D12 私有 helper、私有绑定逻辑直接搬进引擎主线。
|
||||
3. 不先做全局体积雾、froxel fog、clustered volumetrics。
|
||||
4. 不先做复杂编辑器 authoring 工具,例如体积笔刷和节点编辑器。
|
||||
5. 不先做 DXR / path tracing 体渲染。
|
||||
6. 不为体积渲染另开一套与 Unity 不一致的 shader/material/C# 表层语法。
|
||||
|
||||
---
|
||||
|
||||
## 5. 当前真正缺的不是“算法”,而是正式能力
|
||||
|
||||
当前原型已经证明了下面几件事:
|
||||
|
||||
1. `NanoVDB` 数据可以读。
|
||||
2. `NanoVDB` 数据可以上传到 GPU buffer。
|
||||
3. shader 可以基于 `pnanovdb` 做树遍历、HDDA 跳空和 ray marching。
|
||||
4. D3D12 原型能完成从数据到画面的闭环。
|
||||
|
||||
但引擎主线真正还缺下面这些正式能力:
|
||||
|
||||
1. `.nvdb` 如何进入资产与 artifact 体系。
|
||||
2. shader 如何用 Unity 风格语义正式声明 buffer 资源。
|
||||
3. 材质系统如何和运行时 buffer 绑定做严格职责分离。
|
||||
4. RHI 三后端如何正式支持 buffer SRV/UAV。
|
||||
5. renderer 如何把体积渲染作为正式 pass 插入 frame。
|
||||
6. 未来 C# 层如何以 Unity 风格接口绑定 buffer。
|
||||
7. editor 和测试如何验证整条链路不会回归。
|
||||
|
||||
所以本计划的本质不是“移植 demo”,而是“先补正式能力,再把 demo 算法放到正式能力之上”。
|
||||
|
||||
---
|
||||
|
||||
## 6. 正式架构目标
|
||||
|
||||
### 6.1 作者可见层:严格对齐 Unity 风格
|
||||
|
||||
这一层是未来引擎用户、shader 作者、C# 脚本作者看到的语义表面。
|
||||
|
||||
要求如下:
|
||||
|
||||
1. `.shader` 文件语法继续按 Unity 风格推进。
|
||||
2. buffer 在 shader 中的声明使用 HLSL 现成资源类型,不使用自定义资源关键字。
|
||||
3. 材质面板暴露的是作者参数,而不是底层 GPU buffer 本体。
|
||||
4. 如果未来提供脚本绑定接口,脚本看到的应该是 `ComputeBuffer / GraphicsBuffer` 风格对象和 `SetBuffer` 风格 API。
|
||||
|
||||
这层明确禁止:
|
||||
|
||||
1. 在 `Properties` 中定义 `StructuredBuffer` 或 `RawBuffer`
|
||||
2. 在 `.shader` 外层再发明 `BufferResources { ... }` 之类自定义块
|
||||
3. 让作者直接接触 `RHIResourceView` 或后端 view descriptor
|
||||
|
||||
### 6.2 引擎中层:运行时绑定契约
|
||||
|
||||
这一层负责把“作者声明的 shader 资源”和“运行时真实绑定的 GPU 资源”接起来。
|
||||
|
||||
正式边界应该是:
|
||||
|
||||
1. `Material` 只持有:
|
||||
- shader 引用
|
||||
- keyword / render state
|
||||
- 序列化参数
|
||||
- 常规纹理/采样器类作者资源
|
||||
2. `VolumeField` 或其他 runtime producer 提供真实 GPU buffer 资源
|
||||
3. `Renderer` / `CommandBuffer` / future script binding API 在渲染阶段把 buffer 绑定进 pass
|
||||
|
||||
也就是说:
|
||||
|
||||
1. `NanoVDB` 主数据不是普通 material property
|
||||
2. 它是运行时资源绑定
|
||||
3. 材质只负责“怎么渲染”
|
||||
4. `VolumeField` 负责“渲染的数据是什么”
|
||||
|
||||
### 6.3 引擎底层:RHI 资源分类
|
||||
|
||||
在 RHI 与编译反射层,可以存在正式的内部分类,例如:
|
||||
|
||||
1. `StructuredBuffer`
|
||||
2. `RawBuffer`
|
||||
3. 后续可扩展:
|
||||
- `ByteAddressBuffer` 的内部映射
|
||||
- `RWStructuredBuffer`
|
||||
- `RWByteAddressBuffer`
|
||||
|
||||
但要强调:
|
||||
|
||||
1. 这些是内部分类,不是高层 authoring 语法。
|
||||
2. 高层写的是 Unity/HLSL 资源声明。
|
||||
3. 引擎负责把这些声明反射并落到底层分类。
|
||||
|
||||
### 6.4 资源层:正式 VolumeField 资产
|
||||
|
||||
新增正式资源抽象:
|
||||
|
||||
1. `ResourceType::VolumeField`
|
||||
2. `Resources::VolumeField`
|
||||
3. `VolumeFieldLoader`
|
||||
4. `NanoVDBVolumeImporter`
|
||||
|
||||
导入链路建议:
|
||||
|
||||
1. 源资产:`Assets/.../*.nvdb`
|
||||
2. artifact:`Library/Artifacts/.../main.xcvol`
|
||||
|
||||
`main.xcvol` 建议包含:
|
||||
|
||||
1. schema version
|
||||
2. storage kind
|
||||
3. grid type / grid class
|
||||
4. world bbox
|
||||
5. voxel size
|
||||
6. active voxel statistics
|
||||
7. payload byte size
|
||||
8. 原始 `NanoVDB` blob
|
||||
|
||||
原则:
|
||||
|
||||
1. 第一阶段不做有损转换。
|
||||
2. artifact 中保留原始稀疏表示。
|
||||
3. metadata 单独结构化,供 loader / renderer 快速读取。
|
||||
|
||||
### 6.5 渲染层:正式体积 pass
|
||||
|
||||
正式新增:
|
||||
|
||||
1. `VolumeRendererComponent`
|
||||
2. `VisibleVolumeItem`
|
||||
3. `RenderSceneData.visibleVolumes`
|
||||
4. `BuiltinVolumetricPass`
|
||||
|
||||
该 pass 的职责:
|
||||
|
||||
1. 消费 `VisibleVolumeItem`
|
||||
2. 绑定 camera / depth / lighting / shadow / volume buffer / material 参数
|
||||
3. 执行 ray marching
|
||||
4. 合成到 scene color
|
||||
|
||||
推荐阶段位置:
|
||||
|
||||
1. `ShadowCaster`
|
||||
2. `Depth / Opaque`
|
||||
3. `Skybox`
|
||||
4. `Volumetrics`
|
||||
5. `Transparent`
|
||||
6. `PostProcess`
|
||||
7. `FinalColor`
|
||||
|
||||
这保证它天然兼容未来 SRP 的 renderer feature / pass event 方向。
|
||||
|
||||
### 6.6 C# 层预留目标
|
||||
|
||||
即使当前 C# 模块还没完全落地,这条计划也必须先把接口方向冻结。
|
||||
|
||||
后续需要预留的高层语义包括:
|
||||
|
||||
1. `ComputeBuffer`/`GraphicsBuffer` 风格资源对象
|
||||
2. `Material.SetBuffer`
|
||||
3. `Shader.SetGlobalBuffer`
|
||||
4. `CommandBuffer.SetGlobalBuffer`
|
||||
|
||||
这一层的原则是:
|
||||
|
||||
1. 先冻结语义,再决定托管层具体封装细节
|
||||
2. 不允许等到以后 C# 接入时再发现底层 buffer 路径与 Unity 语义冲突
|
||||
|
||||
---
|
||||
|
||||
## 7. 关键命名与职责冻结
|
||||
|
||||
为了避免后续反复推翻命名,这一轮先冻结:
|
||||
|
||||
1. 正式资源抽象:`VolumeField`
|
||||
2. 存储种类:`VolumeStorageKind::NanoVDB`
|
||||
3. 导入器:`NanoVDBVolumeImporter`
|
||||
4. 运行时加载器:`VolumeFieldLoader`
|
||||
5. 组件:`VolumeRendererComponent`
|
||||
6. frame data:`VisibleVolumeItem`
|
||||
7. pass:`BuiltinVolumetricPass`
|
||||
|
||||
命名原则:
|
||||
|
||||
1. 引擎对外抽象是 `VolumeField`
|
||||
2. `NanoVDB` 是第一种正式存储后端
|
||||
3. 不把具体文件格式命名泄露到整个 renderer 表面
|
||||
|
||||
---
|
||||
|
||||
## 8. Shader 与 Buffer 的正式契约
|
||||
|
||||
这是整个计划最关键的部分。
|
||||
|
||||
### 8.1 shader 作者看到的语义
|
||||
|
||||
作者应该写的是:
|
||||
|
||||
1. `StructuredBuffer<uint>` 这类 Unity/HLSL 现成声明
|
||||
2. 未来必要时的 `ByteAddressBuffer`
|
||||
3. 如果进入写路径,再扩到 `RWStructuredBuffer<T>` / `RWByteAddressBuffer`
|
||||
|
||||
作者不应该写的是:
|
||||
|
||||
1. `RawBuffer`
|
||||
2. `StructuredRawBuffer`
|
||||
3. 任何引擎自创的高层 buffer 类型关键字
|
||||
|
||||
### 8.2 材质系统的职责边界
|
||||
|
||||
材质系统只负责:
|
||||
|
||||
1. 密度、吸收、散射、步长、相位函数参数
|
||||
2. tint / emission
|
||||
3. shader variant / keyword
|
||||
4. render state
|
||||
|
||||
材质系统不负责:
|
||||
|
||||
1. 序列化 `NanoVDB` payload
|
||||
2. 序列化 GPU buffer handle
|
||||
3. 序列化底层 SRV/UAV view
|
||||
|
||||
### 8.3 运行时绑定职责
|
||||
|
||||
运行时绑定应该由以下层级之一完成:
|
||||
|
||||
1. `Renderer`
|
||||
2. `CommandBuffer`
|
||||
3. future script API
|
||||
|
||||
典型语义应接近:
|
||||
|
||||
1. `material.SetBuffer("buf", volumeBuffer)`
|
||||
2. `commandBuffer.SetGlobalBuffer(name, buffer)`
|
||||
|
||||
### 8.4 内部映射规则
|
||||
|
||||
建议的内部映射是:
|
||||
|
||||
1. `StructuredBuffer<T>` -> `ShaderResourceType::StructuredBuffer`
|
||||
2. `ByteAddressBuffer` -> `ShaderResourceType::RawBuffer`
|
||||
3. `RWStructuredBuffer<T>` -> UAV + structured 分类
|
||||
4. `RWByteAddressBuffer` -> UAV + raw 分类
|
||||
|
||||
这里的重点不是名字,而是边界:
|
||||
|
||||
1. 高层保持 Unity/HLSL 语义
|
||||
2. 中层做反射和 metadata
|
||||
3. 低层做真正 view 创建与 descriptor 绑定
|
||||
|
||||
### 8.5 一个硬性前置条件
|
||||
|
||||
如果当前 shader authoring 体系还不能正式表达 Unity 风格 buffer 声明,那么这个缺口的优先级高于体积渲染本身。
|
||||
|
||||
也就是说:
|
||||
|
||||
1. 不允许为了先做 volumetric,单独给它走私有 shader 语法
|
||||
2. 必须先把正式 shader authoring / reflection / runtime binding 路径打通
|
||||
|
||||
---
|
||||
|
||||
## 9. RHI 正式能力目标
|
||||
|
||||
RHI 侧至少需要补齐:
|
||||
|
||||
1. `RHIDevice::CreateShaderResourceView(RHIBuffer*, const ResourceViewDesc&)`
|
||||
2. `RHIDevice::CreateUnorderedAccessView(RHIBuffer*, const ResourceViewDesc&)`
|
||||
3. `ResourceViewDesc` 的 buffer 字段:
|
||||
- firstElement
|
||||
- elementCount
|
||||
- structureByteStride
|
||||
- raw / structured / formatted 标记
|
||||
4. `RHIResourceView` 对 buffer SRV/UAV 的统一表达
|
||||
5. descriptor set / bind group 更新路径对 buffer view 的正式支持
|
||||
|
||||
三后端预期映射:
|
||||
|
||||
1. D3D12:
|
||||
- buffer SRV
|
||||
- buffer UAV
|
||||
2. Vulkan:
|
||||
- storage buffer
|
||||
- 需要时使用 texel buffer 路径
|
||||
3. OpenGL:
|
||||
- SSBO 优先
|
||||
- 明确 GLSL 转译和绑定规则
|
||||
|
||||
这里必须强调:
|
||||
|
||||
1. 这些是底层实现能力
|
||||
2. 它们服务于高层 Unity 风格语义
|
||||
3. 它们不能反过来决定高层作者接口长什么样
|
||||
|
||||
---
|
||||
|
||||
## 10. 正式实施阶段
|
||||
|
||||
## 阶段 0:冻结 Unity 对齐边界
|
||||
|
||||
### 目标
|
||||
|
||||
在改代码前,先冻结高层语义、中层契约、低层分类三层边界。
|
||||
|
||||
### 任务
|
||||
|
||||
1. 冻结 `VolumeField` / `VolumeRendererComponent` / `VisibleVolumeItem` / `BuiltinVolumetricPass` 命名
|
||||
2. 冻结体积渲染在 scene pass 中的阶段位置
|
||||
3. 冻结高层 buffer 语义:
|
||||
- 只接受 Unity/HLSL 写法
|
||||
4. 冻结材质边界:
|
||||
- buffer 不进 `Properties`
|
||||
5. 冻结未来 C# 方向:
|
||||
- `ComputeBuffer / GraphicsBuffer + SetBuffer` 语义
|
||||
6. 冻结内部映射:
|
||||
- `StructuredBuffer<T>` -> internal structured
|
||||
- `ByteAddressBuffer` -> internal raw
|
||||
|
||||
### 验收标准
|
||||
|
||||
1. 后续实现阶段不再反复摇摆高层语义
|
||||
|
||||
---
|
||||
|
||||
## 阶段 1:先补底层 buffer 资源视图能力
|
||||
|
||||
### 目标
|
||||
|
||||
补齐 renderer 目前缺失的“buffer 作为正式 shader 资源”的底层能力。
|
||||
|
||||
### 任务
|
||||
|
||||
1. 完成 `RHIBuffer` 的 SRV/UAV 创建接口
|
||||
2. 补齐 `ResourceViewDesc` 的 buffer 视图字段
|
||||
3. 让 descriptor set 更新路径正式支持 buffer 类 view
|
||||
4. D3D12 / Vulkan / OpenGL 三后端同时按正式接口设计
|
||||
5. 明确 backend capability 与错误回报,不允许 silent wrong image
|
||||
|
||||
### 测试
|
||||
|
||||
1. `tests/RHI/unit` 增加 buffer SRV/UAV 创建测试
|
||||
2. descriptor set buffer binding 测试
|
||||
3. 三后端分别验证 buffer view 不再被误当成 texture view
|
||||
|
||||
### 验收标准
|
||||
|
||||
1. 引擎能在不依赖 texture hack 的前提下,正式把 GPU buffer 绑定给 shader
|
||||
|
||||
---
|
||||
|
||||
## 阶段 2:打通 Unity 风格 shader buffer 语义
|
||||
|
||||
### 目标
|
||||
|
||||
让 shader authoring / parser / reflection / artifact / runtime binding 能正式表达 Unity/HLSL 风格 buffer 资源。
|
||||
|
||||
### 任务
|
||||
|
||||
1. 扩展 shader 反射模型以支持 structured/raw buffer 资源
|
||||
2. 明确 `StructuredBuffer<T>` 与 `ByteAddressBuffer` 的内部映射
|
||||
3. 扩展 runtime build、pass layout、descriptor metadata 对新资源类型的支持
|
||||
4. 明确 OpenGL 的 GLSL/SSBO 转译规则
|
||||
5. 严格禁止 volume 专属私有 shader 语法旁路
|
||||
|
||||
### 强约束
|
||||
|
||||
1. 不新增高层 `RawBuffer` 关键字
|
||||
2. 不新增 `MaterialPropertyType::StructuredBuffer / RawBuffer`
|
||||
3. 如果 `Properties` 中尝试声明 buffer,必须报错或拒绝导入
|
||||
|
||||
### 测试
|
||||
|
||||
1. `tests/Resources/Shader`
|
||||
2. `tests/Resources/Material`
|
||||
3. `tests/Rendering/unit` 中的 pass layout / metadata 测试
|
||||
4. authoring 约束测试:
|
||||
- Unity/HLSL 风格 buffer 声明可以被正确反射
|
||||
- 非法把 buffer 塞进 `Properties` 会被拒绝
|
||||
|
||||
### 验收标准
|
||||
|
||||
1. shader 资源描述层正式支持 buffer 资源
|
||||
2. 高层 authoring 语义仍保持 Unity 风格,不被底层分类污染
|
||||
|
||||
---
|
||||
|
||||
## 阶段 3:冻结 future C# buffer 绑定契约
|
||||
|
||||
### 目标
|
||||
|
||||
在 C# 模块尚未完全落地前,先把与本主线相关的 buffer 绑定语义冻结。
|
||||
|
||||
### 任务
|
||||
|
||||
1. 定义 native 侧可对应 future C# 的 buffer 绑定入口
|
||||
2. 约束 API 形状向 Unity 对齐:
|
||||
- `Material.SetBuffer`
|
||||
- `Shader.SetGlobalBuffer`
|
||||
- `CommandBuffer.SetGlobalBuffer`
|
||||
3. 明确 `VolumeField` 与 runtime buffer producer 的关系
|
||||
4. 保证未来托管层不会直接看到 RHI view 对象
|
||||
|
||||
### 测试
|
||||
|
||||
1. native 侧 binding metadata 测试
|
||||
2. material/runtime/global 三类 buffer 绑定路径测试
|
||||
|
||||
### 验收标准
|
||||
|
||||
1. 当前实现方向已经为 future C# 留好 Unity 风格接口落点
|
||||
|
||||
---
|
||||
|
||||
## 阶段 4:接入正式 VolumeField 资产链路
|
||||
|
||||
### 目标
|
||||
|
||||
让 `.nvdb` 成为项目里的正式资源,而不是 demo 私有文件。
|
||||
|
||||
### 任务
|
||||
|
||||
1. 新增 `ResourceType::VolumeField`
|
||||
2. 新增 `Resources::VolumeField`
|
||||
3. 新增 `VolumeFieldLoader`
|
||||
4. 新增 `NanoVDBVolumeImporter`
|
||||
5. `AssetDatabase` 识别 `.nvdb`
|
||||
6. 生成 `main.xcvol`
|
||||
7. 接入 reimport / dependency / cache / runtime load
|
||||
|
||||
### 测试
|
||||
|
||||
1. `tests/Resources/Volume`
|
||||
2. `tests/Core/Asset`
|
||||
3. `.nvdb` 改动后的 reimport 测试
|
||||
|
||||
### 验收标准
|
||||
|
||||
1. `.nvdb` 可以像 mesh / material / shader 一样进入正式资产体系
|
||||
|
||||
---
|
||||
|
||||
## 阶段 5:接入组件、提取与 frame data
|
||||
|
||||
### 目标
|
||||
|
||||
让场景系统知道“什么是可渲染的体积对象”。
|
||||
|
||||
### 任务
|
||||
|
||||
1. 新增 `VolumeRendererComponent`
|
||||
2. 组件持有:
|
||||
- `VolumeField`
|
||||
- `Material`
|
||||
- enable/disable
|
||||
- 局部参数 override
|
||||
- bounds / transform
|
||||
3. `RenderSceneExtractor` 增加体积对象提取
|
||||
4. 新增 `VisibleVolumeItem`
|
||||
5. `RenderSceneData.visibleVolumes` 接入
|
||||
|
||||
### 测试
|
||||
|
||||
1. 组件序列化 / 反序列化
|
||||
2. `VisibleVolumeItem` 提取测试
|
||||
3. frustum / bounds culling 与稳定顺序测试
|
||||
|
||||
### 验收标准
|
||||
|
||||
1. renderer 已经能从正式场景数据中拿到体积对象输入
|
||||
|
||||
---
|
||||
|
||||
## 阶段 6:BuiltinVolumetricPass 首次正式点亮
|
||||
|
||||
### 目标
|
||||
|
||||
在当前 renderer 正式链路中点亮第一版 `NanoVDB` 稀疏体积渲染。
|
||||
|
||||
### 任务
|
||||
|
||||
1. 新增 `BuiltinVolumetricPass`
|
||||
2. pass 输入包括:
|
||||
- camera
|
||||
- depth
|
||||
- scene color
|
||||
- light / shadow
|
||||
- volume buffer
|
||||
- material parameters
|
||||
3. D3D12 首次点亮
|
||||
4. 允许保留一条集成测试级别的 fullscreen debug path
|
||||
5. 正式运行时路径收口到 proxy box / unit cube volume renderer
|
||||
|
||||
### 强约束
|
||||
|
||||
1. fullscreen path 只能作为 bring-up / integration debug 路径
|
||||
2. 正式场景运行时不能停留在 fullscreen 方案
|
||||
3. 不得保留 `MVS` 风格直接文件加载或私有绑定路径
|
||||
|
||||
### 测试
|
||||
|
||||
1. `tests/Rendering/integration/volume_nanovdb_minimal`
|
||||
2. GT 对比
|
||||
3. 中间调试图输出:
|
||||
- `volume_mask_debug`
|
||||
- `depth_debug`
|
||||
- 必要时的 shadow debug
|
||||
|
||||
### 验收标准
|
||||
|
||||
1. 正式 renderer 已经能稳定渲染一个来自 `.nvdb` 的体积对象
|
||||
|
||||
---
|
||||
|
||||
## 阶段 7:深度、阴影、合成规则收口
|
||||
|
||||
### 目标
|
||||
|
||||
把“能出图”收口成“行为正确”。
|
||||
|
||||
### 任务
|
||||
|
||||
1. 深度遮挡规则确定
|
||||
2. scene color 合成规则确定
|
||||
3. 主方向光与阴影采样接入
|
||||
4. transform 与 bbox 变换一致
|
||||
5. 步长、阴影采样步数、质量参数稳定化
|
||||
|
||||
### 测试
|
||||
|
||||
1. `volume_nanovdb_occlusion`
|
||||
2. `volume_nanovdb_transform`
|
||||
3. 光照方向变化的回归测试
|
||||
|
||||
### 验收标准
|
||||
|
||||
1. 画面行为能解释、能回归、能维护,不再只是 demo level
|
||||
|
||||
---
|
||||
|
||||
## 阶段 8:多后端 rollout
|
||||
|
||||
### 目标
|
||||
|
||||
把已在 D3D12 验证的正式能力推进到 Vulkan 与 OpenGL。
|
||||
|
||||
### 任务
|
||||
|
||||
1. Vulkan storage buffer 路线点亮
|
||||
2. OpenGL SSBO 路线点亮
|
||||
3. 明确 capability 检测与 fallback / disable 行为
|
||||
4. 补齐 backend-specific shader compile / runtime binding 回归
|
||||
|
||||
### 测试
|
||||
|
||||
1. 各后端统一 GT 对比
|
||||
2. backend-specific shader compile 测试
|
||||
3. buffer binding 回归测试
|
||||
|
||||
### 验收标准
|
||||
|
||||
1. 不是只有 D3D12 demo 可用,而是正式进入多后端 renderer 能力集合
|
||||
|
||||
---
|
||||
|
||||
## 阶段 9:Editor 最小工作流接入
|
||||
|
||||
### 目标
|
||||
|
||||
让这项能力进入项目工作流,而不是只能靠测试程序看图。
|
||||
|
||||
### 任务
|
||||
|
||||
1. `ProjectPanel` 识别 `.nvdb`
|
||||
2. `Inspector` 显示 `VolumeField` metadata
|
||||
3. `VolumeRendererComponent` inspector 编辑
|
||||
4. scene view / game view 中体积对象可见
|
||||
5. 资源缺失、编译失败、后端不支持时给出明确状态
|
||||
|
||||
### 验收标准
|
||||
|
||||
1. 用户可以从资产、组件、场景三个层面完整使用这项能力
|
||||
|
||||
---
|
||||
|
||||
## 11. 测试体系规划
|
||||
|
||||
### 11.1 单元测试
|
||||
|
||||
需要新增或扩展:
|
||||
|
||||
1. `tests/RHI/unit`
|
||||
- buffer SRV/UAV 创建
|
||||
- descriptor set buffer binding
|
||||
2. `tests/Resources/Shader`
|
||||
- Unity/HLSL 风格 buffer 声明解析
|
||||
- 非法 `Properties` buffer 声明拒绝
|
||||
3. `tests/Resources/Material`
|
||||
- 材质参数 schema 与 runtime buffer 绑定边界
|
||||
4. `tests/Resources/Volume`
|
||||
- `.nvdb -> .xcvol`
|
||||
- metadata roundtrip
|
||||
- reimport
|
||||
5. `tests/Rendering/unit`
|
||||
- `VisibleVolumeItem` 提取
|
||||
- `BuiltinVolumetricPass` 输入校验
|
||||
|
||||
### 11.2 集成测试
|
||||
|
||||
建议新增:
|
||||
|
||||
1. `tests/Rendering/integration/volume_nanovdb_minimal`
|
||||
2. `tests/Rendering/integration/volume_nanovdb_occlusion`
|
||||
3. `tests/Rendering/integration/volume_nanovdb_transform`
|
||||
|
||||
GT 规则:
|
||||
|
||||
1. 使用单一 `GT.ppm`
|
||||
2. 各后端输出都对同一张 `GT.ppm` 做对比
|
||||
3. 必要时保留中间调试图,但不把调试图当正式输出
|
||||
|
||||
### 11.3 性能与稳定性验收
|
||||
|
||||
至少记录:
|
||||
|
||||
1. 单体积对象 frame time
|
||||
2. 多体积对象 frame time
|
||||
3. `.nvdb` 首次导入时长
|
||||
4. artifact 命中后的加载时长
|
||||
|
||||
---
|
||||
|
||||
## 12. 关键风险
|
||||
|
||||
### 12.1 最大风险不是算法,而是语义边界被做脏
|
||||
|
||||
如果一开始把 `StructuredBuffer / RawBuffer` 直接暴露成高层 authoring 概念,后面整个 shader/material/C# 体系都会被污染。
|
||||
|
||||
### 12.2 OpenGL 路线风险最高
|
||||
|
||||
原因:
|
||||
|
||||
1. `StructuredBuffer` 到 GLSL/SSBO 的映射最容易出边角问题
|
||||
2. `PNanoVDB` 这种大 include 在 GLSL 转译路径上更容易暴露兼容性问题
|
||||
|
||||
### 12.3 fullscreen debug path 容易反客为主
|
||||
|
||||
它只能用于 bring-up 和集成测试,不能变成正式场景架构。
|
||||
|
||||
### 12.4 如果当前 shader 体系还没正式支持 Unity 风格 buffer 语义,这会成为主阻塞项
|
||||
|
||||
这不是额外问题,而是本主线的前置依赖。
|
||||
|
||||
---
|
||||
|
||||
## 13. 本阶段收口标准
|
||||
|
||||
当下面条件同时满足时,可以认为“稀疏体积渲染第一阶段正式完成”:
|
||||
|
||||
1. `.nvdb` 已成为正式项目资源,可导入、可 reimport、可 artifact 化。
|
||||
2. 引擎 shader / runtime binding / RHI 已正式支持 Unity 风格 buffer 资源链路。
|
||||
3. 高层 authoring 没有引入任何体积专用自定义 buffer 语法。
|
||||
4. `VolumeRendererComponent` 能把 `VolumeField` 放进场景。
|
||||
5. `BuiltinVolumetricPass` 已作为正式 scene pass 存在。
|
||||
6. D3D12 至少已经在正式链路中点亮。
|
||||
7. 集成测试能稳定出图并做 GT 对比。
|
||||
8. future C# 的 `SetBuffer` 风格接口已经有明确落点,不会和当前底层设计冲突。
|
||||
9. 代码中不再依赖 `MVS/VolumeRenderer` 那套 D3D12 私有 demo 实现。
|
||||
|
||||
---
|
||||
|
||||
## 14. 一句话结论
|
||||
|
||||
这条主线的正确做法不是“先做出体积渲染再说”,而是“从第一天开始就按 Unity 风格的 shader/C# 语义设计 buffer 路径,把 `NanoVDB` 作为正式稀疏体积能力接入 renderer”,这样后面无论是 C# 层还是 SRP,都不会被今天的底层实现反噬。
|
||||
@@ -1,198 +0,0 @@
|
||||
# Renderer 剩余收口与体积渲染多后端正式化计划
|
||||
|
||||
日期:2026-04-10
|
||||
|
||||
## 1. 文档定位
|
||||
|
||||
旧的 `Renderer当前阶段正式收口计划_2026-04-09` 与 `NanoVDB稀疏体积渲染后续正式化计划_2026-04-09`
|
||||
已归档。
|
||||
|
||||
当前这份计划只覆盖 Rendering 主线里仍然没有真正收口的剩余问题,不再重复记录已经完成的阶段成果。
|
||||
|
||||
本轮目标有且只有四类:
|
||||
|
||||
1. 把 NanoVDB 体积渲染从 D3D12 单后端正式能力推进到 Vulkan / OpenGL 正式能力。
|
||||
2. 把 `FinalColor` 与 final-output 的契约收死,消除“接口已存在但运行时半接线”的状态。
|
||||
3. 把 `ObjectId` 从“低 32 位截断可用方案”推进到清晰稳定的正式渲染 ID 契约。
|
||||
4. 把阴影规划与前向管线剩余的硬编码策略、monolith 结构继续拆干净。
|
||||
|
||||
---
|
||||
|
||||
## 2. 当前状态判断
|
||||
|
||||
当前 Rendering 已经具备稳定运行基础,但还不能称为“完全收口”,核心原因是:
|
||||
|
||||
1. `volume_scene / volume_occlusion_scene / volume_transform_scene` 目前仍只在 D3D12 上做了正式实例化与 GT 验证。
|
||||
2. `FinalColorSettings` 当前真正落地的是 pipeline defaults + camera overrides;此前残留的 volume override 分支只是头文件级 API,并未进入运行时。
|
||||
3. `ObjectId` 仍基于 runtime object id 低 32 位编码,不是长期正式方案。
|
||||
4. `SceneRenderRequestPlanner` 里仍保留方向光阴影尺寸与 focus 策略的硬编码。
|
||||
5. `BuiltinForwardPipeline` 功能上可用,但资源布局、PSO、descriptor、skybox、draw path 仍聚集在同一条大实现链里。
|
||||
|
||||
这五点不继续收掉,后面不管做 editor 深化还是做更高层 renderer / SRP,都会反复返工。
|
||||
|
||||
---
|
||||
|
||||
## 3. 本轮明确不做什么
|
||||
|
||||
1. 不引入 `RenderGraph`。
|
||||
2. 不提前启动 `SRP` 正式实现。
|
||||
3. 不改写现有 Shader/Material 语法主线。
|
||||
4. 不扩展新的大特性,例如 deferred、reflection probe、完整 post-processing stack。
|
||||
5. 不为了追求“结构完美”重写整个 builtin renderer。
|
||||
|
||||
本轮只做剩余收口,不做新能力扩张。
|
||||
|
||||
---
|
||||
|
||||
## 4. 执行阶段
|
||||
|
||||
### Phase 1:NanoVDB Vulkan 正式点亮
|
||||
|
||||
目标:
|
||||
|
||||
把当前 D3D12 已验证的体积渲染链路推进到 Vulkan 正式支持。
|
||||
|
||||
任务:
|
||||
|
||||
1. 审查 `BuiltinVolumetricPass` 在 Vulkan 下的 StructuredBuffer / storage buffer 绑定路径。
|
||||
2. 核对体积 shader 在 Vulkan 下的资源布局、反射、descriptor set 绑定。
|
||||
3. 让 `volume_scene`、`volume_occlusion_scene`、`volume_transform_scene` 在 Vulkan 下正式实例化并通过。
|
||||
4. 固化 Vulkan 失败日志与 capability 行为,禁止 silent fallback。
|
||||
|
||||
验收标准:
|
||||
|
||||
1. Vulkan 路线能稳定出图。
|
||||
2. 三个体积集成测试全部通过。
|
||||
3. 失败时日志能直接定位到 capability / shader / 绑定问题。
|
||||
|
||||
### Phase 2:NanoVDB OpenGL 正式点亮
|
||||
|
||||
目标:
|
||||
|
||||
把体积渲染推进到 OpenGL 正式支持,并确认 SSBO/GLSL 转译边界。
|
||||
|
||||
任务:
|
||||
|
||||
1. 审查 StructuredBuffer -> OpenGL SSBO 的运行时绑定链。
|
||||
2. 解决体积 shader 在 OpenGL 下的 layout / binding / 对齐问题。
|
||||
3. 让三组体积集成测试在 OpenGL 下正式实例化并通过。
|
||||
4. 明确 OpenGL 不支持场景下的禁用与报错策略。
|
||||
|
||||
验收标准:
|
||||
|
||||
1. OpenGL 路线能稳定出图。
|
||||
2. D3D12 / Vulkan / OpenGL 三后端都具备正式验证。
|
||||
3. 不再存在“体积渲染只是 D3D12 正式能力”的状态。
|
||||
|
||||
### Phase 3:FinalColor 契约彻底收死
|
||||
|
||||
目标:
|
||||
|
||||
让 `FinalColor` 的 API 契约、运行时行为、测试覆盖完全一致。
|
||||
|
||||
任务:
|
||||
|
||||
1. 审查 `FinalColorSettings`、camera overrides、final-output 的设计边界。
|
||||
2. 将未落地的 volume override 分支从现阶段 API 中正式收掉,不再保留半接线接口。
|
||||
3. 补齐对应单测与场景回归测试。
|
||||
4. 明确 post-process / final-output 的职责关系文档。
|
||||
|
||||
验收标准:
|
||||
|
||||
1. 不再存在“接口支持但执行层没接”的不自洽状态。
|
||||
2. `FinalColor` 行为能用测试稳定验证。
|
||||
|
||||
### Phase 4:ObjectId 正式渲染 ID 契约
|
||||
|
||||
目标:
|
||||
|
||||
把 picking / outline / selection 依赖的 ID 方案从阶段实现推进到正式契约。
|
||||
|
||||
任务:
|
||||
|
||||
1. 明确长期方案是稳定 32-bit render id,还是 runtime mapping table。
|
||||
2. 将 `ObjectIdCodec` 与 `GameObject::ID` 的关系从“低 32 位截断”调整为正式约束。
|
||||
3. 让 picking / outline / editor selection 共用同一份正式契约。
|
||||
4. 补齐边界测试与错误防护。
|
||||
|
||||
验收标准:
|
||||
|
||||
1. `ObjectId` 不再依赖模糊约定。
|
||||
2. 相关 editor 能力建立在稳定渲染 ID 契约上。
|
||||
|
||||
### Phase 5:阴影规划与前向管线剩余结构收口
|
||||
|
||||
目标:
|
||||
|
||||
继续把 planner / pipeline 里剩余的硬编码和 monolith 结构压缩到合理边界。
|
||||
|
||||
任务:
|
||||
|
||||
1. 把方向光阴影尺寸、focus/depth/padding 策略抽成正式配置点。
|
||||
2. 收紧 `SceneRenderRequestPlanner` 的职责,避免继续膨胀。
|
||||
3. 拆分 `BuiltinForwardPipeline` 剩余的大块职责:
|
||||
- pipeline state resolve
|
||||
- descriptor/layout cache
|
||||
- skybox path
|
||||
- draw submission
|
||||
4. 保持功能不回退的前提下优化代码结构与目录边界。
|
||||
|
||||
验收标准:
|
||||
|
||||
1. 阴影规划不再写死关键策略参数。
|
||||
2. `BuiltinForwardPipeline` 不再继续向单体类膨胀。
|
||||
|
||||
### Phase 6:测试、文档、归档
|
||||
|
||||
目标:
|
||||
|
||||
让本轮真正可交接、可回归、可归档。
|
||||
|
||||
任务:
|
||||
|
||||
1. 复跑关键 Rendering unit / integration。
|
||||
2. 输出阶段总结。
|
||||
3. 将本轮完成计划归档到 `docs/used`。
|
||||
|
||||
验收标准:
|
||||
|
||||
1. 关键测试全绿。
|
||||
2. 活跃 plan 只保留真正未完成事项。
|
||||
|
||||
---
|
||||
|
||||
## 5. 执行顺序
|
||||
|
||||
严格按下面顺序推进:
|
||||
|
||||
1. `Phase 1`
|
||||
2. `Phase 2`
|
||||
3. `Phase 3`
|
||||
4. `Phase 4`
|
||||
5. `Phase 5`
|
||||
6. `Phase 6`
|
||||
|
||||
原因:
|
||||
|
||||
1. 体积渲染多后端 rollout 还没完成,当前还不能宣称 renderer 全面正式支持。
|
||||
2. `FinalColor` 与 `ObjectId` 都是契约问题,必须在继续推进更高层系统前收死。
|
||||
3. planner / pipeline 结构整理必须建立在前面能力边界稳定之后。
|
||||
|
||||
---
|
||||
|
||||
## 6. 本轮完成标志
|
||||
|
||||
当下面条件同时成立时,本轮才算真正完成:
|
||||
|
||||
1. 三个体积场景已在 D3D12 / Vulkan / OpenGL 三后端正式验证。
|
||||
2. `FinalColor` 契约与执行层完全一致。
|
||||
3. `ObjectId` 已切换到正式渲染 ID 契约。
|
||||
4. 阴影规划关键策略点已正式化。
|
||||
5. `BuiltinForwardPipeline` 剩余的结构性膨胀得到明显收口。
|
||||
6. 文档与测试全部同步完成。
|
||||
|
||||
---
|
||||
|
||||
## 7. 一句话结论
|
||||
|
||||
当前 Rendering 不是“功能没做完”,而是“剩余收口项已经缩小到多后端体积渲染、运行时契约、自身结构整理三类问题”;本轮做完之后,才可以严肃地说它进入了真正可长期维护的正式状态。
|
||||
@@ -1,291 +0,0 @@
|
||||
# Scene 选中描边彻底修复计划
|
||||
|
||||
日期:2026-04-08
|
||||
|
||||
## 1. 文档定位
|
||||
|
||||
本文档只解决一个问题:
|
||||
|
||||
- `Scene` 面板里,选中对象的描边效果需要达到稳定、可扩展、接近商业编辑器的质量。
|
||||
|
||||
本文档不讨论:
|
||||
|
||||
- 层级面板右键菜单
|
||||
- 灯光 gizmo 图标
|
||||
- 运行时高亮
|
||||
- 游戏内选中反馈
|
||||
|
||||
## 2. 当前问题结论
|
||||
|
||||
当前实现不是“可见外轮廓提取”,而是“基于 object-id 的屏幕空间膨胀”。
|
||||
|
||||
现状链路如下:
|
||||
|
||||
1. 主场景正常渲染。
|
||||
2. `BuiltinObjectIdPass` 把屏幕上最前面的物体 id 写入 `objectIdTexture`。
|
||||
3. `BuiltinObjectIdOutlinePass` 在全屏 shader 中检查当前像素周围是否存在“选中对象的 object-id”。
|
||||
4. 只要邻域中存在选中 object-id,当前像素就会被涂成 outline。
|
||||
|
||||
这个算法的根本缺陷是:
|
||||
|
||||
- 它只知道“邻域里有没有选中对象”。
|
||||
- 它不知道“当前像素前面是不是别的物体挡住了选中对象”。
|
||||
- 它也不知道“这条边是可见外轮廓,还是选中大平面与前景遮挡物之间的内部接触边界”。
|
||||
|
||||
所以当选中一个大平面时,平面上方的 cube、sphere、capsule 这些前景物体边缘也会被错误描边。
|
||||
|
||||
这不是数据污染,也不是拾取错误,而是算法层面的必然结果。
|
||||
|
||||
## 3. 根因
|
||||
|
||||
根因可以归纳为两条:
|
||||
|
||||
### 3.1 描边输入选错了
|
||||
|
||||
当前描边 pass 直接消费 `objectIdTexture`。
|
||||
|
||||
但 `objectIdTexture` 的职责本质上是:
|
||||
|
||||
- 告诉编辑器“当前屏幕像素最前面的对象是谁”,用于 picking。
|
||||
|
||||
它不适合作为“选中描边”的唯一依据,因为它不包含:
|
||||
|
||||
- 选中对象的可见 mask
|
||||
- 选中对象自己的深度
|
||||
- 当前场景深度与选中对象深度之间的遮挡关系
|
||||
|
||||
### 3.2 描边算法缺少深度裁决
|
||||
|
||||
当前 outline shader 的判断规则是:
|
||||
|
||||
- 当前像素不是选中对象
|
||||
- 但周围像素里存在选中对象
|
||||
|
||||
满足这两个条件就画 outline。
|
||||
|
||||
这套规则没有使用场景深度,也没有使用选中对象的可见性信息,因此无法区分:
|
||||
|
||||
- 真正应该画的外轮廓
|
||||
- 不应该画的前景遮挡边界
|
||||
|
||||
## 4. 修复目标
|
||||
|
||||
本次彻底修复后的目标是:
|
||||
|
||||
1. 选中大平面时,前景物体边缘不再被错误染成 outline。
|
||||
2. 选中普通 mesh 时,outline 仍然能稳定出现在可见外轮廓处。
|
||||
3. 被遮挡区域不画“伪外轮廓”。
|
||||
4. picking 与 selection outline 两套能力彻底解耦。
|
||||
5. 后续如果要做 Unity 风格的 x-ray 选中、被遮挡高亮、hover outline,可以在当前结构上继续扩展,而不是推倒重来。
|
||||
|
||||
## 5. 非目标
|
||||
|
||||
这次修复不做以下内容:
|
||||
|
||||
1. 不做运行时游戏内高亮系统。
|
||||
2. 不做完整的“透视遮挡时仍显示淡色轮廓”的 x-ray 选中风格。
|
||||
3. 不借这次机会重写整个 editor render pipeline。
|
||||
4. 不继续在现有 `object-id-outline.shader` 上做只针对单个 case 的临时补丁。
|
||||
|
||||
## 6. 总体方案
|
||||
|
||||
正确方案是把“拾取”和“描边”拆开,改成:
|
||||
|
||||
- `object id for picking`
|
||||
- `visible selection mask for outline`
|
||||
- `depth-aware outline composite`
|
||||
|
||||
### 6.1 picking 继续走 object-id
|
||||
|
||||
现有 `objectIdTexture` 继续只用于:
|
||||
|
||||
- 场景点击选中
|
||||
- object-id readback
|
||||
|
||||
它不再直接决定 outline 的视觉结果。
|
||||
|
||||
### 6.2 新增 selection mask pass
|
||||
|
||||
新增一张仅服务于 outline 的 `selectionMaskTexture`。
|
||||
|
||||
它的规则是:
|
||||
|
||||
1. 只渲染当前选中的对象。
|
||||
2. 渲染时绑定主场景已有的深度缓冲。
|
||||
3. 依赖深度测试,只把“真正可见的 selected fragment”写进 `selectionMaskTexture`。
|
||||
|
||||
这样得到的不是 picking id 图,而是“选中对象可见区域 mask”。
|
||||
|
||||
### 6.3 scene viewport 暴露深度 SRV
|
||||
|
||||
为了做真正的 depth-aware composite,`Scene` viewport 需要同时持有:
|
||||
|
||||
1. `depthView`
|
||||
2. `depthShaderView`
|
||||
|
||||
也就是说,Scene viewport 的深度纹理既要能作为 DSV 使用,也要能在全屏 composite shader 中作为 SRV 读取。
|
||||
|
||||
底层 RHI 已经具备这条能力,阴影缓存已有相同用法,因此这不是架构冒险,而是 viewport 资源层尚未接线。
|
||||
|
||||
### 6.4 重写 outline composite pass
|
||||
|
||||
新的 outline shader 不再直接从 `objectIdTexture` 做邻域膨胀,而是读取:
|
||||
|
||||
1. `selectionMaskTexture`
|
||||
2. `sceneDepthTexture`
|
||||
|
||||
必要时预留:
|
||||
|
||||
3. `selectionDepthTexture`
|
||||
|
||||
新的裁决规则应为:
|
||||
|
||||
1. 中心像素若属于 selected mask,本像素不直接着色。
|
||||
2. 若邻域存在 selected mask,则该像素可能位于 selected 的边界附近。
|
||||
3. 只有在深度关系证明“当前像素不是更近的前景遮挡物”时,才允许输出 outline。
|
||||
|
||||
这一步的本质是:
|
||||
|
||||
- 只画可见 selected 外轮廓。
|
||||
- 不把前景遮挡物边界误判为 selected outline。
|
||||
|
||||
## 7. 具体实施步骤
|
||||
|
||||
### Phase 1:补齐 viewport 资源
|
||||
|
||||
目标:
|
||||
|
||||
- Scene viewport 持有可采样深度。
|
||||
- Scene viewport 持有单独的 selection mask render target。
|
||||
|
||||
需要修改:
|
||||
|
||||
1. `editor/src/Viewport/ViewportHostRenderTargets.h`
|
||||
2. 相关 viewport resource reuse / destroy / create 流程
|
||||
|
||||
新增资源建议:
|
||||
|
||||
1. `depthShaderView`
|
||||
2. `selectionMaskTexture`
|
||||
3. `selectionMaskView`
|
||||
4. `selectionMaskShaderView`
|
||||
5. `selectionMaskState`
|
||||
|
||||
验收标准:
|
||||
|
||||
- Scene viewport 创建/销毁/复用逻辑能完整覆盖新资源。
|
||||
- 编译通过。
|
||||
- 不影响现有 Scene 视图展示。
|
||||
|
||||
### Phase 2:新增 selection mask pass
|
||||
|
||||
目标:
|
||||
|
||||
- 只把当前选中对象的可见区域写入 mask。
|
||||
|
||||
实现建议:
|
||||
|
||||
1. 新建 editor 专用 `SelectionMaskPass`。
|
||||
2. 输入为选中对象 id 列表。
|
||||
3. 输出到 `selectionMaskTexture`。
|
||||
4. 渲染时绑定主场景同一套深度。
|
||||
|
||||
实现关键点:
|
||||
|
||||
1. 这个 pass 的语义不是 picking。
|
||||
2. 它只关心“选中对象哪些像素当前可见”。
|
||||
3. 它不需要写 object id,只需要写统一 mask 值或选中组 id。
|
||||
|
||||
验收标准:
|
||||
|
||||
- 选中对象时可以输出稳定的可见 mask。
|
||||
- 未选中时 pass 可被跳过。
|
||||
|
||||
### Phase 3:重写 outline composite
|
||||
|
||||
目标:
|
||||
|
||||
- outline 只出现在选中对象的可见外轮廓。
|
||||
|
||||
实现建议:
|
||||
|
||||
1. 保留全屏 pass 形式。
|
||||
2. 输入从 `objectIdTexture` 改为 `selectionMaskTexture + depthShaderView`。
|
||||
3. 邻域检查继续保留,但结果必须经过深度裁决。
|
||||
|
||||
最低正确规则:
|
||||
|
||||
1. 中心像素若是别的前景几何,且深度显著近于 selected 边界,不画 outline。
|
||||
2. 中心像素若是背景或更远表面,可以画 outline。
|
||||
|
||||
验收标准:
|
||||
|
||||
1. 选中大平面时,前景 cube/sphere/capsule 边缘不再被错误描边。
|
||||
2. 选中单个 mesh 时,外轮廓仍连续稳定。
|
||||
|
||||
### Phase 4:移除旧耦合
|
||||
|
||||
目标:
|
||||
|
||||
- 彻底消除 “outline 直接依赖 object-id picking 纹理” 这一旧逻辑。
|
||||
|
||||
需要完成:
|
||||
|
||||
1. 旧 `object-id-outline.shader` 逻辑下线或重命名。
|
||||
2. `SceneViewportSelectionOutlinePass` 的输入契约改为新资源。
|
||||
3. 保证 `objectIdTexture` 仅服务于 picking。
|
||||
|
||||
验收标准:
|
||||
|
||||
- 代码层不再存在“outline 直接读 object id 邻域并膨胀”的核心路径。
|
||||
|
||||
## 8. 测试计划
|
||||
|
||||
至少补以下回归验证:
|
||||
|
||||
### 8.1 功能场景
|
||||
|
||||
1. 选中大平面,前面摆放 cube / sphere / capsule:
|
||||
- 前景物体边缘不能被错误描边。
|
||||
2. 选中单个 cube,背景是地面:
|
||||
- outline 能稳定显示在 cube 可见外轮廓上。
|
||||
3. 选中被部分遮挡的物体:
|
||||
- 只允许可见区域出现 outline。
|
||||
4. 多选相邻物体:
|
||||
- outline 不串边,不污染第三方前景物体。
|
||||
|
||||
### 8.2 回归类型
|
||||
|
||||
1. shader 资源加载测试
|
||||
2. render plan 接线测试
|
||||
3. pass 构建与资源存在性测试
|
||||
4. 视口级人工验证截图
|
||||
|
||||
## 9. 风险与边界
|
||||
|
||||
### 9.1 风险
|
||||
|
||||
1. 仅使用 `sceneDepth + selectionMask` 可能仍有少数边界 case 需要 `selectionDepth` 才能更稳。
|
||||
2. Scene viewport 新增 render target 后,资源管理和状态切换复杂度会上升。
|
||||
3. 若后续要兼容 Vulkan / OpenGL,需要同步确认深度 SRV 路径与资源状态语义。
|
||||
|
||||
### 9.2 边界
|
||||
|
||||
本计划的第一目标不是“做最炫的 outline”,而是:
|
||||
|
||||
- 先把语义做对
|
||||
- 再把视觉做稳
|
||||
|
||||
只要还在复用 picking 的 `objectIdTexture` 直接做 outline,问题就不可能彻底解决。
|
||||
|
||||
## 10. 最终结论
|
||||
|
||||
这次 bug 不能靠继续修补当前 `object-id-outline.shader` 解决。
|
||||
|
||||
彻底方案必须满足三点:
|
||||
|
||||
1. picking 与 outline 解耦
|
||||
2. 选中对象生成独立可见 mask
|
||||
3. outline composite 引入深度裁决
|
||||
|
||||
只有这样,Scene 视图选中描边才会从“屏幕空间膨胀特效”升级为“真正可用的编辑器轮廓系统”。
|
||||
@@ -1,287 +0,0 @@
|
||||
# Shader 预编译缓存与 D3D12 字节码正式化计划
|
||||
|
||||
文档日期:2026-04-10
|
||||
|
||||
适用范围:`XCEngine` 当前 `Resources / Asset / Rendering / RHI / Editor` 主线,目标问题为编辑器与运行时虽然已经具备 shader artifact 与 variant 系统,但 D3D12 路径仍普遍在运行时现场 `D3DCompile(...)`,缺少真正可复用的编译字节码缓存链路。
|
||||
|
||||
关联归档:
|
||||
[NanoVDB体积云加载阻塞与Runtime上传修复计划_完成归档_2026-04-10.md](/D:/Xuanchi/Main/XCEngine/docs/used/NanoVDB体积云加载阻塞与Runtime上传修复计划_完成归档_2026-04-10.md)
|
||||
|
||||
---
|
||||
|
||||
## 1. 当前结论
|
||||
|
||||
本轮 `NanoVDB` 卡顿排查已经把“资源读入/上传”与“shader 编译”分开钉死:
|
||||
|
||||
1. `main.xcvol` artifact 载入已经压到亚秒级。
|
||||
2. `cloud.nvdb` 对应 payload 的 CPU 读入和 GPU 上传不再是主瓶颈。
|
||||
3. 当前剩余大头是 D3D12 图形管线创建时的运行时 shader 编译。
|
||||
|
||||
最新 editor 实测:
|
||||
|
||||
- `VolumeFieldLoader Artifact total_ms = 674`
|
||||
- `AsyncVolumeLoadCompleted elapsed_ms = 909`
|
||||
- `UploadVolumeField total_ms = 736`
|
||||
- `D3D12 shader compile MainPS total_ms = 6205`
|
||||
- `SceneReady elapsed_ms = 8500`
|
||||
|
||||
这说明当前主线问题已经从“体积资源路径错误”切换成“D3D12 shader 运行时现场编译没有被缓存机制覆盖”。
|
||||
|
||||
---
|
||||
|
||||
## 2. 根本问题
|
||||
|
||||
当前工程里虽然有 shader artifact,也预留了 `compiledBinary` 字段,但整条链路没有真正闭合。
|
||||
|
||||
### 2.1 已有但未闭环的部分
|
||||
|
||||
1. `ShaderStageVariant` 有 `compiledBinary` 字段。
|
||||
2. shader artifact 文件格式会序列化/反序列化 `compiledBinary`。
|
||||
3. shader runtime build 也会统计 `compiledBinary` 的内存占用。
|
||||
|
||||
### 2.2 真正缺失的部分
|
||||
|
||||
1. 没有任何正式步骤把 D3D12 编译结果写入 `variant.compiledBinary`。
|
||||
2. 没有任何 D3D12 运行时路径优先消费 `compiledBinary`。
|
||||
3. `D3D12Device::CreatePipelineState(...)` 仍然直接对 `ShaderCompileDesc` 走 `D3DCompile(...)`。
|
||||
4. shader artifact 现在缓存的是源码展开结果和变体描述,不是可直接用于 D3D12 的 DXBC/DXIL。
|
||||
|
||||
因此当前的真实状态不是“缓存偶尔失效”,而是:
|
||||
|
||||
`D3D12 shader 预编译缓存机制整体没有真正落地。`
|
||||
|
||||
`NanoVDB` 只是把这个系统性缺口最先炸出来,因为它的 `MainPS` 足够重。
|
||||
|
||||
---
|
||||
|
||||
## 3. 目标
|
||||
|
||||
## 3.1 一级目标
|
||||
|
||||
让 D3D12 路径对同一 shader variant 不再反复运行时现场编译。
|
||||
|
||||
要求:
|
||||
|
||||
1. 首次编译后能够落盘保存 D3D12 可复用字节码。
|
||||
2. 再次打开 editor / scene / project 时优先命中缓存。
|
||||
3. `CreatePipelineState(...)` 在缓存命中时不再进入 `D3DCompile(...)`。
|
||||
|
||||
## 3.2 二级目标
|
||||
|
||||
把这套机制做成通用能力,而不是只给 `volumetric.shader` 打补丁。
|
||||
|
||||
要求:
|
||||
|
||||
1. 面向所有 D3D12 shader variant。
|
||||
2. 与现有 shader artifact/variant 系统兼容。
|
||||
3. 缓存失效规则明确,可随源码、profile、macro、backend、entry point 正确失效。
|
||||
|
||||
## 3.3 三级目标
|
||||
|
||||
为后续 Vulkan / OpenGL / DXC 路径保留统一设计空间。
|
||||
|
||||
本轮不要求一次做完多后端,但数据模型和接口命名不能把后续扩展堵死。
|
||||
|
||||
---
|
||||
|
||||
## 4. 非目标
|
||||
|
||||
本轮不做以下内容:
|
||||
|
||||
1. 不重写整个 shader authoring 系统。
|
||||
2. 不直接切 DXC 全量替换 FXC。
|
||||
3. 不先做跨后端统一离线编译工具链。
|
||||
4. 不先做完整 PSO cache blob 体系。
|
||||
5. 不先解决所有 shader 首次编译耗时,只先解决“重复现场编译”这个系统性缺口。
|
||||
|
||||
---
|
||||
|
||||
## 5. 正式实施方向
|
||||
|
||||
## 5.1 方向一:明确 D3D12 预编译产物的数据模型
|
||||
|
||||
要先统一“什么叫缓存命中”。
|
||||
|
||||
建议把 D3D12 预编译缓存键固定为以下信息的稳定组合:
|
||||
|
||||
1. shader 资源路径
|
||||
2. pass name
|
||||
3. stage
|
||||
4. backend
|
||||
5. source language
|
||||
6. entry point
|
||||
7. profile
|
||||
8. macro 集合
|
||||
9. 变体关键字集合
|
||||
10. 源码内容哈希
|
||||
|
||||
产物:
|
||||
|
||||
1. 对应 stage 的已编译 D3D12 字节码
|
||||
2. 可选的编译日志/调试信息版本标识
|
||||
3. 可选的 shader reflection 派生数据版本号
|
||||
|
||||
这一步的目的不是立即提速,而是先避免后面做出“缓存看起来有,实际上命不中或误命中”的半成品。
|
||||
|
||||
## 5.2 方向二:把 `compiledBinary` 从占位字段变成真实产物
|
||||
|
||||
当前 `compiledBinary` 只是格式字段,不是真正能力。
|
||||
|
||||
本阶段要做:
|
||||
|
||||
1. D3D12 编译成功后把字节码写回 `ShaderStageVariant::compiledBinary`
|
||||
2. shader artifact 写出时携带该字节码
|
||||
3. 重新载入 shader artifact 时恢复该字节码
|
||||
|
||||
要求:
|
||||
|
||||
1. 同一 variant 的 artifact 可以直接携带 D3D12 字节码
|
||||
2. 非 D3D12 后端不会被这套数据污染
|
||||
3. 缓存不存在时依然允许 fallback 到现场编译
|
||||
|
||||
## 5.3 方向三:D3D12 runtime 优先消费已编译字节码
|
||||
|
||||
这是运行时闭环的关键。
|
||||
|
||||
需要调整的不是 asset 层,而是 D3D12 runtime 创建 shader / pipeline 的入口:
|
||||
|
||||
1. `CreateShader(...)`
|
||||
2. `CreatePipelineState(...)`
|
||||
3. 任何内部 `CompileD3D12Shader(...)` 的调用链
|
||||
|
||||
优先级顺序应为:
|
||||
|
||||
1. 命中 `compiledBinary`,直接创建 shader bytecode
|
||||
2. 未命中时才走 `D3DCompile(...)`
|
||||
3. 首次现场编译成功后可回写缓存
|
||||
|
||||
验收标准:
|
||||
|
||||
1. 对同一项目二次启动时,`MainPS` 不再重新编译
|
||||
2. 日志里能明确区分 `cache_hit` / `cache_miss` / `runtime_compile`
|
||||
|
||||
## 5.4 方向四:把缓存失效规则正式化
|
||||
|
||||
没有失效规则,缓存就会变成隐患。
|
||||
|
||||
必须纳入失效判定的至少包括:
|
||||
|
||||
1. shader 源文件内容变化
|
||||
2. `#include` 展开结果变化
|
||||
3. entry point 变化
|
||||
4. profile 变化
|
||||
5. macro 变化
|
||||
6. backend 变化
|
||||
7. variant keyword 变化
|
||||
8. shader artifact schema version 变化
|
||||
|
||||
这一阶段必须输出一套明确规则,避免后面出现“改了 shader 却继续吃旧字节码”的错误。
|
||||
|
||||
## 5.5 方向五:引入可验证的日志与测试
|
||||
|
||||
这次 `NanoVDB` 能钉死问题,靠的是可计时日志,不是猜。
|
||||
|
||||
shader 预编译缓存正式化之后,也必须保留最小必要日志:
|
||||
|
||||
1. cache key
|
||||
2. cache hit/miss
|
||||
3. runtime compile elapsed
|
||||
4. binary load elapsed
|
||||
5. fallback reason
|
||||
|
||||
测试层至少要覆盖:
|
||||
|
||||
1. 首次冷启动:miss + compile + write
|
||||
2. 第二次热启动:hit + no compile
|
||||
3. 改 shader 源码后:缓存失效 + 重新编译
|
||||
4. 切换 profile / macro / keyword 后:缓存失效
|
||||
|
||||
---
|
||||
|
||||
## 6. 分阶段执行计划
|
||||
|
||||
## Phase 0:基线与接口盘点
|
||||
|
||||
任务:
|
||||
|
||||
1. 梳理 `ShaderStageVariant::compiledBinary` 的现状用途
|
||||
2. 盘点 D3D12 shader / pipeline 创建的全部编译入口
|
||||
3. 定义统一缓存键和版本策略
|
||||
4. 确认 artifact 与 runtime 的责任边界
|
||||
|
||||
交付:
|
||||
|
||||
1. 一份固定缓存键规则
|
||||
2. 一份 D3D12 编译调用链清单
|
||||
|
||||
## Phase 1:让 artifact 真正带上 D3D12 编译产物
|
||||
|
||||
任务:
|
||||
|
||||
1. 为 D3D12 variant 生成并保存编译字节码
|
||||
2. artifact 写入与读取完整覆盖 `compiledBinary`
|
||||
3. 为编译产物附加必要版本信息
|
||||
|
||||
交付:
|
||||
|
||||
1. D3D12 shader artifact 中存在真实二进制 payload
|
||||
2. 可验证 artifact 前后字节码一致
|
||||
|
||||
## Phase 2:让 D3D12 runtime 优先吃缓存
|
||||
|
||||
任务:
|
||||
|
||||
1. 调整 `CompileD3D12Shader(...)` 调用链
|
||||
2. 优先从 `compiledBinary` 构造 shader bytecode
|
||||
3. 未命中时 fallback 到 `D3DCompile(...)`
|
||||
4. 命中/失效/回写日志打通
|
||||
|
||||
交付:
|
||||
|
||||
1. 二次打开 editor 时不再重新编译体积云 `MainPS`
|
||||
2. 其他 D3D12 shader 也具备相同缓存能力
|
||||
|
||||
## Phase 3:引入自动验证与冷启动对比
|
||||
|
||||
任务:
|
||||
|
||||
1. 加入 shader cache 命中测试
|
||||
2. 加入变体失效测试
|
||||
3. 对 editor 冷启动做两轮连续对比
|
||||
|
||||
目标数字:
|
||||
|
||||
1. 二次启动 `MainPS compile total_ms` 应接近 `0`
|
||||
2. `SceneReady` 应继续从当前 `~8.5s` 下探
|
||||
|
||||
---
|
||||
|
||||
## 7. 当前建议的提交边界
|
||||
|
||||
在新计划开始实施前,建议把已经完成的修复与后续 shader cache 工作拆成两批提交:
|
||||
|
||||
### 提交一:NanoVDB 路径修复与编译热点降压
|
||||
|
||||
应包含:
|
||||
|
||||
1. volume artifact / payload 路径修复
|
||||
2. `ResizeUninitialized` 与大 payload 默认构造开销修复
|
||||
3. `volumetric.shader` 去除高风险 `[unroll]`
|
||||
4. HLSL profile 对齐到 `5_1`
|
||||
5. 当前保留的计时日志
|
||||
|
||||
### 提交二:Shader 预编译缓存正式化
|
||||
|
||||
暂不在本提交里混入,避免把“已验证修复”和“下一阶段系统改造”搅在一起。
|
||||
|
||||
---
|
||||
|
||||
## 8. 验收标准
|
||||
|
||||
本计划完成时,至少满足:
|
||||
|
||||
1. D3D12 对同一 shader variant 的二次启动不再现场编译。
|
||||
2. 日志能明确证明缓存命中。
|
||||
3. 修改 shader 后缓存会正确失效。
|
||||
4. `NanoVDB` 体积云场景的二次启动 `SceneReady` 不再被 `MainPS` 编译拖慢。
|
||||
|
||||
@@ -1,484 +0,0 @@
|
||||
# XCUI NewEditor主线重建计划
|
||||
|
||||
日期:2026-04-07
|
||||
|
||||
## 1. 文档定位
|
||||
|
||||
本文档用于收口 `XCUI / XCEditor / new_editor` 当前阶段的真实执行主线。
|
||||
|
||||
它不是重复定义 XCUI 全部架构,而是把“接下来到底怎么做”重新拍板,并覆盖之前已经不再成立的执行假设。
|
||||
|
||||
本文档与现有文档的关系如下:
|
||||
|
||||
- `docs/plan/XCUI完整架构设计与执行计划.md`
|
||||
- 继续作为 XCUI 总体架构与三层设计的总纲参考。
|
||||
- 本文档
|
||||
- 作为 `new_editor` 主线重建的最新执行计划。
|
||||
- 优先级高于旧的阶段状态快照与旧执行顺序说明。
|
||||
|
||||
---
|
||||
|
||||
## 2. 当前拍板结论
|
||||
|
||||
当前必须明确以下结论,并作为后续开发硬约束:
|
||||
|
||||
### 2.1 旧 `editor/` 不再作为 XCUI 替换目标
|
||||
|
||||
- `editor/` 当前 ImGui 版本继续保留。
|
||||
- 它是参考实现、行为对照和视觉基线来源。
|
||||
- 当前阶段不再把主要精力放在“把 `XCEditor` 回填进旧 `editor/` 并替掉 ImGui”这条路线。
|
||||
|
||||
### 2.2 `new_editor/` 是未来正式编辑器主线
|
||||
|
||||
- `new_editor/` 不再只是临时名字上的沙盒。
|
||||
- 它应被视为未来正式编辑器的新主线工作区。
|
||||
- 后续真正重建编辑器时,应以 `new_editor/` 为宿主和产品主线,而不是回头改旧 `editor/`。
|
||||
|
||||
### 2.3 `tests/UI` 仍然是基础层唯一实验场
|
||||
|
||||
- `tests/UI/Core` 只验证 Core 共享能力。
|
||||
- `tests/UI/Editor` 只验证 Editor 基础层能力。
|
||||
- `tests/UI/Runtime` 只验证 Runtime 层能力。
|
||||
- 所有基础层验证、交互试验、截图检查,都优先放在 `tests/UI`。
|
||||
|
||||
### 2.4 `new_editor/` 当前不承担“实验面板堆场”
|
||||
|
||||
- `new_editor/` 当前只承载:
|
||||
- `XCEditor` 基础层库
|
||||
- `new_editor` 的真实宿主与产品装配代码
|
||||
- 在 Editor 基础层收口前,禁止把 `new_editor/` 继续做成杂乱的试验场或验证面板集合。
|
||||
- 需要人工操作检查的内容,仍然通过 `tests/UI/*/integration` 提供。
|
||||
|
||||
### 2.5 当前主线优先级是 `Editor`,不是 `Runtime`
|
||||
|
||||
- `Runtime` 继续按三层设计保留,但不是当前最高优先级。
|
||||
- 当前最高优先级是先把 `UI Core + UI Editor` 做成熟,并让其具备支撑 `new_editor` 正式重建的能力。
|
||||
|
||||
### 2.6 Editor 视觉基线以旧 `editor/` 为准
|
||||
|
||||
- `XCEditor` 当前默认视觉还没有达到旧 `editor/` 的程度。
|
||||
- 后续 `Editor UI` 的默认主题、控件密度、边框、分隔线、状态反馈强度,都要以旧 `editor/` 现有风格为基线。
|
||||
- 目标不是做一套“看起来差不多的深色 UI”,而是做出可以承载当前编辑器产品感的正式 Editor 风格。
|
||||
|
||||
---
|
||||
|
||||
## 3. 三层边界再次确认
|
||||
|
||||
### 3.1 Core
|
||||
|
||||
`Core` 负责:
|
||||
|
||||
- retained-mode UI 运行机制
|
||||
- layout / input / focus / scroll / popup / text / render contract
|
||||
- theme / token / style resolve 的通用机制
|
||||
- 与 Editor / Runtime 都共享的基础能力
|
||||
|
||||
`Core` 不负责:
|
||||
|
||||
- Editor 风格语义
|
||||
- Runtime screen/player 宿主策略
|
||||
- 业务面板
|
||||
|
||||
### 3.2 Runtime
|
||||
|
||||
`Runtime` 负责:
|
||||
|
||||
- game UI 的 screen / layer / player / system
|
||||
- runtime 输入路由、阻塞、导航、menu/hud/modal 语义
|
||||
- 面向游戏开发者的运行时宿主层
|
||||
|
||||
`Runtime` 不负责:
|
||||
|
||||
- Editor 专用控件
|
||||
- Editor 默认风格
|
||||
- 新编辑器重建主线
|
||||
|
||||
### 3.3 Editor
|
||||
|
||||
`Editor` 负责:
|
||||
|
||||
- editor-only widget
|
||||
- editor-only shell / dock / workspace / panel session / viewport shell
|
||||
- editor 风格语义
|
||||
- 默认 editor theme token 与资源化样式体系
|
||||
|
||||
`Editor` 不负责:
|
||||
|
||||
- 旧 `editor/` 的就地替换
|
||||
- 直接承担游戏 Runtime UI
|
||||
|
||||
---
|
||||
|
||||
## 4. 当前状态评估
|
||||
|
||||
## 4.1 已有基础
|
||||
|
||||
当前 `XCEditor` 已经具备以下基础:
|
||||
|
||||
- shell 级模型与状态骨架:
|
||||
- workspace
|
||||
- dock host
|
||||
- panel registry
|
||||
- panel lifecycle
|
||||
- layout persistence
|
||||
- shortcut / command 基础路径
|
||||
- editor 常用基础件:
|
||||
- menu bar / popup / context menu
|
||||
- tab strip
|
||||
- tree view / list view
|
||||
- scroll view
|
||||
- property grid
|
||||
- viewport shell / viewport slot
|
||||
- bool / number / enum field
|
||||
- `tests/UI/Editor` 已形成 `unit + integration` 的基础验证体系。
|
||||
|
||||
结论:
|
||||
|
||||
- `XCEditor` 已经不是 demo 级玩具。
|
||||
- 它已经具备继续作为 `new_editor` 底座推进的资格。
|
||||
|
||||
## 4.2 当前最突出的缺口
|
||||
|
||||
当前离“可正式支撑 `new_editor` 重建”的差距主要在以下几点:
|
||||
|
||||
### 4.2.1 Editor 默认样式资源层太薄
|
||||
|
||||
- 当前 `.xctheme` 只能控制少量颜色、spacing、radius。
|
||||
- 远不足以承载旧 editor 那种完整的产品级视觉体系。
|
||||
- 目前很多控件仍然只是在通用深色皮肤上运行,不是正式 Editor 风格。
|
||||
|
||||
### 4.2.2 Widget 视觉参数仍未充分资源化
|
||||
|
||||
- 仍有大量 palette / metrics / font size / rounding / inset 硬编码在 C++ 里。
|
||||
- 这会导致 `.xctheme` 只能“改一点颜色”,无法真正控制 Editor 外观。
|
||||
|
||||
### 4.2.3 视觉基线还未对齐旧 editor
|
||||
|
||||
- 当前 widget 默认密度偏松。
|
||||
- 圆角偏大。
|
||||
- 行高偏高。
|
||||
- 分隔线与层级关系不够清晰。
|
||||
- 菜单栏、面板框架、属性行、tab 等视觉结构还没有贴近旧 editor。
|
||||
|
||||
### 4.2.4 仍缺少若干 Editor 通用字段件与高级能力
|
||||
|
||||
- `TextField`
|
||||
- `Vector2Field`
|
||||
- `Vector3Field`
|
||||
- `Vector4Field`
|
||||
- `ColorField`
|
||||
- 后续可扩展的 `AssetField / ObjectField`
|
||||
|
||||
### 4.2.5 collection / shell 高级能力还未收口
|
||||
|
||||
- multi-selection
|
||||
- inline rename
|
||||
- drag/drop contract
|
||||
- virtualization
|
||||
- icon / glyph 统一语义
|
||||
- 更完整的 toolbar / status / shell chrome 体系
|
||||
|
||||
---
|
||||
|
||||
## 5. 当前阶段硬规则
|
||||
|
||||
### 5.1 先做基础层,不提前做业务面板
|
||||
|
||||
在 Editor 基础层完成收口前:
|
||||
|
||||
- 不开始复刻 Hierarchy / Inspector / Console / Project 这些完整业务面板。
|
||||
- 只允许做承载这些面板将来必需的通用 Editor 基础件。
|
||||
|
||||
### 5.2 Core 能力缺口必须先回补 Core
|
||||
|
||||
凡是发现缺的是:
|
||||
|
||||
- layout
|
||||
- input
|
||||
- focus
|
||||
- popup
|
||||
- text
|
||||
- render contract
|
||||
- shared widget primitive
|
||||
|
||||
都必须优先回补到 `Core` 或 shared 层,而不是在 `Editor` 层写临时绕过实现。
|
||||
|
||||
### 5.3 Editor 风格必须资源化,但语义归 Editor 层管理
|
||||
|
||||
- 样式机制归 `Core`
|
||||
- Editor 默认风格语义归 `Editor`
|
||||
- 颜色 / spacing / radius / border / density / typography 应尽量走资源化 token
|
||||
- 控件行为与结构语义仍由 `Editor` 代码层控制
|
||||
|
||||
### 5.4 `new_editor/` 只做正式产品装配,不做测试堆场
|
||||
|
||||
- 验证入口继续放在 `tests/UI`
|
||||
- `new_editor/` 只保留未来正式编辑器真正需要的宿主、装配、资源与业务层结构
|
||||
|
||||
---
|
||||
|
||||
## 6. Editor基础层完成标准
|
||||
|
||||
只有当以下条件基本满足后,才允许进入 `new_editor` 业务面板重建阶段:
|
||||
|
||||
### 6.1 视觉与样式层
|
||||
|
||||
- 已建立足够厚的 Editor token 体系
|
||||
- widget 的 palette / metrics 基本完成 theme 驱动
|
||||
- menu / popup / tab / panel / property / list / tree / status / scrollbar / splitter 都已接入 Editor 主题
|
||||
- 新主题能稳定逼近旧 editor 的视觉基线
|
||||
|
||||
### 6.2 字段件层
|
||||
|
||||
- `BoolField / NumberField / EnumField / TextField / Vector2/3/4Field / ColorField` 可稳定使用
|
||||
- `PropertyGrid` 能复用这些字段件,而不是自己硬画假控件
|
||||
|
||||
### 6.3 collection / shell 层
|
||||
|
||||
- tree / list / tab / popup / scroll / dock / workspace 都具备稳定基础交互
|
||||
- keyboard / focus / shortcut / popup / panel session 契约稳定
|
||||
- 关键状态机已通过 `unit`
|
||||
|
||||
### 6.4 验证层
|
||||
|
||||
- 每个重要基础件都有对应 `unit`
|
||||
- 每个重要交互点都有对应 `integration exe`
|
||||
- 截图检查链路可用
|
||||
- 中文操作指示和检查重点明确
|
||||
|
||||
---
|
||||
|
||||
## 7. 分阶段执行计划
|
||||
|
||||
## Phase A:Editor主题系统重构
|
||||
|
||||
### 目标
|
||||
|
||||
把 `Editor` 当前“薄主题 + 硬编码 widget 视觉”的状态,重构为“厚 token + 统一默认 Editor Theme”。
|
||||
|
||||
### 任务
|
||||
|
||||
- 定义 Editor 主题 token 命名规范。
|
||||
- 覆盖以下语义槽位:
|
||||
- workspace
|
||||
- panel
|
||||
- header
|
||||
- footer
|
||||
- menu bar
|
||||
- popup
|
||||
- tab
|
||||
- property row
|
||||
- field label/value
|
||||
- status bar
|
||||
- splitter
|
||||
- scrollbar
|
||||
- selection / hover / active / focus
|
||||
- 把现有 widget 中的默认 palette / metrics 尽量迁出为 Editor theme 可控项。
|
||||
- 建立“旧 editor 风格对齐”的基准场景与截图检查。
|
||||
|
||||
### 完成标准
|
||||
|
||||
- 主题资源文件已足够控制主要 Editor 视觉。
|
||||
- 修改主题时,不再需要频繁改 widget 绘制代码。
|
||||
|
||||
## Phase B:字段件体系补齐
|
||||
|
||||
### 目标
|
||||
|
||||
补齐 Editor 通用字段件,为后续 Inspector / 面板表单重建打基础。
|
||||
|
||||
### 任务
|
||||
|
||||
- 正式完成 `TextField`
|
||||
- 完成 `Vector2Field`
|
||||
- 完成 `Vector3Field`
|
||||
- 完成 `Vector4Field`
|
||||
- 完成 `ColorField`
|
||||
- 视需要评估 `AssetField / ObjectField` 的基础契约
|
||||
- 让 `PropertyGrid` 正式承接这些字段件
|
||||
|
||||
### 完成标准
|
||||
|
||||
- `PropertyGrid` 不再停留在基础标量字段件阶段。
|
||||
- 复合字段件已有完整 `unit + integration`。
|
||||
|
||||
### 当前检查点(2026-04-08)
|
||||
|
||||
- `TextField` 已完成正式接入:
|
||||
- `XCEditor` 已提供 `UIEditorTextField` 与 `UIEditorTextFieldInteraction`
|
||||
- `UIEditorTheme` 已补齐 `TextField` theme resolver 与 hosted builder
|
||||
- `PropertyGrid` 的 `Text` 行已切到正式 `TextField` 复用链路
|
||||
- `Vector2Field` 已完成第一批复合字段件接入:
|
||||
- `XCEditor` 已提供 `UIEditorVector2Field` 与 `UIEditorVector2FieldInteraction`
|
||||
- `UIEditorTheme` 已补齐 `Vector2Field` theme resolver 与 hosted builder
|
||||
- `tests/UI/Editor` 已补齐 `Vector2Field` 的 layout / hit-test / interaction / theme 单测
|
||||
- `editor_ui_vector2_field_basic_validation` 已可编译、运行、自动截图,并已接入 `editor_ui_integration_tests`
|
||||
- 当前 `tests/UI/Editor` 中,这两批字段件都遵守同一条验证规范:
|
||||
- 顶部必须明确写“这个测试验证什么功能”
|
||||
- 试验面板只放当前批次要检查的控件
|
||||
- 保持 Unity 向黑白灰风格,不把业务面板塞进 `new_editor`
|
||||
- 因此 `Phase B` 当前已正式完成前两项:
|
||||
1. `TextField`
|
||||
2. `Vector2Field`
|
||||
- 下一批主线顺序固定为:
|
||||
1. `Vector3Field`
|
||||
2. `Vector4Field`
|
||||
3. `ColorField`
|
||||
|
||||
## Phase C:Collection与交互高级能力收口
|
||||
|
||||
### 目标
|
||||
|
||||
让 tree/list/tab/menu/property 进入可长期复用的 Editor 基础件水平。
|
||||
|
||||
### 任务
|
||||
|
||||
- multi-selection
|
||||
- inline rename/edit session
|
||||
- drag/drop contract
|
||||
- virtualization 基础设计与第一轮实现
|
||||
- icon / glyph / disclosure / dropdown indicator 统一语义
|
||||
- collection 与 keyboard navigation/focus 的进一步收口
|
||||
|
||||
### 完成标准
|
||||
|
||||
- collection 控件不再只是演示级原型。
|
||||
- 已具备支撑真实 editor 面板的核心交互能力。
|
||||
|
||||
## Phase D:Shell与Workspace正式收口
|
||||
|
||||
### 目标
|
||||
|
||||
把 shell 基础层从“能演示”推进到“可作为新编辑器外壳底座”。
|
||||
|
||||
### 任务
|
||||
|
||||
- menu bar / popup / context menu 视觉与交互对齐旧 editor 基线
|
||||
- panel frame / status bar / tab strip / dock host 进一步主题化和结构收口
|
||||
- workspace session / panel lifecycle / shortcut / command / layout persistence 继续补齐
|
||||
- viewport shell / viewport input bridge 与 Editor shell 契约继续稳定
|
||||
|
||||
### 完成标准
|
||||
|
||||
- shell 基础层达到可承载空业务编辑器外壳的程度。
|
||||
|
||||
## Phase E:`new_editor` 空壳正式接管
|
||||
|
||||
### 目标
|
||||
|
||||
在不引入具体业务面板的前提下,让 `new_editor` 正式使用 `XCEditor` 搭建空编辑器壳层。
|
||||
|
||||
### 任务
|
||||
|
||||
- `new_editor` 中装配:
|
||||
- 主菜单壳层
|
||||
- 工具栏占位
|
||||
- workspace / dock 壳层
|
||||
- status bar
|
||||
- viewport shell 占位
|
||||
- 保持业务内容为空或极简占位,不提前混入具体面板逻辑。
|
||||
|
||||
### 完成标准
|
||||
|
||||
- `new_editor` 具备“正式产品空壳”形态。
|
||||
- 旧 `editor/` 继续只承担参考作用。
|
||||
|
||||
## Phase F:业务面板分批重建
|
||||
|
||||
### 前置条件
|
||||
|
||||
只有在 Phase A 到 Phase E 基本完成后,才进入本阶段。
|
||||
|
||||
### 执行原则
|
||||
|
||||
- 以旧 `editor/` 为行为和视觉参考
|
||||
- 以 `new_editor/` 为正式宿主
|
||||
- 业务面板按独立垂直切片逐个迁入
|
||||
|
||||
### 候选顺序
|
||||
|
||||
- Inspector 基础表单链路
|
||||
- Hierarchy
|
||||
- Console
|
||||
- Project
|
||||
- Scene/Game 相关工具壳层
|
||||
|
||||
---
|
||||
|
||||
## 8. 测试与验收规则
|
||||
|
||||
### 8.1 Core 能力进 `tests/UI/Core`
|
||||
|
||||
凡是共享能力,一律在 `tests/UI/Core` 验证:
|
||||
|
||||
- popup overlay primitive
|
||||
- scroll / focus / keyboard navigation
|
||||
- text input / style resolve / layout
|
||||
|
||||
### 8.2 Editor-only 能力进 `tests/UI/Editor`
|
||||
|
||||
凡是 editor-only,一律在 `tests/UI/Editor` 验证:
|
||||
|
||||
- field widgets
|
||||
- property grid
|
||||
- menu / popup / tab / dock / workspace
|
||||
- shell state / panel lifecycle / viewport shell
|
||||
|
||||
### 8.3 `new_editor` 不承担测试职责
|
||||
|
||||
- `new_editor` 只做产品集成冒烟检查
|
||||
- 不把验证场景继续塞到 `new_editor` 里
|
||||
|
||||
### 8.4 每批次收口要求
|
||||
|
||||
每推进一个 Editor 基础件批次,至少必须完成:
|
||||
|
||||
- 对应 `unit`
|
||||
- 对应 `integration exe`
|
||||
- 人工截图检查
|
||||
- 风格与交互结论记录
|
||||
|
||||
---
|
||||
|
||||
## 9. 与其它计划的关系
|
||||
|
||||
### 9.1 继续有效的文档
|
||||
|
||||
- `docs/plan/XCUI完整架构设计与执行计划.md`
|
||||
- 继续作为 XCUI 总体架构总纲
|
||||
- `docs/plan/Material Inspector与Shader属性面板收口计划_2026-04-07.md`
|
||||
- 继续作为未来业务层计划保留
|
||||
- 但执行前提是本计划中的 Editor 基础层先收口
|
||||
|
||||
### 9.2 已被覆盖的旧结论
|
||||
|
||||
以下旧结论不再作为当前主线执行依据:
|
||||
|
||||
- “后续主要目标是把 XCUI 直接嵌回旧 `editor/` 并替掉 ImGui”
|
||||
- “`new_editor/` 长期只作为临时沙盒存在”
|
||||
- “可以在 `new_editor/` 中继续堆各类试验面板作为主验证入口”
|
||||
|
||||
---
|
||||
|
||||
补充归档结论:
|
||||
|
||||
- `docs/plan/used/XCUI_Phase_Status_2026-04-05.md` 已转入归档,不再作为当前主线执行依据。
|
||||
|
||||
## 10. 当前结论
|
||||
|
||||
当前最重要的不是立刻复刻旧 editor 的具体业务面板,而是先把:
|
||||
|
||||
- `UI Core`
|
||||
- `UI Editor`
|
||||
- `new_editor` 正式宿主边界
|
||||
|
||||
这三者之间的职责彻底做对。
|
||||
|
||||
正确路径是:
|
||||
|
||||
1. 继续在 `tests/UI` 中把基础层做成熟
|
||||
2. 以旧 `editor/` 为风格与行为参考
|
||||
3. 以 `new_editor/` 为未来正式编辑器主线
|
||||
4. 待基础层达标后,再进入业务面板分批重建
|
||||
|
||||
这条路线比“直接替换旧 editor 中的 ImGui”风险更低,也更符合当前工程实际。
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,349 +0,0 @@
|
||||
# XCUI Phase Status 2026-04-05
|
||||
|
||||
## Scope
|
||||
|
||||
Current execution stays inside the XCUI module and `new_editor`.
|
||||
Old `editor` replacement is explicitly out of scope for this phase.
|
||||
|
||||
## Latest Checkpoint
|
||||
|
||||
- Phase 1 sandbox batch committed and pushed as `67a28bd` (`Add XCUI new editor sandbox phase 1`).
|
||||
- Phase 2 common/runtime batch committed and pushed as `ade5be3` (`Add XCUI runtime screen layer and demo textarea`).
|
||||
- Phase 3 has now produced a stable mixed batch across common/runtime/editor:
|
||||
- schema document definition data is now retained on `UIDocumentModel` and round-trips through the UI artifact path
|
||||
- schema self-definition validation is now stricter around enum/document-only metadata combinations
|
||||
- engine runtime coverage was tightened again around `UISystem` and concrete document-host rendering
|
||||
- `LayoutLab` continues as the editor widget proving ground for tree/list/property-section style controls
|
||||
- the demo sandbox and editor bridge APIs were tightened again without touching the old editor replacement scope
|
||||
- `new_editor` now has an explicit two-step window compositor seam through `IWindowUICompositor` / `ImGuiWindowUICompositor`, layered on top of `IEditorHostCompositor` / `ImGuiHostCompositor`
|
||||
- The native-host / hosted-preview publication follow-up is now landed in `new_editor`:
|
||||
- `NativeWindowUICompositor` is now buildable alongside the legacy ImGui compositor
|
||||
- `Application` now defaults to a native XCUI host path instead of creating the ImGui shell by default
|
||||
- the native default path now reuses shell-agnostic `XCUIDemoPanel::ComposeFrame(...)` / `XCUILayoutLabPanel::ComposeFrame(...)` results instead of keeping a second demo/layout runtime + input-bridge stack inside `Application`
|
||||
- the native compositor now publishes hosted-preview textures as SRV-backed `UITextureRegistration` / `UITextureHandle` values instead of relying on ImGui-only descriptor semantics
|
||||
- the native shell now begins the hosted-preview queue/registry lifecycle each frame, queues native preview frames, drains them during the native render pass, and consumes published hosted-surface images directly in panel cards with live/warming placeholder states
|
||||
- the old ImGui shell path remains present as an explicit compatibility host instead of the default host
|
||||
- `XCUIInputBridge.h` no longer drags `imgui.h` through the public XCUI input seam
|
||||
- The default native text path is now also de-ImGuiized inside `new_editor`:
|
||||
- `XCUIStandaloneTextAtlasProvider` now builds and owns its atlas through a Windows/GDI raster path instead of using ImGui font-atlas internals
|
||||
- the standalone atlas provider now exposes both `RGBA32` and `Alpha8` views, supports non-nominal size resolution, lazily adds non-prebaked BMP glyphs, and falls back to `?` when a glyph cannot be rasterized
|
||||
- The default native shell path now also has a cleaner translation-unit seam:
|
||||
- legacy ImGui shell chrome / HUD rendering now lives in a dedicated legacy-only `Application` translation unit instead of keeping direct `ImGui::*` calls inside the main native host TU
|
||||
- `Application.cpp` no longer directly includes `<imgui.h>`, even though the compatibility host path is still compiled into `new_editor`
|
||||
- The `new_editor` build now also has an explicit compatibility-source slice:
|
||||
- legacy ImGui shell sources and vendored ImGui backend sources are now grouped into a dedicated compatibility static library instead of being compiled directly as part of the main `XCNewEditor` source list
|
||||
- The main-target header-isolation milestone is now also landed in `new_editor`:
|
||||
- `XCNewEditor` drops direct `${IMGUI_SOURCE_DIR}` / `${IMGUI_SOURCE_DIR}/backends` include-directory requirements
|
||||
- the generated `XCNewEditor.vcxproj` no longer advertises `imgui-src` or `editor/src` on the default include surface
|
||||
- the default native compile path no longer reaches `<imgui.h>` through `editor/src/UI/ImGuiBackendBridge.h` or equivalent bridge headers
|
||||
- `XCNewEditorImGuiCompat` remains the explicit compatibility-only consumer of legacy ImGui headers and backend sources
|
||||
- The compositor/font compat boundary is now tighter as well:
|
||||
- `IWindowUICompositor.h` and `IEditorHostCompositor.h` no longer declare `CreateImGui*` factories on the generic XCUI seam
|
||||
- ImGui factory entry points now live only in `LegacyImGuiHostInterop.h`
|
||||
- `XCUIEditorFontSetup.h` no longer exposes `ImFont` / `ImFontAtlas`; the public API is now a current-context font bootstrap helper
|
||||
- `XCUIStandaloneTextAtlasProvider.h` no longer depends on the ImGui-oriented font bootstrap header
|
||||
- The generic shell command/state boundary is now narrower as well:
|
||||
- `XCUIShellChromeState` no longer carries the legacy host demo-window toggle or command id on the generic shell-state surface
|
||||
- `Application.h` no longer exposes that legacy demo command through generic `ShellCommandIds`, `ShellCommandBindings`, or `RegisterShellViewCommands(...)`
|
||||
- the legacy demo window toggle now lives as a compatibility-only command inside `ApplicationLegacyImGui.cpp`
|
||||
- `Application.h` now also uses backend-neutral compatibility-host naming for the non-native window-host path, so the generic application header no longer names `LegacyImGui` in its mode enum or private host-frame method declarations
|
||||
- generic `Application` host-mode/member naming is now also shifting from `LegacyImGui` toward `CompatibilityHost`, so the default shell surface does not have to encode ImGui into every host-facing method name
|
||||
- Old `editor` replacement remains deferred; all active execution still stays inside XCUI shared code and `new_editor`.
|
||||
|
||||
## Three-Layer Status
|
||||
|
||||
### 1. Common Core
|
||||
|
||||
- `UI::DrawData`, input event types, focus routing, style/theme resolution are in active use.
|
||||
- `UIDocumentCompiler` is buildable again after repairing the duplicated schema helper regression introduced by overlapping schema work.
|
||||
- `UIDocumentModel` / `UIDocumentResource` now retain schema definition metadata explicitly, including memory accounting and `UISchema` accessors.
|
||||
- `.xcschema` round-trip coverage is now present through compile, loader, artifact write, and artifact read paths.
|
||||
- Build-system hardening for MSVC/PDB output paths has started in root CMake, `engine/CMakeLists.txt`, `new_editor/CMakeLists.txt`, and `tests/NewEditor/CMakeLists.txt`.
|
||||
- Shared engine-side XCUI runtime scaffolding is now present under `engine/include/XCEngine/UI/Runtime` and `engine/src/UI/Runtime`.
|
||||
- Shared engine-side `UIDocumentScreenHost` now compiles `.xcui` / `.xctheme` screen documents into a runtime-facing document host path instead of leaving all document ownership in `new_editor`.
|
||||
- Shared text-editing primitives now live under `engine/include/XCEngine/UI/Text` and `engine/src/UI/Text`, so UTF-8 caret movement, line splitting, and multiline navigation are no longer trapped inside `XCUI Demo`.
|
||||
- Shared text-input controller/state now also lives under `engine/include/XCEngine/UI/Text` and `engine/src/UI/Text`, so character insertion, backspace/delete, submit, and multiline key handling no longer need to be reimplemented per host.
|
||||
- Shared editor collection primitive classification and metric helpers now also live under `engine/include/XCEngine/UI/Widgets` and `engine/src/UI/Widgets`, covering the current `ScrollView` / `TreeView` / `ListView` / `PropertySection` / `FieldRow` prototype taxonomy.
|
||||
- Shared single-selection state now also lives under `engine/include/XCEngine/UI/Widgets` and `engine/src/UI/Widgets` as `UISelectionModel`, so collection-style widget selection no longer has to stay private to `LayoutLab`.
|
||||
- Shared expansion state now also lives under `engine/include/XCEngine/UI/Widgets` and `engine/src/UI/Widgets` as `UIExpansionModel`, so collapsible tree/property-style widget state no longer has to stay private to `LayoutLab`.
|
||||
- Shared keyboard-navigation state now also lives under `engine/include/XCEngine/UI/Widgets` and `engine/src/UI/Widgets` as `UIKeyboardNavigationModel`, so list/tree/property-style widgets can share current-index, anchor, and home/end/step navigation rules instead of re-rolling them per sandbox.
|
||||
- Shared property-edit session state now also lives under `engine/include/XCEngine/UI/Widgets` and `engine/src/UI/Widgets` as `UIPropertyEditModel`, so editor-facing field rows can reuse begin/edit/commit/cancel transaction state instead of baking that directly into sandbox runtimes.
|
||||
- Core regression coverage now includes `UIContext`, layout, style, runtime screen player/system, and real document-host tests through `core_ui_tests`.
|
||||
|
||||
Current gap:
|
||||
|
||||
- Minimal schema self-definition support is landed, including consistency checks for enum/document-only schema metadata, but schema-driven validation for `.xcui` / `.xctheme` instances is still not implemented.
|
||||
- Shared widget/runtime instantiation is still thin and mostly editor-side.
|
||||
- Common widget primitives are still incomplete: shared text-input presentation/composition on top of the new text controller, multi-selection/focus-traversal/virtualized collection state on top of the new editor-primitive helpers, shared scroll/navigation-scope/caret-layout helpers, and promotion of the current native text-atlas path into a shared/cross-platform text subsystem.
|
||||
|
||||
### 2. Runtime/Game Layer
|
||||
|
||||
- The main concrete progress here is that the retained-mode demo runtime now supports a real `TextField` input path with UTF-8 text entry and backspace handling.
|
||||
- The demo runtime has moved past single-line input: multiline `TextArea` behavior is now covered in the sandbox testbed.
|
||||
- Engine-side runtime ownership is no longer zero: `UIScreenPlayer`, `UIDocumentScreenHost`, and `UISystem` now define a shared runtime contract for loading a screen document, ticking it with input, and collecting `UI::UIDrawData`.
|
||||
- `UISystem` now supports layered screen composition semantics: stacked screen players, top-interactive input routing, and modal layers that block lower screens.
|
||||
- `UIScreenStackController::ReplaceTop` now preserves the previous top screen if the replacement screen fails to load, so runtime menu flows do not silently drop their active layer on bad assets.
|
||||
- `SceneRuntime` now owns a dedicated `UISceneRuntimeContext`, so game/runtime code has a first-class place to configure viewport/focus, queue `UIInputEvent`s, drive `UISystem` each `Update`, and inspect the latest UI frame result.
|
||||
- Runtime screen emission now also carries concrete button text in the shared document host path instead of silently dropping button labels.
|
||||
- `UISystemFrameResult` now also preserves viewport rect, submitted input count, frame delta, and focus state, and both `UISystem` and `UISceneRuntimeContext` now expose `ConsumeLastFrame()` so runtime/game hosts can drain the last retained frame packet without copying editor-side concepts into the shared layer.
|
||||
- `UIScreenPlayer` now also exposes `ConsumeLastFrame()`, so player/system/context all share the same consume-vs-borrow frame ownership semantics in the runtime layer.
|
||||
|
||||
Current gap:
|
||||
|
||||
- Runtime UI is now wired into `SceneRuntime`, but render submission is still limited to producing `UIDrawData`; there is no game-view/runtime presenter path that automatically draws those frames yet.
|
||||
- The runtime widget library is still shallow and missing the editor-grade controls that will later be shared downward.
|
||||
|
||||
### 3. Editor Layer
|
||||
|
||||
- `new_editor` remains the isolated XCUI sandbox.
|
||||
- Native hosted preview is now working end-to-end as `RHI offscreen surface -> SRV-backed publication -> hosted surface image present` through the native shell path.
|
||||
- Hosted preview surface descriptors now stay on XCUI-owned value types (`UITextureHandle`, `UIPoint`, `UIRect`) instead of exposing ImGui texture/UV types through the generic preview contract.
|
||||
- Shared `UI::UIDrawData` image commands now carry explicit `uvMin` / `uvMax` source-rect semantics, and the native panel canvas host preserves those UVs when it records hosted surface-image preview commands.
|
||||
- `XCUI Demo` remains the long-lived effect and behavior testbed.
|
||||
- `XCUI Demo` now covers both single-line and multiline text authoring behavior, including click caret placement, delete/backspace, tab indentation, and optional text-area line numbers.
|
||||
- `XCUI Demo` now consumes the shared `UITextInputController` path for text editing instead of carrying a private key-handling state machine.
|
||||
- `LayoutLab` now includes a `ScrollView` prototype and a more editor-like three-column authored layout.
|
||||
- `LayoutLab` now also covers editor-facing widget prototypes: `TreeView`, `TreeItem`, `ListView`, `ListItem`, `PropertySection`, and `FieldRow`.
|
||||
- `LayoutLab` now consumes the shared `UIEditorCollectionPrimitives` helper layer for collection-widget tag classification, clipping flags, and default metric resolution instead of keeping that taxonomy private to the sandbox runtime.
|
||||
- `LayoutLab` now also consumes the shared `UISelectionModel` for click-selection persistence across collection-style widgets, and the diagnostics panel now exposes both hovered and selected element ids.
|
||||
- `LayoutLab` now also consumes the shared `UIExpansionModel` for tree expansion and property-section collapse, with reserved property headers, disclosure glyphs, and persisted click-toggle behavior in the sandbox runtime.
|
||||
- `new_editor` now also has an isolated `XCUIEditorCommandRouter` model with shortcut matching, enable predicates, and direct command invocation semantics covered by dedicated tests, ready for shell-frame integration.
|
||||
- `XCUI Demo` now exports pending per-frame command ids through `DrainPendingCommandIds()`, so editor-side hosts have a clean seam for observing demo/runtime command traffic without parsing draw data.
|
||||
- `XCUI Demo` and `LayoutLab` panel canvases are now being pulled behind a dedicated `IXCUIPanelCanvasHost` seam, so canvas surface presentation, hover/focus fallback state, and overlay draw hooks no longer have to stay hard-coded inside each ImGui panel implementation.
|
||||
- The panel-canvas seam now keeps the generic host contract on observable canvas behavior only; backend/capability identity probing has been removed from `IXCUIPanelCanvasHost`, and the minimal `NullXCUIPanelCanvasHost` remains the concrete placeholder host for non-ImGui paths.
|
||||
- `XCUI Demo` and `LayoutLab` panel input now also flows through an explicit `IXCUIInputSnapshotSource` seam, so panel/runtime code no longer reads `ImGuiIO` / `ImGui::IsKeyPressed` / `ImGui::IsMouseClicked` directly when the shell wants to use an ImGui adapter.
|
||||
- `new_editor` now also has an explicit `ImGuiXCUIInputSnapshotSource` adapter, keeping ImGui-specific input capture in the host adapter layer instead of inside panel/runtime update code.
|
||||
- `XCUI Demo` and `LayoutLab` panels now both expose shell-agnostic per-frame composition results (`ComposeFrame(...)` + `GetLastFrameComposition()`), so native/compat shells can reuse one panel-local frame pipeline instead of duplicating runtime/input/preview assembly logic.
|
||||
- `XCUIDemoPanel::ComposeFrame(...)` now also accepts an injected input snapshot plus explicit placeholder/frame options, which lets the native shell reuse the panel pipeline without falling back to the compatibility-path placeholder behavior on direct draw-data cards.
|
||||
- Panel diagnostics were expanded to clearly separate preview/runtime/input state and native vs legacy paths.
|
||||
- The editor bridge layer now has smoke coverage for swapchain after-UI rendering hooks and SRV-backed ImGui texture descriptor registration.
|
||||
- `Application` no longer owns the ImGui backend directly; window presentation now routes through `IWindowUICompositor` with an `ImGuiWindowUICompositor` implementation, which currently delegates to `IEditorHostCompositor` / `ImGuiHostCompositor`.
|
||||
- Hosted preview offscreen surfaces now keep compositor-returned `UITextureRegistration` / `UITextureHandle` data inside `Application` instead of storing `ImTextureID` directly.
|
||||
- The generic hosted-preview presenter contract no longer owns `ImGuiTransitionBackend`; the ImGui presenter now sits in a separate `ImGuiXCUIHostedPreviewPresenter` header while the native queue/surface registry remains XCUI-generic.
|
||||
- The generic hosted-preview frame contract no longer carries an ImGui draw-list pointer; the legacy ImGui presenter resolves its inline draw target from the active ImGui window context instead of pushing that type through the XCUI contract.
|
||||
- The legacy ImGui hosted-preview presenter now also accepts an explicit draw-target binding object, so presenter-side `ImGui::GetWindowDrawList()` lookup is no longer hard-coded inside the generic presenter path and can stay isolated behind the ImGui adapter layer.
|
||||
- `Application` shell menu toggles and global shortcuts now route through `XCUIEditorCommandRouter` instead of directly mutating shell booleans from menu callbacks, giving the editor layer a real command-routing seam.
|
||||
- `LayoutLab` runtime now consumes the shared `UIKeyboardNavigationModel` for abstract list/tree/property navigation actions (`previous/next/home/end/collapse/expand`), so keyboard collection traversal rules are no longer trapped in sandbox-local state.
|
||||
- `LayoutLab` panel input now also maps concrete arrow/home/end keys into those shared navigation actions, so keyboard traversal is reachable from the sandbox UI instead of staying runtime-only.
|
||||
- `XCUIDemoRuntime` now bridges pointer activation, text-edit commands, and shortcut-triggered commands through a unified command path, and `DrainPendingCommandIds()` now preserves mixed pointer/text/shortcut ordering.
|
||||
- `new_editor` now also has a pure `XCUIShellChromeState` model covering panel visibility, hosted-preview mode, and shell-level view toggles without depending on ImGui, `Application`, or the old editor.
|
||||
- `XCUIShellChromeState` hosted-preview mode naming is now backend-neutral (`HostedPresenter` / `NativeOffscreen`) instead of encoding `ImGui` into the XCUI shell model.
|
||||
- The shell chrome view-toggle model no longer carries the legacy host demo window at all on the generic XCUI seam; that command now lives only inside the compatibility-only legacy host implementation.
|
||||
- `new_editor` now also has a concrete `NativeWindowUICompositor` path and native-focused compositor tests, so the window compositor seam is no longer ImGui-only.
|
||||
- `Application` now also has a native XCUI shell path that:
|
||||
- becomes the default `new_editor` startup path
|
||||
- lays out `XCUI Demo` and `Layout Lab` as native cards directly in the swapchain window
|
||||
- routes shell shortcuts through the same command router without reading ImGui capture state in the default host path
|
||||
- reuses panel-local `ComposeFrame(...)` entry points for demo/layout runtime, input, hosted-preview, and overlay composition instead of maintaining duplicate native-shell runtime/input state in `Application`
|
||||
- composes one native `UIDrawData` packet and submits it through `NativeWindowUICompositor`
|
||||
- Native shell preview-mode reconfiguration now rebuilds the native panel bindings instead of rebinding a legacy hosted presenter, so the default host path no longer needs the ImGui presenter when a card stays on direct draw-data composition.
|
||||
- The native shell layout policy now also lives behind `XCUINativeShellLayout`, so top-bar/footer/workspace geometry, panel split rules, and active-panel transfer on pointer press are no longer hard-coded inline inside `Application.cpp`.
|
||||
- `NativeXCUIPanelCanvasHost` now backs that direct shell path as an externally driven canvas/session host for native cards instead of assuming an ImGui child-window model, and it now emits native `Image` draw commands for hosted surface-image previews while preserving per-surface UVs.
|
||||
- `NativeWindowUICompositor` now creates and frees SRV-backed texture registrations for hosted preview surfaces, so native publication no longer depends on ImGui descriptor handles.
|
||||
- `Application` now runs the hosted-preview lifecycle in both legacy and native frame paths, treats published textures as XCUI-owned `UITextureHandle` state, queues native preview frames from `BuildNativeShellDrawData(...)`, and drains them during native rendering before shell chrome overlays.
|
||||
- `XCUIInputBridge.h` no longer includes `imgui.h`, so the public XCUI input bridge seam is now host-neutral at the header boundary.
|
||||
- `XCNewEditor` builds successfully to `build/new_editor/bin/Debug/XCNewEditor.exe`.
|
||||
|
||||
Current gap:
|
||||
|
||||
- The default shell host is now native, and the legacy ImGui shell/panel path has been split out of the default executable into the standalone `XCNewEditorImGuiCompat` compatibility slice.
|
||||
- The default native shell path is now split away from direct `ImGui::*` calls at the main-target header/include level and no longer links the compatibility slice by default.
|
||||
- The default native shell now also consumes shared `XCUINativeShellLayout` and `UIEditorPanelChrome` helpers for panel split/chrome policy instead of duplicating that card layout logic entirely inside `Application.cpp`.
|
||||
- The native shell currently proves direct runtime composition, but its shell chrome is still a bespoke `Application`-side layout rather than a fully shared XCUI-authored editor shell document.
|
||||
- Editor-specialized widgets are still incomplete at the shared-module level: the authored prototypes exist, but virtualization, multi-selection/focus traversal, toolbar/menu chrome, menu interaction widgets, and icon-atlas widgets are not yet extracted into reusable XCUI modules.
|
||||
- The default native text path now uses a standalone Windows/GDI atlas through `XCUIStandaloneTextAtlasProvider`, but that provider still lives inside `new_editor` and is not yet promoted into a shared/cross-platform text subsystem.
|
||||
|
||||
## Validated This Phase
|
||||
|
||||
- `new_editor_xcui_demo_panel_tests`: `4/4`
|
||||
- `new_editor_xcui_demo_runtime_tests`: `12/12`
|
||||
- `new_editor_xcui_input_bridge_tests`: `4/4`
|
||||
- `new_editor_imgui_xcui_input_adapter_tests`: `2/2`
|
||||
- `new_editor_xcui_layout_lab_runtime_tests`: `12/12`
|
||||
- `new_editor_xcui_rhi_command_compiler_tests`: `7/7`
|
||||
- `new_editor_xcui_rhi_render_backend_tests`: `5/5`
|
||||
- `new_editor_xcui_standalone_text_atlas_provider_tests`: `6/6`
|
||||
- `new_editor_xcui_hosted_preview_presenter_tests`: `20/20`
|
||||
- `new_editor_legacy_imgui_host_interop_tests`: `4/4`
|
||||
- `new_editor_imgui_window_ui_compositor_tests`: `7/7`
|
||||
- `new_editor_native_window_ui_compositor_tests`: `8/8`
|
||||
- `new_editor_xcui_editor_command_router_tests`: `5/5`
|
||||
- `new_editor_application_shell_command_bindings_tests`: `11/11`
|
||||
- `new_editor_xcui_shell_chrome_state_tests`: `11/11`
|
||||
- `new_editor_xcui_panel_canvas_host_tests`: `4/4`
|
||||
- `new_editor_imgui_xcui_panel_canvas_host_tests`: `1/1`
|
||||
- `new_editor_native_xcui_panel_canvas_host_tests`: `4/4`
|
||||
- `new_editor_xcui_layout_lab_panel_tests`: `6/6`
|
||||
- `XCNewEditor` Debug target builds successfully
|
||||
- `XCNewEditor.exe` native-default smoke run stayed alive for `5s`
|
||||
- `core_ui_tests`: `52 total` (`50` passed, `2` skipped because `KeyCode::Delete` currently aliases `Backspace`)
|
||||
- `scene_tests`: `68/68`
|
||||
- `core_ui_style_tests`: `5/5`
|
||||
- `ui_resource_tests`: `11/11`
|
||||
- `editor_tests` targeted bridge smoke: `3/3`
|
||||
|
||||
## Landed This Phase
|
||||
|
||||
- Demo runtime `TextField` with UTF-8 text insertion, caret state, and backspace.
|
||||
- Demo runtime multiline `TextArea` path in the sandbox and test coverage for caret movement / multiline input.
|
||||
- Common-core `UITextEditing` extraction now owns UTF-8 offset stepping, codepoint counting, line splitting, and vertical caret motion with dedicated `core_ui_tests` coverage.
|
||||
- Common-core `UITextInputController` extraction now owns per-field text state, character insertion, enter-submit, and multiline keyboard editing behavior with dedicated `core_ui_tests` coverage.
|
||||
- Common-core `UIEditorCollectionPrimitives` extraction now owns the editor collection tag taxonomy and default metric resolution used by current `LayoutLab` widget prototypes, with dedicated `core_ui_tests` coverage.
|
||||
- Common-core `UISelectionModel` extraction now owns reusable single-selection state for collection-style widgets, with dedicated `core_ui_tests` coverage.
|
||||
- Common-core `UIExpansionModel` extraction now owns reusable expansion/collapse state for tree/property-style widgets, with dedicated `core_ui_tests` coverage.
|
||||
- Common-core `UIKeyboardNavigationModel` extraction now owns reusable current-index/anchor navigation state for collection-style widgets, with dedicated `core_ui_tests` coverage.
|
||||
- Common-core `UIPropertyEditModel` extraction now owns reusable property-field edit session state, including staged values and commit/cancel behavior, with dedicated `core_ui_tests` coverage.
|
||||
- Runtime frame ownership was tightened again:
|
||||
- `UIScreenPlayer::ConsumeLastFrame()` now exposes consume-style packet ownership at the player layer
|
||||
- `UISystemFrameResult` now carries viewport rect, submitted input event count, frame delta, and focus state
|
||||
- `UISystem::ConsumeLastFrame()` moves the retained packet out of the runtime layer
|
||||
- `UISceneRuntimeContext::ConsumeLastFrame()` forwards the same shared runtime seam upward
|
||||
- Demo runtime text editing was extended with:
|
||||
- click-to-place caret
|
||||
- `Delete` support
|
||||
- `Tab` / `Shift+Tab` indentation for text areas
|
||||
- optional text-area line-number gutter rendering
|
||||
- Demo authored resources updated to exercise the input field.
|
||||
- LayoutLab `ScrollView` prototype with clipping and hover rejection outside clipped content.
|
||||
- LayoutLab editor-widget prototypes for tree/list/property-style sections with dedicated runtime coverage.
|
||||
- LayoutLab click-selection now persists through the shared `UISelectionModel`, including selected-state diagnostics and reusable visual selection feedback on cards, collection rows, and field rows.
|
||||
- LayoutLab tree expansion and property-section collapse now persist through the shared `UIExpansionModel`, including reserved property headers, disclosure glyphs, and runtime coverage for collapsed/expanded visibility.
|
||||
- `XCUIDemoRuntime` now exposes `DrainPendingCommandIds()` so hosts can observe emitted runtime commands in order across pointer/text interactions without scraping UI text or draw-command payloads.
|
||||
- `XCUIDemoRuntime` command recording was tightened so pointer activation, text editing, and shortcut-triggered commands now share one bridge path and preserve mixed ordering in `DrainPendingCommandIds()`.
|
||||
- Schema document support extended with:
|
||||
- retained `UISchemaDefinition` data on `UIDocumentModel`
|
||||
- artifact schema version bump for UI documents
|
||||
- loader/resource accessors and memory accounting
|
||||
- schema compile/load/artifact regression coverage
|
||||
- schema consistency rules for:
|
||||
- `allowedValues` only on `enum`
|
||||
- `documentKind` / `restrictDocumentKind` only on `document`
|
||||
- explicit `documentKind` required when `restrictDocumentKind=true`
|
||||
- Engine runtime layer added:
|
||||
- `UIScreenPlayer`
|
||||
- `UIDocumentScreenHost`
|
||||
- `UISceneRuntimeContext`
|
||||
- `UIScreenStackController`
|
||||
- `UISystem`
|
||||
- layered screen composition and modal blocking semantics
|
||||
- Runtime/game integration scaffolding now includes reusable `HUD/menu/modal` stack helpers on top of `UISystem`.
|
||||
- `UIScreenStackController` replacement now rolls back safely on failure instead of popping the active top layer first.
|
||||
- `SceneRuntime` now exposes XCUI runtime ownership directly:
|
||||
- `GetUISystem()`
|
||||
- `GetUIScreenStackController()`
|
||||
- `GetLastUIFrame()`
|
||||
- `SetUIViewportRect(...)`
|
||||
- `SetUIFocused(...)`
|
||||
- `QueueUIInputEvent(...)`
|
||||
- `ClearQueuedUIInputEvents()`
|
||||
- automatic `UISystem` ticking during `SceneRuntime::Update(...)`
|
||||
- Runtime document-host draw emission now preserves button labels for shared screen rendering.
|
||||
- RHI image path improvements:
|
||||
- clipped image UV adjustment
|
||||
- mirrored image UV preservation
|
||||
- explicit image UV/source-rect submission through `UI::UIDrawData`
|
||||
- external texture binding reuse
|
||||
- per-batch scissor application
|
||||
- Editor bridge helpers now expose:
|
||||
- an `afterUiRender` swapchain callback hook in `D3D12WindowRenderer`
|
||||
- SRV-view based texture descriptor registration in `ImGuiBackendBridge`
|
||||
- smoke tests for window renderer, ImGui backend bridge, and console sink registration
|
||||
- `new_editor` host presentation now has a first-class compositor seam:
|
||||
- `IWindowUICompositor`
|
||||
- `ImGuiWindowUICompositor`
|
||||
- `IEditorHostCompositor`
|
||||
- `ImGuiHostCompositor`
|
||||
- `Application` frame/present flow routed through the compositor instead of direct `m_imguiBackend` ownership
|
||||
- The window-level XCUI compositor seam now also has a dedicated regression target around `ImGuiWindowUICompositor`, covering initialization, render-frame ordering, Win32 message forwarding, texture registration forwarding, and shutdown safety.
|
||||
- The legacy compatibility seam now also has dedicated regression coverage around `LegacyImGuiHostInterop`, covering compat factory wiring, ImGui input-capture forwarding, legacy font bootstrap, and the disabled demo-window path.
|
||||
- The window compositor and hosted-preview seams gained more edge-case coverage around no-UI render passes, compositor re-initialization/rebinding, partial logical-size fallback, and descriptor reuse for repeated queued-frame keys.
|
||||
- `new_editor` now has a dedicated `XCUIEditorCommandRouter` test target covering command registration, replacement, enable predicates, accelerator matching, and policy gates around focus/keyboard capture/text input.
|
||||
- `Application` now integrates `XCUIEditorCommandRouter` into the shell itself:
|
||||
- `View` menu items invoke routed commands instead of directly mutating shell state
|
||||
- shell-level shortcuts now flow from `XCUIWin32InputSource` through `XCUIInputBridge` into command matching
|
||||
- hosted-preview mode toggles still trigger presenter reconfiguration through the routed command bindings
|
||||
- `new_editor` panel canvas ownership is now being split behind `IXCUIPanelCanvasHost`, with an `ImGuiXCUIPanelCanvasHost` adapter carrying the legacy path so panel code stops directly owning `ImGui::Image` / `ImGui::InvisibleButton` / draw-list preview plumbing.
|
||||
- `XCUIDemoPanel` and `XCUILayoutLabPanel` no longer create an ImGui hosted-preview presenter or ImGui panel canvas host implicitly; default construction now stays on null/explicitly injected backends until the outer shell binds a concrete host adapter.
|
||||
- `Application` now binds `ImGuiXCUIPanelCanvasHost` explicitly at the shell composition root, so the current ImGui panel host path is visible as a host-layer decision instead of a panel-layer fallback.
|
||||
- `XCUIDemoPanel` and `XCUILayoutLabPanel` no longer read ImGui input directly; both now consume an injected `IXCUIInputSnapshotSource`, and the new `ImGuiXCUIInputSnapshotSource` keeps the current ImGui-backed input path isolated behind an explicit adapter.
|
||||
- `new_editor` now also has a pure `XCUIShellChromeState` model with dedicated tests, covering shell panel visibility, hosted-preview mode, and shell view toggles without depending on ImGui or `Application`.
|
||||
- `XCUIShellChromeState` now also exposes effective hosted-preview state helpers and shell view-toggle command-id helpers, so shell routing code no longer has to manually combine enablement and requested preview mode.
|
||||
- `XCUIShellChromeState` hosted-preview modes were renamed away from `LegacyImGui`, so XCUI shell state no longer treats ImGui as the generic fallback concept.
|
||||
- The panel-canvas seam now has dedicated null/compat/native coverage around stable debug names, passive-session safety, externally driven native snapshots, and compat-host factory creation without relying on explicit backend/capability reporting.
|
||||
- The native host follow-up is now present in `new_editor`:
|
||||
- `NativeWindowUICompositor` provides a swapchain-native XCUI packet present path beside the legacy ImGui compositor
|
||||
- `Application` now defaults to that native host path and directly composes `XCUI Demo` plus `Layout Lab` into one native shell frame
|
||||
- `NativeXCUIPanelCanvasHost` now drives externally configured native card sessions for that shell path and records hosted surface-image preview commands with preserved UVs
|
||||
- new native compositor/native canvas-host tests now cover the new host seam
|
||||
- `XCUIInputBridge.h` no longer includes `imgui.h`, so XCUI input translation is no longer coupled to ImGui at the public header boundary.
|
||||
- `SceneRuntime` layered XCUI routing now has dedicated regression coverage for:
|
||||
- top-interactive layer input ownership
|
||||
- blocking/modal layer suppression of lower layers
|
||||
- hidden top-layer pass-through back to visible underlying layers
|
||||
- Shared `UITextInputController` coverage now includes more caret-boundary / modifier branches; the remaining `Delete` distinction stays blocked on `KeyCode::Delete` and `KeyCode::Backspace` still sharing the same enum value.
|
||||
- Window compositor texture registration now also flows back into `Application` as XCUI-owned `UITextureRegistration` / `UITextureHandle` data instead of exposing raw `ImTextureID` there.
|
||||
- Hosted preview contracts were tightened again:
|
||||
- generic preview surface metadata stays on XCUI-owned value types
|
||||
- `ImGuiTransitionBackend` moved behind `ImGuiXCUIHostedPreviewPresenter`
|
||||
- generic preview frame submission no longer carries an ImGui draw-list pointer
|
||||
- the ImGui presenter now resolves inline draw targets through an explicit ImGui-only binding seam
|
||||
- panel/runtime callers still preserve the same legacy and native-preview behavior
|
||||
- Native hosted-preview publication milestone is now wired through the default shell path:
|
||||
- `NativeWindowUICompositor` publishes hosted preview textures as SRV-backed XCUI registrations and frees them through the compositor seam
|
||||
- `Application::BeginHostedPreviewFrameLifecycle(...)` now resets queue/registry state for both legacy and native frame paths
|
||||
- hosted-preview surface readiness now keys on published texture availability instead of ImGui-style descriptor validity
|
||||
- `BuildNativeShellDrawData(...)` now queues native preview frames for `XCUI Demo` / `LayoutLab`, while shell cards consume the previously published hosted-surface image with warming/live placeholder text
|
||||
- native compositor and shell-command tests now cover the new publication / lifecycle guards
|
||||
- `LayoutLab` now resolves editor collection widget taxonomy and metrics through shared `UIEditorCollectionPrimitives` helpers instead of duplicating the same tag and metric rules inside the sandbox runtime.
|
||||
- `LayoutLab` runtime now consumes shared keyboard-navigation semantics for list/tree/property traversal, while the remaining panel-level key mapping is tracked as an editor-host integration gap rather than a runtime gap.
|
||||
- `LayoutLab` panel now maps concrete arrow/home/end keys into the shared navigation model, with dedicated panel-level coverage proving that the sandbox UI can actually drive the runtime navigation seam end-to-end.
|
||||
- `new_editor` panel/shell diagnostics improvements for hosted preview state.
|
||||
- XCUI asset document loading changed to prefer direct source compilation before `ResourceManager` fallback for the sandbox path, fixing the LayoutLab crash.
|
||||
- `UIDocumentCompiler.cpp` repaired enough to restore full local builds after the duplicated schema-helper regression.
|
||||
- MSVC debug build hardening was tightened again so large parallel `engine` rebuilds stop tripping over compile-PDB contention.
|
||||
- `XCUIStandaloneTextAtlasProvider` no longer uses ImGui font-atlas/internal baking helpers:
|
||||
- atlas ownership now stays inside a standalone provider implementation built on Windows/GDI glyph rasterization
|
||||
- default editor atlas prewarms the current supported nominal sizes, exposes both `RGBA32` and `Alpha8` atlas views, lazily inserts non-prebaked BMP glyphs, and falls back to `?` for unrasterizable codepoints
|
||||
- standalone atlas coverage now includes reset/rebuild, non-nominal size resolution, lazy glyph insertion/fallback behavior, and smoke use without any ImGui context
|
||||
- Legacy shell chrome / HUD rendering is now split out of the main `Application.cpp` translation unit:
|
||||
- the direct `ImGui::*` shell rendering path now lives in a dedicated legacy-only `Application` implementation file
|
||||
- the main `Application.cpp` native host path no longer directly includes `<imgui.h>`, reducing default-path compile-time coupling while the remaining main-target header/include cleanup stays open
|
||||
- `new_editor` build composition is now split into main/native and compatibility slices:
|
||||
- the main `XCNewEditor` target no longer compiles legacy ImGui shell/panel/backend source files directly
|
||||
- legacy ImGui shell/panel/backend sources plus vendored ImGui sources now build behind a dedicated compatibility static library that the main executable links
|
||||
- the main target no longer carries ImGui include directories or older editor bridge headers on its direct compile surface; those remain confined to the compatibility slice
|
||||
- The default XCUI compile seam is now also cleaner:
|
||||
- generic compositor headers no longer advertise compat-only `CreateImGui*` factories
|
||||
- compat-only host/window compositor factories now live behind `LegacyImGuiHostInterop.*`
|
||||
- public font bootstrap headers no longer leak `ImFont` / `ImFontAtlas` types into generic XCUI consumers
|
||||
|
||||
## Phase Risks Still Open
|
||||
|
||||
- Schema instance validation is still open beyond `.xcschema` self-definition and artifact round-trip coverage.
|
||||
- `ScrollView` is still authored/static; no wheel-driven scrolling or virtualization yet.
|
||||
- `XCNewEditor` no longer depends on ImGui at the default-path header/include level and no longer links `XCNewEditorImGuiCompat`; the legacy shell/panel path now lives only in the standalone compatibility slice.
|
||||
- Legacy panel implementations such as `XCUIDemoPanel` / `XCUILayoutLabPanel` still render as ImGui windows inside the compatibility slice, so editor-layer behavior is not yet fully carried by XCUI-native shell composition.
|
||||
- The default native text path now owns its atlas without ImGui, but the provider is still Windows-only and remains trapped inside `new_editor` instead of a shared/cross-platform text layer.
|
||||
- Hosted-preview compatibility presentation still depends on an ImGui-only inline presenter path when not using the queued native surface path.
|
||||
- Editor widget coverage is still prototype-driven inside `LayoutLab`; it has not yet been promoted into a full reusable shared widget/runtime layer with command routing, virtualization, and property-edit transactions.
|
||||
|
||||
## Execution-Plan Alignment
|
||||
|
||||
- Against `XCUI完整架构设计与执行计划.md`, current `new_editor` progress should be treated as an early `Phase 8` foothold rather than full `Milestone E` completion:
|
||||
- landed: `NativeWindowUICompositor`, native shell packet composition, native hosted-preview publication, XCUI-owned texture registrations, native panel surface-image presentation, standalone native text-atlas ownership inside `new_editor`, legacy `Application` TU split, `XCNewEditorImGuiCompat`, main-target header/include isolation from ImGui, default-executable unlinking from the compatibility slice, compat-only factory/font seam tightening, shared native shell layout helpers, and shared panel-chrome/flat-hierarchy helpers
|
||||
- not yet landed: promotion of the native text-atlas path into a shared/cross-platform text subsystem, shared XCUI-authored editor shell chrome, and retirement of legacy ImGui-window panel implementations
|
||||
- That means the next de-ImGui push should not keep centering on hosted-preview publication; that milestone is now effectively closed for the default native shell path.
|
||||
- The real remaining default-path blockers are:
|
||||
- move editor-layer behavior off the legacy ImGui-window panel implementations and into native/XCUI shell composition
|
||||
- keep compat-only factories/types from drifting back into generic XCUI seams while the compatibility slice remains linked
|
||||
- harden and promote `XCUIStandaloneTextAtlasProvider` / editor font bootstrap into a shared native text subsystem
|
||||
- move native shell chrome out of bespoke `Application` layout code and into a shared XCUI shell model or authored shell document
|
||||
|
||||
## Next Phase
|
||||
|
||||
1. Expand runtime/game-layer ownership from the current `SceneRuntime` UI context into scene-declared HUD/menu bootstrapping, draw submission, and higher-level runtime UI policies.
|
||||
2. Promote the current editor-facing widget prototypes out of authored `LayoutLab` content and into reusable XCUI widget/runtime modules, then continue with toolbar/menu chrome, shell-state adoption, virtualization, and broader focus/multi-selection behavior.
|
||||
3. Push the remaining de-ImGui cleanup from build/header isolation into behavior isolation: keep ImGui confined to `XCNewEditorImGuiCompat`, keep generic XCUI seams free of compat-only factories/types, and keep shrinking the legacy shell boundary.
|
||||
4. Promote the current standalone native text/font path out of `new_editor`, harden its atlas invalidation/caching contract, and keep the remaining font/bootstrap ownership compatibility-only.
|
||||
5. Promote the native shell chrome and card layout out of bespoke `Application` code into a shared XCUI/editor-layer shell model or authored shell document, then retire the remaining ImGui-window panel path.
|
||||
6. Continue phased validation, commit, push, and plan refresh after each stable batch.
|
||||
@@ -2,42 +2,42 @@
|
||||
|
||||
## 1.1 课题背景
|
||||
|
||||
随着实时渲染技术的发展,渲染引擎所面对的应用场景已经逐步扩展到游戏、虚拟现实、数字内容生产和交互式可视化等多个方向,逐渐演变为集图形接口抽象、资源导入与管理、场景组织、材质与光照、脚本运行时以及编辑器工作流于一体的综合软件系统。围绕渲染引擎整体架构开展设计与实现,也更便于将运行时系统、工具链以及后续渲染扩展放在统一平台中加以组织。
|
||||
随着实时渲染技术的发展,渲染引擎的应用场景已经逐步扩展至游戏、虚拟现实、数字内容生产和交互式可视化等多个方向,逐渐演变为集图形硬件接口、资源导入与管理、场景组织、材质与光照、脚本运行时以及编辑器工作流于一体的综合软件系统。围绕渲染引擎整体架构开展设计与实现,更便于将运行时系统、工具链以及后续渲染扩展放在统一平台中加以组织。
|
||||
|
||||
在现代实时渲染中,云、雾、烟、火等体积特效已经成为常见而重要的视觉元素。与基于表面的传统渲染不同,体积渲染需要处理光线在参与介质中的吸收、散射和透射率累积过程,通常伴随着大量采样和较高的计算开销。特别是在实时应用环境下,如何在有限的帧长内兼顾体积效果的空间层次感、光照表现和运行效率是极具价值的技术问题。也正因如此,体积渲染既具有较强的理论背景,也具有较高的工程实现价值。
|
||||
在现代实时渲染中,云、雾、烟、火等体积特效已经成为常见且重要的视觉元素。与基于表面的传统渲染不同,体积渲染需要处理光线在参与介质中的吸收、散射和透射率累积过程,通常伴随着大量采样和较高的计算开销。特别是在实时应用场景下,如何在有限的帧长中兼顾体积特效的空间层次感、光照表现和运行效率是极具价值的技术问题。也正因如此,体积渲染既具有较强的理论背景,也具有较高的工程实现价值。
|
||||
|
||||
在体数据表达方面,OpenVDB 为稀疏体积数据组织提供了成熟思路,而 NanoVDB 通过线性化数据结构进一步提升了 GPU 访问友好性,使其更适合实时渲染场景。与此同时,DirectX 12 提供了较底层的资源、命令和同步控制能力,便于开发者更直接地组织 GPU 数据上传、状态切换与渲染调度流程。将 NanoVDB 稀疏体数据组织方式与 DirectX 12 图形接口结合起来,不仅适合开展体积特效的实现研究,也能够作为扩展渲染引擎高级渲染能力的一条现实技术路径。
|
||||
在体数据表达方面,OpenVDB 为稀疏体积数据组织提供了成熟思路,而 NanoVDB 通过线性化数据结构进一步提升了 GPU 访问友好性,使其更适合实时渲染场景。与此同时,DirectX 12 提供了较底层的渲染资源、命令和同步控制能力,便于开发者更直接地组织 GPU 数据上传、状态切换与渲染调度流程。将 NanoVDB 稀疏体数据组织方式与 DirectX 12 图形接口结合起来,不仅适合开展体积渲染的理论研究,也能够作为扩展渲染引擎高级渲染特性的技术路径。
|
||||
|
||||
## 1.2 课题意义
|
||||
|
||||
从体积渲染理论与实现的角度看,本课题围绕 NanoVDB 稀疏体数据在 DirectX 12 环境下的实时渲染展开,重点关注体数据加载、GPU 访问、光线步进、空域跳过和体积阴影等关键问题。相关工作的完成,有助于为云、雾、烟等体积特效的实时工程实现提供一条较清晰的技术路径,也有助于加深对参与介质渲染、体绘制方程简化以及性能优化方法的理解。
|
||||
从体积渲染理论与实现的角度看,本课题围绕 NanoVDB 稀疏体数据在 DirectX 12 环境下的实时渲染展开,重点关注体数据加载、GPU 访问、光线步进、空域跳过和体积阴影等关键问题。相关工作的完成,有助于为云、雾、烟等体积特效实时渲染的工程实现提供一条较清晰的技术路径,也有助于加深对参与介质渲染、体渲染方程数值化以及性能优化方法的理解。
|
||||
|
||||
从系统设计与工程实践的角度看,本课题在体积渲染实现之外,还涵盖了渲染引擎主体架构、运行时模块和编辑器工作流的设计与实现。当前项目已经形成了包含 RHI 抽象、资源系统、场景与组件系统、渲染主链、材质与光照、C# 脚本系统以及编辑器工具链在内的主体框架,体积渲染是在这一基础上的重要高级扩展。这体现了渲染引擎开发中的模块协同关系,同时满足了系统性、完整性和可扩展性的要求。
|
||||
从系统设计与工程实践的角度看,本课题在体积渲染实现之外,还涵盖了渲染引擎主体架构、运行时模块和编辑器工作流的设计与实现。当前项目已经形成了包含 RHI 抽象、资源缓存系统、场景与组件系统、渲染主链、材质与光照、C# 脚本系统以及编辑器工具链在内的主体框架,体积渲染是在这一基础上的重要高级扩展。这体现了渲染引擎开发中的模块协同关系,同时满足了系统性、完整性和可扩展性的要求。
|
||||
|
||||
## 1.3 本课题的主要内容
|
||||
|
||||
结合当前项目的实际进展,本文的研究与实现内容主要由渲染引擎主体部分和体积渲染扩展部分两方面构成。
|
||||
本文的研究与实现内容主要由渲染引擎主体部分和体积渲染扩展部分两方面构成。
|
||||
|
||||
渲染引擎主体部分围绕运行时系统与编辑器工作流展开。在运行时层面,项目已经建立起平台层、图形接口抽象层、资源系统、场景与组件系统、渲染主链、模型与材质系统、多光源与简单阴影、C# 脚本运行时等核心模块,能够支持基础场景的组织、加载与实时渲染。在编辑器层面,项目已经形成 Scene 视口、Game 视口、Hierarchy、Inspector、Project、Console 等主要界面,并提供对象拾取、轮廓高亮、网格显示、变换 Gizmo 以及脚本构建与重载等辅助能力,具备较完整的开发与调试闭环。
|
||||
渲染引擎主体部分围绕运行时系统与编辑器工作流展开。在运行时层面,项目已经建立起平台层、图形接口抽象层、资源缓存系统、场景与组件系统、渲染主链、模型与材质系统、多光源与简单阴影、C# 脚本运行时等核心模块,能够支持基础场景的组织、加载与实时渲染。在编辑器层面,项目已经形成 Scene 视口、Game 视口、Hierarchy、Inspector、Project、Console 等主要界面,并提供对象拾取、轮廓高亮、网格显示、变换 Gizmo 以及脚本构建与重载等辅助能力,具备较完整的开发与调试闭环。
|
||||
|
||||
体积渲染扩展部分建立在现有引擎主体之上,重点研究参与介质渲染的基本理论以及 NanoVDB 稀疏体数据在 DirectX 12 环境下的工程实现方式。其核心内容包括体积渲染基本物理量分析、体绘制方程的简化理解、光线步进流程、稀疏体数据加载与 GPU 上传、Shader 侧体数据访问、空域跳过优化和体积阴影等。
|
||||
体积渲染扩展部分建立在现有引擎主体之上,重点研究参与介质渲染的基本理论以及 NanoVDB 稀疏体数据在 DirectX 12 环境下的工程实现方式。其核心内容包括体积渲染基本物理量分析、体绘制方程的简化理解、光线步进流程、稀疏体数据加载与 GPU 上传、Shader 侧体数据访问、空域跳过优化和体积阴影等。成功完成了基于 NanoVDB 的体积渲染管线,支持了云、雾、烟等体积特效的实时渲染。
|
||||
|
||||
## 1.4 本文的主要工作
|
||||
|
||||
围绕上述目标,本文已开展并完成的主要工作如下。
|
||||
|
||||
1. 完成了渲染引擎总体架构的设计与模块划分,构建了平台层、RHI 层、资源与场景层、渲染层、脚本层和编辑器层之间的基本组织关系。
|
||||
2. 实现了渲染引擎运行时主体能力,完成了缓冲、纹理、资源视图、管线状态、交换链等核心图形对象封装,并在此基础上建立了渲染请求规划、场景提取和相机执行等主链流程。
|
||||
3. 实现了资源导入与管理、场景与组件组织、OBJ 模型渲染、材质系统、多光源和简单阴影等基础渲染能力,使引擎具备了较完整的场景渲染闭环。
|
||||
2. 实现了渲染引擎运行时主体能力,完成了图形缓冲、纹理、资源视图、管线状态、交换链等核心图形对象封装,并在此基础上建立了渲染请求规划、场景提取和相机执行等主链流程。
|
||||
3. 实现了资源导入与缓存管理、场景与组件组织、OBJ 模型渲染、材质系统、多光源和简单阴影等基础渲染能力,使引擎具备了较完整的场景渲染闭环。
|
||||
4. 实现了基于 Mono 的 C# 脚本系统以及编辑器工作界面,支持脚本程序集构建、脚本运行时装载、Scene/Game 视口显示、Hierarchy 与 Inspector 联动、Project 资源浏览和 Console 调试输出等功能。
|
||||
5. 完成了基于 NanoVDB 的体积渲染管线,实现 `.nvdb` 数据加载、GPU Buffer 上传、HLSL 侧 PNanoVDB 访问、光线步进、HDDA 跳空和体积阴影等流程。
|
||||
5. 完成了基于 NanoVDB 的体积渲染管线,实现 .nvdb 数据加载、GPU Buffer 上传、HLSL 侧 PNanoVDB 访问、光线步进、HDDA 跳空和体积阴影等流程。
|
||||
|
||||
## 1.5 论文结构安排
|
||||
|
||||
全文共分为九章,各章安排如下。
|
||||
|
||||
1. 第1章为绪论,主要说明课题背景、课题意义、本文的主要内容、已完成的主要工作以及全文结构安排。
|
||||
2. 第2章介绍渲染引擎相关技术基础,为后续引擎架构设计与核心模块实现提供技术铺垫。
|
||||
2. 第2章介绍渲染引擎发展历程、当前主流实时渲染引擎特点以及本课题渲染引擎概况,为后续引擎架构设计与核心模块实现做铺垫。
|
||||
3. 第3章介绍体积渲染理论基础,重点说明参与介质、透射率、体绘制方程、光线步进和稀疏体数据等内容。
|
||||
4. 第4章对渲染引擎总体架构进行设计说明,给出系统分层、模块划分、数据流关系以及体积渲染模块在整体架构中的位置。
|
||||
5. 第5章围绕渲染引擎核心模块展开,说明 RHI、资源系统、场景与组件系统、渲染主链、材质光照和脚本系统等关键内容的设计与实现。
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# 第三章 体积渲染理论基础
|
||||
|
||||
第二章已经从渲染引擎角度说明了运行时系统、资源系统、场景组织和编辑器工作流等基础内容。本章进一步转入体积渲染本身的理论部分,重点讨论参与介质、光在介质中的衰减与散射、体绘制方程、光线步进以及稀疏体数据与空域跳过等关键概念,为后续基于 NanoVDB 的体积渲染管线设计与实现提供充足的理论基础。
|
||||
第二章已经对实时渲染引擎进行了详细的介绍,包括渲染引擎发展历程、当前主流实时渲染引擎特点以及本课题渲染引擎概况等。本章进一步转入体积渲染本身的理论部分,重点讨论参与介质、光在介质中的衰减与散射、体绘制方程、光线步进以及稀疏体数据与空域跳过等关键概念,为后续基于 NanoVDB 的体积渲染管线设计与实现提供充足的理论基础。
|
||||
|
||||
## 3.1 参与介质与体积渲染基本概念
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
传统表面渲染主要关心光线在物体表面处发生的反射与折射,而体积渲染关注的对象则是空间中的参与介质(Participating Media)。所谓参与介质,是指光在传播过程中会与其内部粒子持续发生相互作用的介质,例如云、雾、烟、火焰、水汽等。这类介质并不像三角形网格那样只在边界上发生光学变化,而是会在体积内部对光线产生吸收、散射甚至发射作用,因此它们的成像过程天然具有空间累积特征。
|
||||
|
||||
从工程角度看,参与介质通常可以分为均匀介质和非均匀介质两类。均匀介质在空间中的光学参数保持不变,便于分析和推导;非均匀介质的密度或光学参数随位置变化,更接近真实云层和烟雾的外观,也是实际应用中更常见的情况。在本文项目后续的体积渲染实现中,体数据本质上就是对这种空间变化进行离散表达,因此理解参与介质的基本性质是后续实现的前提。
|
||||
从工程角度看,参与介质通常可以分为均匀介质和非均匀介质两类。均匀介质在空间中的光学参数保持不变,便于分析和推导;非均匀介质的密度或光学参数随位置变化,更接近真实云层和烟雾的外观,也是实际应用中更常见的情况。在本文项目后续的体积渲染实现中,稀疏体数据本质上就是对这种空间变化进行离散表达,因此理解参与介质的基本性质是后续实现的前提。
|
||||
|
||||
### 3.1.2 吸收、散射与透射率
|
||||
|
||||
@@ -18,15 +18,13 @@ $$
|
||||
\sigma_t = \sigma_a + \sigma_s
|
||||
$$
|
||||
|
||||
在很多实时体积渲染实现中,密度场会被用来调制这些系数,使介质在空间上的不透明度和亮度分布发生变化。也就是说,密度并不是与吸收、散射并列的第三类光学现象,而更像是对局部光学参数的空间缩放。密度越大,局部吸收和散射通常也越强;密度越小,介质对光的影响则越弱。
|
||||
在很多实时体积渲染实现中,密度场会被用来调制这些系数,使介质在空间上的不透明度和亮度分布发生变化。密度值是对局部光学参数的空间缩放,密度越大,局部吸收和散射通常也越强;密度越小,介质对光的影响则越弱。
|
||||
|
||||
透射率(Transmittance)描述的是光线在介质中传播一段距离后仍然保留下来的比例,其取值范围在 0 到 1 之间。透射率越接近 1,说明光几乎未被削弱;透射率越接近 0,说明光在介质中已被显著衰减。透射率是体积渲染中极为核心的量,因为无论是背景光穿过体积后的结果,还是光源传播到采样点的有效光照,最终都要依赖透射率来表达。
|
||||
|
||||
### 3.1.3 体积颜色形成的基本原因
|
||||
### 3.1.3 体积颜色的形成原因
|
||||
|
||||
体积图像的形成并不是单纯“给体素上色”,而是光在介质中传播、衰减与散射共同作用的结果。从观察者方向看,体积颜色主要来自两个来源。其一是背景或后方物体发出的光在穿过介质时被衰减后剩余的部分;其二是来自光源的入射光在介质内部发生散射后,被重新导向观察方向所产生的内散射贡献。当前项目后续实现的重点主要也集中在这两部分,即背景透射与单次散射近似。
|
||||
|
||||
如果介质本身还会主动发光,例如火焰、高温气体等,则还需要考虑发射项。不过从本文当前项目的体积模块实现状态来看,重点仍然放在云、烟等主要受吸收和散射影响的体数据渲染上,因此本章后续分析将以吸收、散射和透射率累积为主。
|
||||
体积图像的形成是光在介质中传播、衰减与散射共同作用的结果。从观察者方向看,体积颜色主要来自两个来源。其一是背景或体积后方物体发出的光在穿过介质时被衰减后剩余的部分;其二是来自光源的入射光在介质内部发生散射后,被重新导向观察方向所产生的内散射贡献。如果介质本身还会主动发光,例如火焰、高温气体等,则还需要考虑发射项。
|
||||
|
||||
## 3.2 比尔-朗伯定律与光线透射
|
||||
|
||||
@@ -104,39 +102,33 @@ $$
|
||||
|
||||
其中 $g \in [-1,1]$ 为非对称因子。当 $g=0$ 时退化为各向同性散射;当 $g>0$ 时表现为前向散射;当 $g<0$ 时表现为后向散射。后续第7章的工程实现虽然会采用简化形式,但相位函数这一思想仍然是构成单次散射项的理论基础。
|
||||
|
||||
### 3.3.4 为什么在实时系统中常采用简化模型
|
||||
|
||||
如果严格考虑多次散射、复杂相位函数、多个动态光源和高精度体数据积分,那么体积渲染的计算量会迅速增大,很难满足实时应用对帧率的要求。因此,实时系统通常会在若干环节上做简化:例如只考虑单次散射、采用固定步长或分层步进、使用简化相位函数、对阴影进行近似积分,或者在必要时对空区域进行跳过。
|
||||
|
||||
这种简化并不意味着理论被放弃,而是意味着在已知完整物理模型的前提下,有选择地保留最影响画面结果的部分。对于工程设计类项目而言,这种从完整理论到可实时实现之间的取舍非常重要。后续章节的体积模块实现也正是在这一原则下,选择了适合当前项目阶段的实时方案。
|
||||
|
||||
## 3.4 光线步进算法原理
|
||||
|
||||
### 3.4.1 Ray Marching 的基本思想
|
||||
|
||||
由于体绘制方程通常难以直接求得解析解,实际实现中往往采用数值积分近似,而光线步进(Ray Marching)就是其中最典型的方法。其基本思想是:先求出相机光线与体积包围区域的进入点和离开点,再把这段区间划分为若干个小步长,在每个采样点处估计局部密度、局部消光、局部散射贡献与透射率,最后将这些局部结果累积起来,近似连续积分。
|
||||
|
||||
若步长足够小,则每个小区间都可以视作局部均匀,这样就能用离散求和逼近连续积分。也正因为这一思想简单直接,光线步进非常适合作为体积渲染的工程实现起点。本文后续的 NanoVDB 体积原型同样以相机光线与体积边界求交为起点,然后在体积内部做离散采样累积。
|
||||
若步长足够小,则每个小区间都可以视作局部均匀,这样就能用离散求和逼近连续积分。也正因为这一思想简单直接,光线步进算法非常适合作为体积渲染的工程实现起点。本文后续的 NanoVDB 体积渲染同样以相机光线与体积边界求交为起点,然后在体积内部做离散采样累积。
|
||||
|
||||
### 3.4.2 正向步进与反向步进
|
||||
|
||||
按观察方向组织时,光线步进通常可以分为正向步进和反向步进两种。正向步进一般指从靠近相机的一侧向远处推进,也可理解为前向累积(front-to-back);反向步进则从远处向近处积分,也可理解为后向累积(back-to-front)。
|
||||
按观察方向分类,光线步进通常可以分为正向步进和反向步进两种。正向步进一般指从靠近相机的一侧向远处步进,也可理解为前向累积(front-to-back);反向步进则从远处向近处积分,也可理解为后向累积(back-to-front)。
|
||||
|
||||
从数值结果上看,两者都可以逼近同一个积分目标,但在工程实现上,正向步进往往更适合实时系统。原因在于,正向步进可以显式维护“当前剩余透射率”,当透射率已经很低时,后续更远处的采样贡献可以近似忽略,从而支持提前终止优化。反向步进虽然在某些推导上较直观,但不如正向步进方便做前端遮挡裁剪。当前项目后续的体积实现也更接近前向累积方式。
|
||||
从数值结果上看,两者都可以逼近同一个积分目标,但在工程实现上,正向步进往往更适合实时系统。原因在于,正向步进支持提前终止优化。反向步进虽然在推导上较直观,但不如正向步进方便做前端遮挡裁剪。本课题采用前向累积方式。
|
||||
|
||||
### 3.4.3 步长、最大步数与误差控制
|
||||
|
||||
步长是光线步进中的关键参数。步长越小,离散积分越接近连续积分,画面通常越平滑,细节也越稳定;但采样次数随之增加,运行开销也会显著变大。步长过大时,则容易出现条带感、细节丢失和阴影估计不稳定等问题。与步长相对应的另一个参数是最大步数,它决定了一条光线最多允许采样多少次,用于限制最坏情况下的开销。
|
||||
步长是光线步进中的关键参数。步长越小,离散累积越接近连续积分,画面通常越平滑,细节也越稳定;但采样次数随之增加,运行开销也会显著变大。步长过大时,则容易出现条带感、细节丢失和阴影估计不稳定等问题。与步长相对应的另一个参数是最大步数,它决定了一条光线最多允许采样多少次,用于限制最坏情况下的开销。
|
||||
|
||||
因此,实时体积渲染本质上是在“精度”和“性能”之间寻找平衡。工程上通常会根据体素分辨率、包围盒大小、屏幕分辨率以及目标帧率来选择合适步长,并在必要时为主光线和阴影光线设置不同的采样密度。这种参数平衡在后续测试章节中也会体现出来。
|
||||
|
||||
### 3.4.4 提前终止、抖动等常见优化
|
||||
### 3.4.4 提前终止和采样抖动优化
|
||||
|
||||
在体积积分过程中,并不是每一个采样点都同样重要,因此常会配合若干优化策略。最常见的一类是提前终止(Early Termination):当累计透射率已经低于某个阈值时,说明后续更远区域的贡献非常有限,此时可以直接结束当前光线步进。对于较浓的烟雾或高密度区域,这类优化能节省大量无效计算。
|
||||
|
||||
另一类常见方法是采样抖动(Jitter)。固定步长加固定采样起点容易带来规则性的条纹或分层感,而在初始采样位置上引入轻微随机偏移,可以打散这种结构化误差,使图像在视觉上更平滑。除此之外,还可以通过多分辨率步进、阴影光线使用更粗步长、分层包围盒裁剪等方式进一步优化。对于本文后续实现而言,提前终止和空域跳过具有更直接的工程意义。
|
||||
|
||||
## 3.5 稀疏体数据与空域跳过思想
|
||||
## 3.5 稀疏体数据与空域跳过
|
||||
|
||||
### 3.5.1 稠密体素网格的问题
|
||||
|
||||
@@ -146,11 +138,11 @@ $$
|
||||
|
||||
### 3.5.2 稀疏体数据结构的意义
|
||||
|
||||
稀疏体数据结构的核心思想,是只为真正包含有效信息的区域分配更细粒度的存储,而对大量空区域或均匀区域采用更粗层级的表示。OpenVDB 及其面向 GPU 的 NanoVDB 就属于这一类思路。它们通过层级化节点结构组织体数据,使得体素值访问不再局限于简单的三维数组索引,而是能够根据当前区域是否活跃、当前层级分辨率以及节点类型进行更有选择性的访问。
|
||||
稀疏体数据结构的核心思想是只为真正包含有效信息的区域分配更细粒度的存储,而对大量空区域或均匀区域采用更粗层级的表示。OpenVDB 及其面向 GPU 的 NanoVDB 就属于这一思路。它们通过层级化节点结构组织体数据,使得体素值访问不再局限于简单的三维数组索引,而是能够根据当前区域是否活跃、当前层级分辨率以及节点类型进行选择性访问。
|
||||
|
||||
对于实时渲染而言,NanoVDB 的价值尤其明显。它在保留 VDB 层级稀疏表达思想的基础上,对数据进行了线性化组织,使 GPU 更容易访问。这样一来,体数据不但可以以更紧凑的形式存储在显存中,还能够在 Shader 中配合层级遍历或辅助访问器实现更高效的采样与判断。后续第7章的工程实现,正是建立在这种 GPU 友好的稀疏表示之上。
|
||||
|
||||
### 3.5.3 空区域跳过对实时性的作用
|
||||
### 3.5.3 空区域跳的实时性优化
|
||||
|
||||
空域跳过(Empty Space Skipping)的目标,是尽量避免在无效区域上进行逐步长采样。其基本思想不是改变体绘制方程,而是在数值积分过程中快速定位“哪些区域值得细采样,哪些区域可以直接跳过”。如果能在光线进入空区域时一次跨过较长距离,而不是继续做多个低价值采样,那么体积渲染的实时性就会明显提升。
|
||||
|
||||
|
||||
@@ -1,24 +1,30 @@
|
||||
# 第五章 渲染引擎核心模块设计与实现
|
||||
|
||||
上一章已经对渲染引擎的总体分层、模块划分和数据流关系进行了说明。本章进一步下沉到运行时主体,围绕当前项目中已经形成的几个核心模块展开分析,重点说明这些模块在工程实现中的职责边界、关键数据结构和协同方式。结合现有代码实现,本章主要讨论 RHI 抽象层、资源系统、场景与组件系统、渲染主链、模型材质与着色器系统、多光源与简单阴影,以及 C# 脚本系统。它们共同构成了当前渲染引擎的主体能力,也是后续编辑器工作流和体积渲染模块接入的基础。
|
||||
上一章已经对渲染引擎的总体分层、模块划分和数据流关系进行了说明。本章进一步围绕当前项目的几个核心模块展开分析,重点说明这些模块在工程实现中的职责边界、关键数据结构和协同方式。结合现有代码实现,本章主要讨论 RHI 抽象层、资源系统、场景与组件系统、渲染主链、模型材质与着色器系统、多光源与简单阴影,以及 C# 脚本系统。它们共同构成了当前渲染引擎的主体能力,也是后续编辑器工作流和体积渲染模块接入的基础。
|
||||
|
||||
## 5.1 RHI 抽象层设计与实现
|
||||
|
||||
### 5.1.1 抽象目标与接口边界
|
||||
### 5.1.1 设计目标与总体思路
|
||||
|
||||
渲染引擎的底层必须直接面对图形后端差异。不同图形 API 在资源创建方式、命令提交模型、描述符组织形式以及状态切换机制上均存在明显区别。如果上层渲染模块直接依赖某一个后端实现,那么渲染主链、资源绑定和管线状态组织都将与具体平台高度耦合,不利于后续扩展和维护。因此,本项目在底层建立了 RHI(Rendering Hardware Interface)抽象层,将图形设备能力统一为一组稳定接口,使上层模块更多围绕“渲染什么”和“如何组织渲染阶段”来展开,而不是反复处理后端 API 细节。
|
||||
RHI 的设计目标主要有两个。其一,是在 `D3D12`、`OpenGL` 和 `Vulkan` 三套后端之上建立统一接口,使上层渲染模块不再直接依赖具体图形 API;其二,是为资源系统、材质系统、渲染主链和后续高级渲染特性的接入提供一致的底层语义,屏蔽不同后端的差异。
|
||||
|
||||
从当前实现看,`RHIDevice` 是这一抽象层的核心入口。它统一提供缓冲、纹理、交换链、命令列表、命令队列、着色器、管线状态、管线布局、同步栅栏、采样器、渲染通道、帧缓冲、描述符池、描述符集以及各类资源视图的创建接口。这样一来,渲染层在组织离屏纹理、深度表面、阴影图、材质资源绑定和绘制命令时,都可以基于统一对象模型展开。
|
||||
从设计思路看,当前 RHI 遵循“求同存异、分层抽象、特性降级、底层逃逸”的原则。所谓求同存异,是优先提取不同图形 API 在资源、命令、状态与同步层面的共性能力,并把差异控制在后端内部;所谓分层抽象,是把上层渲染流程与底层 API 实现隔开,使渲染管线层只面向统一接口编写;所谓特性降级,是通过硬件能力查询接口把不同后端与设备的能力差异转化为上层可判断、可选择的运行条件;所谓底层逃逸,则是保留原生设备或原生句柄访问入口,以便在调试、扩展或特殊场景下直接调用底层对象。这样设计后,RHI 既不会因为过度抽象而失去实际可用性,也不会因为直接暴露底层细节而破坏整个引擎的模块边界。
|
||||
|
||||
### 5.1.2 多后端统一封装方式
|
||||
### 5.1.2 统一控制模型
|
||||
|
||||
当前项目的 RHI 已经形成了多后端组织结构。在工程构建层面,`engine/CMakeLists.txt` 中已经纳入了 `D3D12`、`OpenGL` 和 `Vulkan` 三套后端实现,以及与之对应的缓冲、纹理、资源视图、交换链、命令队列、命令列表、描述符、管线状态和截图支持等对象。对应地,`RHIFactory` 负责根据 `RHIType` 或字符串名称创建目标后端设备,从而将设备实例化过程与上层运行逻辑解耦。
|
||||
当前项目中的 RHI 抽象并不是把不同后端的接口简单套上一层统一名称,而是围绕一套完整的显式控制模型展开。其起点是统一的资源描述方式。无论是缓冲、纹理、交换链,还是着色器、管线状态和资源视图,上层都先通过统一描述结构表达“需要什么样的 GPU 对象”,然后再交由具体后端完成实例化。这样做的意义在于,把资源需求先从具体 API 中抽离出来,使资源系统、材质系统和渲染主链在进入底层之前就能围绕同一种对象语义组织数据,而不是在不同后端之间来回切换思维方式。
|
||||
|
||||
这种设计并不是简单追求“支持多个 API”,更重要的是建立统一的资源语义。例如,上层不再分别讨论 D3D12 的描述符堆、OpenGL 的纹理单元或 Vulkan 的描述符集布局,而是通过统一的缓冲、纹理、采样器、描述符集和资源视图概念组织资源绑定;同样,上层也不直接处理各后端的原生命令对象,而是通过命令队列和命令列表接口完成绘制、状态切换与结果提交。当前项目的体积渲染研究阶段以 D3D12 为重点推进,但从引擎主体架构看,多后端 RHI 已经为引擎保留了较好的平台弹性。
|
||||
在资源被创建出来之后,RHI 进一步把资源状态、描述符绑定、管线布局、命令录制与提交、渲染通道与帧缓冲组织成一条连续的控制链。资源状态解决的是“当前资源处于什么用途、下一步将被怎样访问”的问题;描述符绑定与管线布局解决的是“资源如何进入着色器、以什么绑定关系参与绘制或计算”的问题;命令列表与命令队列解决的是“这些状态和资源按什么顺序被提交给 GPU 执行”的问题;渲染通道与帧缓冲则进一步规定“本次执行向哪些目标输出、怎样装载和保存结果”。经过这层整理,场景绘制、阴影贴图、对象 ID 输出、离屏渲染和后处理就不再是零散的 API 调用,而是可以被统一组织和复用的阶段化流程。
|
||||
|
||||
### 5.1.3 RHI 对上层模块的支撑作用
|
||||
这套控制模型本质上更接近 `D3D12` 和 `Vulkan` 这类高级图形 API 的设计取向,因为它们天然强调资源、状态、绑定、命令和同步的显式管理。与此同时,RHI 还通过能力查询把不同硬件和后端的差异转化为上层可判断的运行条件,使某些高级特性能够根据实际能力选择启用、降级或关闭。也正因为如此,RHI 在当前引擎中承担的并不是单纯的“后端适配”职责,而是为整个渲染系统建立一套统一、可控、可扩展的底层执行模型。
|
||||
|
||||
RHI 抽象层在整个引擎中承担的是“运行时图形基础设施”的角色。资源系统最终生成的网格、纹理、材质常量和着色器变体,都需要落到 RHI 对象上;渲染主链中的主场景绘制、阴影绘制、对象 ID 绘制、后处理和最终输出,也都依赖 RHI 提供的离屏表面、命令提交和资源状态切换能力;编辑器视口同样是通过对渲染表面的申请和复用接入渲染主链的。可以说,RHI 为整个渲染引擎提供了统一而稳定的底层执行面,是后续所有高层模块成立的前提。
|
||||
### 5.1.3 显式控制模型的后端落地
|
||||
|
||||
从实际实现看,当前 RHI 的整体结构明显以 `D3D12` 和 `Vulkan` 这类显式图形 API 为主干。无论是资源描述到对象创建的转换,还是资源状态切换、描述符绑定、管线布局、命令列表录制、命令队列提交以及渲染通道组织,这些抽象都天然更贴近显式 API 的工作方式。换句话说,这套 RHI 并不是先从三个后端求一个平均值,再拼出一层抽象;它首先确立的是一种偏高级 API 的控制模式,再让不同后端向这套模式靠拢。
|
||||
|
||||
在这种前提下,`D3D12` 和 `Vulkan` 的接入相对直接,因为它们本身就具备较强的显式控制特征。相比之下,`OpenGL` 属于典型的隐式状态机 API,本身并不天然提供与描述符集、管线布局、显式同步和阶段化渲染通道完全对应的机制。如果直接按照 OpenGL 的原生使用方式暴露给上层,那么整个 RHI 统一抽象就会迅速失去约束力。为了解决这一问题,当前项目在 OpenGL 后端中做了较多模拟实现,把原本分散在上下文状态中的绑定、同步和资源使用过程重新整理为接近显式 API 的形式。例如,在资源绑定层,通过绑定点映射、纹理单元分配和统一缓冲管理来模拟描述符集与管线布局;在同步层,通过 `GLsync` 配合 CPU 侧计数方式适配统一的 Fence 语义;在渲染输出层,则通过帧缓冲和渲染通道封装,把 OpenGL 的输出过程纳入与其他后端一致的阶段化组织方式。
|
||||
|
||||
因此,三套后端虽然共同服务于同一套 RHI 接口,但它们在这套模型中的角色并不完全对称。`D3D12` 和 `Vulkan` 更像是这套显式控制模型的直接承载者,`OpenGL` 则是在保留自身底层实现的同时,通过模拟与适配被收束到同一框架之中。对上层模块而言,这种差异被有效屏蔽,资源系统、渲染主链、材质绑定和编辑器视口都可以围绕统一的执行语义工作;而对底层实现而言,这又允许不同后端保留各自的实现特点。RHI 的真正价值也正体现在这里,即以统一的显式控制模型支撑整个渲染引擎,同时把后端差异压缩在可管理的范围之内。
|
||||
|
||||
## 5.2 资源系统设计与实现
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
# 第六章 编辑器与引擎工作流设计与实现
|
||||
|
||||
上一章已经围绕渲染引擎运行时的核心模块展开分析,说明了底层图形抽象、资源管理、场景组织、渲染主链以及脚本系统的实现方式。在此基础上,本章进一步转向引擎的可视化界面与工作流部分,重点说明当前项目中的编辑器如何围绕场景编辑、资源浏览、参数调整、脚本运行和调试输出等功能组织起来。对于本课题而言,编辑器是连接资源系统、场景系统、渲染系统和脚本系统的直接入口,也是展示整个系统功能的重要部分。
|
||||
上一章已经围绕渲染引擎运行时的核心模块展开分析,说明了各个模块的实现方式。在此基础上,本章进一步转向引擎的可视化界面与工作流部分,重点说明当前项目中的编辑器如何围绕场景编辑、资源浏览、参数调整、脚本运行和调试输出等功能组织起来。对于本课题而言,编辑器是连接资源系统、场景系统、渲染系统和脚本系统的直接入口,也是展示整个系统功能的重要部分。
|
||||
|
||||
## 6.1 编辑器在引擎中的定位
|
||||
|
||||
当前项目中的编辑器由 `Application`、`EditorLayer`、`EditorWorkspace` 以及各类面板共同构成,其初始化过程直接建立在现有引擎能力之上。编辑器启动后,会依次完成窗口渲染器初始化、项目根目录设置、`ResourceManager` 与脚本运行时初始化、ImGui 后端桥接以及视口离屏渲染资源接管,随后进入逐帧更新与绘制阶段。从系统定位看,编辑器并不是独立于引擎主体之外的附加程序,而是直接复用资源系统、渲染链路、场景数据和脚本运行时能力的可视化工作界面。它承担的核心任务包括场景编辑、资源管理、渲染结果验证以及脚本与调试支撑,并通过 `Scene`、`Game`、`Hierarchy`、`Inspector`、`Project` 和 `Console` 等主要界面组织这些工作。
|
||||
当前项目中的编辑器由 `Application`、`EditorLayer`、`EditorWorkspace` 以及各类面板共同构成,其初始化过程直接建立在现有引擎能力之上。编辑器启动后,会依次完成窗口渲染器初始化、项目根目录设置、`ResourceManager` 与脚本运行时初始化、ImGui 后端桥接以及视口离屏渲染资源接管,随后进入逐帧更新与绘制阶段。编辑器是直接复用资源系统、渲染链路、场景数据和脚本运行时能力的可视化工作界面,它承担的核心任务包括场景编辑、资源管理、渲染结果验证以及脚本与调试支撑,并通过 `Scene`、`Game`、`Hierarchy`、`Inspector`、`Project` 和 `Console` 等主要面板组织这些工作。
|
||||
|
||||
编辑器与运行时之间的关系并不是简单的界面展示关系,而是围绕统一上下文形成了完整闭环。`EditorContext` 负责组织事件总线、选择管理器、场景管理器、项目管理器、撤销管理器和视口宿主服务,使不同面板之间的数据与事件能够保持联动;`EditorWorkspace` 负责在运行阶段组织菜单栏、停靠布局和各类面板的更新与绘制;`PlaySessionController` 则负责连接编辑态与运行态,在进入播放模式前保存场景快照、停止播放后恢复快照。这样一来,编辑器既能够把 `Game` 视口中的脚本逻辑和运行时场景更新真实执行出来,又能够在退出运行后回到稳定的编辑状态,因此它构成了当前渲染引擎工作流中的核心组织层。
|
||||
|
||||
|
||||
@@ -1,22 +1,20 @@
|
||||
# 第四章 渲染引擎总体架构设计
|
||||
|
||||
前两章已经分别给出了渲染引擎概述和体积渲染理论基础。在此基础上,本章从系统设计层面对当前项目中的渲染引擎进行说明,重点讨论引擎设计目标、总体架构与模块划分、模块之间的协同关系,以及体积渲染模块在整体架构中的位置。本章的任务是把整个系统的组织方式讲清楚,为后续第5章、第6章和第7章的具体实现分析建立统一框架。
|
||||
前两章已经分别给出了渲染引擎概述和体积渲染理论基础。在此基础上,本章从系统设计层面对当前项目中的渲染引擎进行说明,重点讨论引擎设计目标、总体架构与模块划分、模块之间的协同关系,以及体积渲染模块在整体架构中的位置。本章的任务是说明整个系统的组织方式,为后续第5章、第6章和第7章的具体实现分析建立统一框架。
|
||||
|
||||
## 4.1 引擎设计目标
|
||||
|
||||
本项目的架构设计以渲染引擎为主体展开,目标是在统一框架下组织图形接口、资源管理、场景组织、渲染执行、脚本扩展和编辑器工作流等能力。这样的架构安排使系统能够围绕同一套运行时基础持续扩展,并将图形绘制、资源处理和工具能力组织在同一体系中。当前项目不仅包含运行时系统,也包含围绕资源浏览、场景编辑、参数调整和结果验证构建的编辑器工作流。
|
||||
|
||||
渲染引擎的架构设计不仅服务于当前已完成的基础能力,也需要为后续扩展保留空间。体积渲染作为当前项目中最重要的高级渲染扩展,需要依附既有资源系统、渲染主链和编辑器验证能力接入系统。因此,在总体架构层面,需要预留新资源类型、新渲染阶段和新调试入口能够自然接入的位置,使扩展能够沿既有资源、渲染和验证链路进入系统。
|
||||
本项目的架构设计以渲染引擎为主体展开,目标是在统一框架下组织图形硬件接口、资源管理、场景组织、渲染执行、脚本扩展和编辑器工作流等基础功能。同时体积渲染作为当前项目中最重要的高级渲染扩展,需要依附既有资源系统、渲染主链和编辑器验证能力接入系统。因此,在总体架构层面,需要兼顾基础功能完善和可扩展性。
|
||||
|
||||
## 4.2 引擎总体架构与模块划分
|
||||
|
||||
结合当前项目的代码结构和功能组织方式,本文将渲染引擎总体概括为平台层、图形接口抽象层、资源与场景层、渲染组织层以及脚本与编辑器层五个主要层级。在此之外,系统中还存在内存、线程、调试、音频和 UI 等支撑模块,但从论文主体展开角度看,上述五层构成了最核心的结构主线。围绕这一分层结构,引擎主体的核心模块可以进一步归纳为 `RHI`、`Resources / Assets`、`Scene / Components`、`Rendering`、`Scripting` 和 `Editor` 六个部分,它们分别落在不同层次上,共同构成当前系统的主体架构。
|
||||
本文将渲染引擎总体概括为平台层、图形接口抽象层、资源与场景层、渲染组织层以及脚本与编辑器层五个主要层级。在此之外,系统中还存在内存、线程、调试和 UI 等支撑模块,但从论文主体展开角度看,上述五层构成了最核心的结构主线。围绕这一分层结构,引擎主体的核心模块可以进一步归纳为 `RHI`、`Resources / Assets`、`Scene / Components`、`Rendering`、`Scripting` 和 `Editor` 六个部分,它们分别落在不同层次上,共同构成当前系统的主体架构。
|
||||
|
||||
【插图:渲染引擎总体分层架构】
|
||||
|
||||
### 平台层
|
||||
|
||||
平台层位于整个系统的底部,负责窗口、消息循环、输入处理、时间管理和文件系统访问等基础运行环境。它决定了主循环如何驱动系统运行,也决定了渲染结果最终如何呈现到设备屏幕上。平台层本身不直接组织场景和资源,但它为上层所有模块提供统一的执行环境。
|
||||
平台层位于整个系统的底部,负责窗口、消息循环、输入处理、时间管理和文件系统访问等基础运行环境。它决定了主循环如何驱动系统运行,也决定了渲染结果最终如何呈现到设备屏幕上。平台层本身不直接组织场景和资源,但它为上层所有模块提供统一的执行环境。本课题项目主要建立在 Windows 平台上,同时也考虑到了跨平台的扩展性。
|
||||
|
||||
### 图形接口抽象层
|
||||
|
||||
@@ -40,7 +38,7 @@
|
||||
|
||||
### 4.3.1 资源从工程目录进入运行时的路径
|
||||
|
||||
工程目录中的模型、纹理、材质、着色器和场景文件首先经过资源系统的扫描、导入和缓存,随后被装载为运行时可直接使用的资源对象。这些资源再进一步被场景中的对象和组件引用,最终参与渲染和逻辑更新。由此形成从工程资源到运行时内容的第一条主线。
|
||||
工程目录中的模型、纹理、材质、着色器、稀疏体积数据和场景文件首先经过资源系统的扫描、导入和缓存,随后被装载为运行时可直接使用的资源对象。这些资源再进一步被场景中的对象和组件引用,最终参与渲染和逻辑更新。由此形成从工程资源到运行时内容的第一条主线。
|
||||
|
||||
### 4.3.2 场景状态到渲染数据的转换关系
|
||||
|
||||
@@ -62,6 +60,6 @@
|
||||
|
||||
## 4.5 本章小结
|
||||
|
||||
本章从系统设计层面对当前项目中的渲染引擎进行了分析,明确了引擎架构的主要目标,包括形成完整工作框架、建立基础场景渲染闭环、兼顾运行时能力与编辑器工作流,以及为高级渲染特性扩展预留空间。在此基础上,将系统概括为平台层、图形接口抽象层、资源与场景层、渲染组织层以及脚本与编辑器层五个主要层级,并在同一结构下说明了 `RHI`、`Resources / Assets`、`Scene / Components`、`Rendering`、`Scripting` 和 `Editor` 六个核心模块在整体架构中的归属关系。
|
||||
本章从系统设计层面对本课题的渲染引擎进行了分析,明确了引擎架构的主要目标,并将系统概括为平台层、图形接口抽象层、资源与场景层、渲染组织层以及脚本与编辑器层五个主要层级,并在同一结构下说明了 `RHI`、`Resources / Assets`、`Scene / Components`、`Rendering`、`Scripting` 和 `Editor` 六个核心模块在整体架构中的归属关系。
|
||||
|
||||
同时,本章还从资源进入运行时、场景状态转化为渲染数据、编辑器视口接入渲染主链以及脚本驱动场景与渲染结果联动四个角度说明了模块之间的协同关系,并明确了体积渲染模块在总体架构中的位置。基于这一总体架构,下一章将进一步转入渲染引擎核心模块设计与实现的具体分析。
|
||||
|
||||
Reference in New Issue
Block a user