【DX12龙书】第七章 利用Direct3D绘制几何体(续)
本章是 Direct3D 12 绘制体系的进阶核心内容,承接前序章节的基础绘制流程,构建了高性能、可复用的工业级渲染架构,完整补充了根签名的全量用法,实现了程序化 3D 几何体生成与 CPU 端动态顶点动画,为后续复杂场景渲染、特效实现奠定了核心框架。章节核心解决了 CPU-GPU 同步的性能瓶颈,规范了渲染数据的分层管理,同时完善了 Direct3D 12 核心资源的使用规则。
各小节内容概况
7.1 帧资源
本小节核心解决每帧 Flush 命令队列导致的 CPU-GPU 空闲与性能浪费问题。
-
问题根源:前序渲染流程中,每帧结束必须强制同步 CPU 与 GPU,等待 GPU 完成当前帧所有指令后才能重置命令分配器、更新常量缓冲,导致 CPU 和 GPU 频繁出现空闲,硬件利用率极低。
-
核心方案:设计环形帧资源数组(通常设置 3 个元素),每个帧资源封装了单帧渲染专属的、CPU 可修改的全部资源,包括:帧专属命令分配器、逐 Pass / 逐物体常量缓冲、标记帧指令执行进度的围栏值。
-
运行逻辑:CPU 无需等待当前帧 GPU 执行完成,即可循环取用数组中未被 GPU 占用的帧资源,提前处理后续 2 帧的资源更新、命令列表录制与提交;仅当 CPU 超过 GPU 处理进度 2 帧以上时,才需要等待 GPU 同步,确保 GPU 命令队列始终非空,最大化 CPU-GPU 并行利用率。
-
补充内容:讲解了帧资源的生命周期管理、基于围栏的帧同步实现细节,以及 CPU 空闲周期的复用逻辑(可用于 AI、物理等游戏逻辑计算)。
7.2 渲染项
本小节核心实现单物体绘制全量数据的标准化封装与管理,简化多物体场景的渲染逻辑。
-
渲染项定义:一个轻量级结构体,封装了提交一次完整绘制调用所需的全部参数,核心字段包括:物体世界矩阵、脏标记
NumFramesDirty、物体常量缓冲索引、关联的几何体数据指针、图元拓扑类型、DrawIndexedInstanced所需的索引计数、起始索引 / 顶点位置等。 -
核心价值:
-
统一管理场景中所有可绘制物体,将绘制逻辑与物体数据解耦;
-
支持多渲染项共享同一份几何体数据,减少显存冗余;
-
通过脏标记优化常量缓冲更新:仅当物体数据发生变化时,才会更新所有帧资源中对应的常量缓冲,避免无效的 CPU-GPU 数据传输。
-
-
工程实践:按管线状态对象(PSO)对渲染项进行分类管理(如不透明渲染项、透明渲染项),减少渲染过程中的管线状态切换,提升性能。
7.3 渲染 Pass 常量
本小节核心实现常量缓冲的按更新频率分组优化,减少常量缓冲的更新开销。
-
核心设计:将着色器使用的常量数据,按更新频率拆分为两类,分别对应独立的常量缓冲:
-
逐物体常量(Object Constants):每个物体独有,如世界矩阵,仅当物体发生位移、旋转、缩放时才需要更新;
-
逐 Pass 常量(Pass Constants):整个渲染 Pass 内所有物体共享,每帧仅需更新一次,包括视图 / 投影 / 视口投影矩阵及其逆矩阵、相机世界位置、渲染目标尺寸、近远平面、游戏总时间 / 帧间隔时间等。
-
-
工程实现:讲解了两类常量缓冲的结构体定义、CPU 端更新逻辑,以及对应的着色器代码、根签名配置修改;同时给出性能建议:着色器中使用的常量缓冲数量建议控制在 5 个以内,避免硬件性能损耗。
7.4 形状几何体
本小节核心实现基础 3D 几何体的程序化生成,提供可复用的几何体生成工具。
-
工具设计:封装
GeometryGenerator工具类,通过内嵌的MeshData结构体存储网格的完整数据,包括顶点属性(位置、法线、切线、UV 坐标)和索引数据,支持 16 位 / 32 位索引格式转换。 -
核心几何体生成算法:
-
网格(Grid):通过行列数细分生成平面网格,可用于地形、水面、平面场景;
-
圆柱体(Cylinder):支持上下底面自定义半径(可实现圆台、圆锥),通过切片数(Slice)和堆叠数(Stack)控制网格精度,分为侧面、顶面、底面三部分独立生成;
-
球体(Sphere):基于三角函数的环式生成算法,通过切片和堆叠数控制球面精度,适配不同的平滑度需求。
-
-
适用场景:生成的几何体可用于调试可视化、碰撞体展示、天空盒、地形基底、基础场景搭建等。
7.5 形状Demo
本小节将前 4 节的核心内容整合为完整可运行的渲染 Demo,落地全流程渲染架构。
-
核心实现内容:
-
帧资源的初始化、环形循环管理与围栏同步;
-
多渲染项的创建与管理,实现盒子、球体、圆柱体、网格等多个物体的同场景渲染,支持几何体数据共享;
-
逐 Pass / 逐物体常量缓冲的按帧更新逻辑,基于脏标记的优化实现;
-
适配新常量结构的根签名、管线状态对象(PSO)配置;
-
完整的渲染循环:命令列表录制、提交、帧同步的全流程实现。
-
7.6 细探根签名
本小节完整讲解 Direct3D 12 根签名的全量参数类型、配置规则与性能优化方案,补充前序章节未覆盖的核心用法。
-
根参数的三种完整类型
-
表格
-
核心配置规则
-
硬限制:根签名的总大小不得超过 64 个 DWORD;
-
着色器可见性:可限制根参数仅对特定着色器阶段可见(顶点 / 像素 / 几何 / 外壳 / 域着色器),减少不必要的资源广播,优化性能;
-
描述符范围配置:详细讲解了描述符类型、数量、起始着色器寄存器、寄存器空间、描述符偏移量的配置规则,支持寄存器空间的资源隔离。
-
-
性能优化与版本化机制
-
硬件会为每个绘制调用保存根参数的快照,因此应尽量减小根签名的大小,降低快照复制开销;
-
建议设计兼容多个 PSO 的通用根签名,减少渲染过程中的根签名切换,避免管线刷新带来的性能损耗;
-
校验规则:创建 PSO 时会强制校验根签名与着色器输入的匹配性,着色器使用的资源必须在根签名中声明,反之无强制要求。
-
7.7 地形与水波模拟
本小节核心实现CPU 端顶点动画与动态顶点缓冲的工程应用,是前序网格几何体的进阶延伸。
-
核心内容:
-
动态顶点缓冲:讲解了动态顶点缓冲的创建、CPU 内存映射、数据更新与 GPU 上传的完整流程,实现运行时顶点数据的实时修改;
-
地形生成:基于正弦函数叠加算法生成高度图,修改网格顶点的 Y 轴坐标,实现起伏的山地地形;
-
水波动画:通过时间变量驱动的正弦函数,实时修改水面网格的顶点位置、法线数据,实现动态波动的水面效果;
-
-
补充说明:分析了动态顶点缓冲的性能开销,明确其适用场景为顶点数据高频变化的动画效果,不适合静态几何体。
7.8 小结
对全章核心内容进行复盘,再次强调:
-
帧资源对 CPU-GPU 并行利用率的提升价值;
-
渲染项与常量分组对渲染架构的规范化作用;
-
根签名三类参数的用法、区别与性能优化原则;
-
动态顶点缓冲的实现逻辑与顶点动画的核心思路。
核心知识点列表
1. 渲染性能与 CPU-GPU 同步
-
帧资源(Frame Resources)的设计思想、环形数组实现与生命周期管理
-
基于围栏(Fence)的帧级同步机制,替代每帧 Flush 的优化方案
-
CPU-GPU 并行化渲染循环的设计,最大化硬件利用率
-
渲染资源的帧隔离设计,避免 GPU 执行过程中资源被 CPU 修改
2. 渲染数据管理架构
-
渲染项(Render Item)的封装设计,单绘制调用全量数据的标准化管理
-
按更新频率的常量缓冲分层设计:逐物体常量、逐 Pass 常量
-
脏标记(Dirty Flag)机制,优化常量缓冲的更新频率
-
多渲染项的分类管理,基于 PSO 的渲染分组优化
3. 程序化几何体生成
-
GeometryGenerator工具类与MeshData网格数据结构设计 -
网格、圆柱体、球体的程序化生成算法
-
顶点属性(位置、法线、切线、UV)的计算与索引缓冲的构建
-
几何体数据的复用与显存优化
4. 根签名进阶与优化
-
根签名三类根参数的区别、用法与适用场景
-
根签名的 64 DWORD 硬件限制,各参数的开销计算
-
着色器可见性的配置与性能优化
-
描述符范围与寄存器空间的配置规则
-
根签名的版本化机制,通用根签名的设计原则
-
根签名与着色器的校验规则
5. 动态顶点缓冲与顶点动画
-
动态顶点缓冲的创建、映射与更新全流程
-
CPU 端顶点动画的实现逻辑,基于时间的顶点数据修改
-
地形高度图生成算法,水波动画的数学实现
-
动态顶点缓冲的性能开销与适用场景边界
难点笔记
渲染架构
总体概述

