Files
XCEngine/docs/used/NanoVDB稀疏体积渲染正式集成计划_第一阶段完成归档_2026-04-09.md

22 KiB
Raw Blame History

NanoVDB 稀疏体积渲染正式集成计划Unity 对齐版)

文档日期2026-04-08

适用范围:当前 XCEngineResources / 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 中定义 StructuredBufferRawBuffer
  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. artifactLibrary/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 dataVisibleVolumeItem
  7. passBuiltinVolumetricPass

命名原则:

  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 已经能从正式场景数据中拿到体积对象输入

阶段 6BuiltinVolumetricPass 首次正式点亮

目标

在当前 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 能力集合

阶段 9Editor 最小工作流接入

目标

让这项能力进入项目工作流,而不是只能靠测试程序看图。

任务

  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都不会被今天的底层实现反噬。