Files
XCEngine/docs/api/_guides/Rendering/Scene-Extraction-And-Builtin-Forward-Pipeline.md

6.4 KiB
Raw Blame History

Scene Extraction And Builtin Forward Pipeline

这条链路现在怎么工作

当前 XCEngine 的主场景渲染链路可以按下面的顺序理解:

  1. SceneRenderer 负责组织相机请求和执行顺序。
  2. CameraRenderer 选择主渲染管线;默认情况下,它通过 BuiltinForwardPipelineAsset 创建 BuiltinForwardPipeline
  3. RenderSceneExtractor 把 scene/game-object/component 结构压平成 RenderSceneData
  4. BuiltinForwardPipelineRenderSceneData 绘制到 RenderSurface

这里的关键点不是“有没有前向渲染”,而是场景数据和绘制数据已经被拆成两层:

  • 提取阶段负责从场景组织里收集相机、光照和可绘制项。
  • pipeline 阶段只消费已经整理好的 RenderSceneData

不过这里讲的只是主场景绘制槽位,不是整次相机提交的完整尾段。按 engine/src/Rendering/CameraRenderer.cpp 的当前实现,BuiltinForwardPipeline 前后还可能包着:

  • preScenePasses
  • object-id pass
  • postScenePasses
  • overlayPasses

Scene View 的网格、选中描边和 editor overlay当前就是先在 Editor 层组装成这些 request 级序列,再交给 CameraRenderer 执行。

RenderSceneData 现在包含什么

RenderSceneExtractor 当前产出的核心数据有三块:

  • cameracameraData
  • lighting
  • visibleItems

其中 visibleItems 的元素类型是 VisibleRenderItem。头文件里仍保留 VisibleRenderObject = VisibleRenderItem 的兼容别名,但当前实现和新文档都应以 visibleItems 为准,而不是旧说法 visibleObjects

每个 VisibleRenderItem 除了 mesh 和变换,还已经携带:

  • material
  • materialIndex
  • sectionIndex
  • hasSection
  • renderQueue
  • cameraDistanceSq

这让后续 pipeline 不需要回到场景层重新做组件查询。

为什么还要保留 scene extraction

游戏对象系统天然偏向层级、组件和编辑器语义;渲染系统天然偏向“当前相机要画哪些项”。RenderSceneExtractor 的价值就是把这两种数据形状解耦。

当前 extractor 已经做的事情包括:

  • 选择相机并生成 RenderCameraData
  • 提取主方向光数据
  • 递归收集满足基础绘制条件的可见项
  • 产出 visibleItems 并带上 material / section / distance 等渲染侧字段

它还没有做完整的商业级可见性系统,例如:

  • 视锥裁剪
  • 遮挡裁剪
  • instancing / batching
  • 透明排序主路径

BuiltinForwardPipeline 的当前定位

BuiltinForwardPipeline 现在是默认主渲染路径,但它内部已经不是旧的固定绑定模型,而是三层缓存:

  • (shader*, passName) 缓存 PassResourceLayout
  • (shader*, passName, render state) 缓存 RHIPipelineState
  • (passLayout, setIndex, objectId, material) 缓存动态 descriptor set

此外它已经接入 RenderPassSequence

  • 构造时注册 BuiltinForwardOpaquePass
  • Initialize() 走 sequence 生命周期
  • Render() 先构造 RenderPassContext,再执行 sequence
  • Shutdown() 也走 sequence再由 opaque pass 回调资源清理

所以当前 builtin forward 更像“一条默认可运行的 pass-sequence 管线”,而不是过去那种把所有逻辑揉在一个固定 layout 里的实现。

Shader pass 资源契约

当前 builtin forward 对 shader pass 的资源声明有明确白名单。可识别语义只有:

  • PerObject:必需,单个 CBV
  • Material:可选,单个 CBV
  • BaseColorTexture:可选,单个 Texture2DTextureCube
  • LinearClampSampler:可选,单个 sampler

如果 shader pass 没有声明 resources,则会回退到 legacy builtin forward 绑定:

  • set 1 binding 0 -> PerObject
  • set 2 binding 0 -> Material
  • set 3 binding 0 -> BaseColorTexture
  • set 4 binding 0 -> LinearClampSampler

如果声明不合法,例如未知语义、同 set 混用 sampler 和非 sampler、重复 binding 或缺少 PerObjectpipeline 会拒绝为该 pass 创建布局。

测试 tests/Rendering/unit/test_builtin_forward_pipeline.cpp 还明确校验了 builtin shader ForwardLit pass 的这组资源契约,以及 BuildInputLayout() 现在使用 POSITION=float3

单个可见项如何被画出来

BuiltinForwardOpaquePass 遍历 RenderSceneData::visibleItems 时,每个物体大致会经历以下步骤:

  1. 根据材质解析真正要用的 shader/pass没有合适 pass 时回退到 builtin forward shader。
  2. 取得 mesh 的 GPU 资源缓存。
  3. 生成 PerObjectConstantsPerMaterialConstants
  4. 获取或创建当前 shader pass 的 PassResourceLayout
  5. 逐 set 绑定 descriptor
    • PerObject / Material / Texture 的 set 走动态缓存。
    • 只含 sampler 的 set 走静态缓存。
  6. 按 section 或整 mesh 发出 draw call。

BaseColorTexture 如果解析不到,会回退到初始化阶段创建的 1x1 白色纹理 SRV。

RenderSurface 在这里的作用

RenderSurface 不是单纯的附件容器而是当前这一帧输出目标的完整描述。builtin forward 会消费它的:

  • 颜色附件
  • 深度附件
  • render area
  • clear color override
  • 自动状态切换开关
  • colorStateBefore / colorStateAfter

因此这条主渲染路径既能服务于窗口 back buffer也能服务于 editor viewport、离屏 RT 或其它自定义输出目标。

当前限制

  • 主场景默认只有 builtin forward opaque pass。
  • 透明、阴影、延迟渲染和更复杂的资源依赖管理还不在这条链路里。
  • RenderPassSequence 目前只提供顺序执行,不是 render graph。

相关 API