Shape Demo的类图如上。
地形水面模拟Demo,因为需要按波动方程动态更新水面的顶点位置,所以顶点也被整合到帧资源中。
帧资源(FrameResource)
现在是单线程,游戏业务逻辑(App::Update)与渲染命令编制(App::Draw)是在主线程串行执行。
帧资源是一个长度为3的数组,通过对Index取模,实现类似循环列表。
在帧开始时,先去获取帧资源,并且检查它的FenceValue。如果帧资源是空闲的,即可进行当帧的工作;如果帧资源还在使用的状态,CPU执行将被阻塞。所以逻辑可以领先渲染3帧,这样是的CPU和GPU都在同时独立地不相互影响地工作。

商业引擎的Tick,都是多线程。像Unity,会拆分出负责业务逻辑的主线程,和1或n条用于编制渲染命令的渲染线程;而Unreal Engine,更是对渲染进行更细致拆分,有编制渲染命令的渲染线程和具体与图形API打交道的RHI线程。
几何体类(MeshGeometry)
Shape Demo中所有几何体(盒子、栅格、圆柱、球体),在ShapesApp::BuildShapeGeometry,打包为一个大的顶点/索引缓冲,对应一个MeshGeometry,设置到mGeometries中。
每个几何体对应一个SubmeshGeometry,里面记录顶点/索引偏移,可以独立绘制

Submesh的索引和顶点的信息,通过RenderItem,传递给ID3D12GraphicsCommandList的DrawIndexedInstanced进行绘制。但没有使用实例渲染,所以InstanceCount传入1,StartInstanceLocation传入0,即只画一个实例。
可以改进之处
通过RenderDoc截帧发现一些问题。
- 在视椎体外的Mesh的RenderItem也被传入管线进行绘制,经过顶点着色器后被剔除

可以通过软光栅、GPUScene等技术,在CPU侧或GPU侧进行剔除
- 虽然共用Mesh几何数据,但还是一个一个地渲染Mesh。

应该通过实例化渲染,这样能节省DC

浙公网安备 33010602011771号