[C++]学习《DirectX12 3D 游戏开发实战》 第八天 利用 Direct3D 绘制几何体(续)

本章将介绍一些此书后面常会用到的绘图模式。首先讲解与绘图优化相关的内容,此处涉及"帧资源 (frame resource)"等概念。若采用帧资源,我们就得修改程序中的渲染循环,好处:不必在每一帧都刷新命令队列,继而改善 CPU 和 GPU 的利用率。

接下来,我们会提出渲染项 (render item) 的概念,并解析如何基于更新频率来划分常量数据。

此外,我们将研究根签名的更多细节,并学习其他两种根参数类型:根描述符根常量

最后,我们还会展示怎样绘制更为复杂的物体。

完成本章的学习后,将能够绘制出形如山川的表面,还有圆台、球体以及模拟波浪运动的动画。

目标

  1. 学会一种无须每帧都要刷新命令队列的渲染流程,由此来优化程序的性能。

  2. 了解另外两种根签名参数类型:根描述符和根常量。

  3. 探索如何在程序中生成和绘制常见的几何体,如栅格、圆台和球体。

  4. 研究怎样通过动态顶点缓冲区来更新 CPU 中的顶点数据,并且向 GPU 上传顶点的新位置信息。

内容整理

  1. 帧资源

  2. 渲染项

  3. 渲染过程中所用到的常量数据

  4. 不同形状的几何体

  5. 绘制多种几何体演示过程

  6. 细探根签名

  7. 陆地与波浪演示程序

小结

  1. 在每帧中等待 GPU 处理完队列中所有命令的做法效率极低,因为这种策略在某些时刻会导致 CPU 或 GPU 处于空闲状态。一种更有效的技巧是创建帧资源(frame resource) ------ 一个由每帧都需 CPU 来修改的资源所构成的环形数组。这种方法令 CPU 无需等待 GPU 结束当前的任务,即可继续处理下一帧的相关工作;对此,CPU 只需处理下一个可用的(即 GPU 没在使用中的)帧资源。如果 CPU 处理帧的速度总是快于 GPU,则 CPU 必在某些时刻等待 GPU 追赶上来,但此情景又正是我们所期盼的:不仅 GPU 的处理能力将得到充分的发挥,同时,多出来的 CPU 资源又总是可被游戏的其他部分,如 AI,物理模拟与游戏逻辑所利用。

  2. 我们可以用 ID3D12DescriptorHeap::GetCPUDescriptorHandleForHeapStart 方法来获取堆中第一个描述符的句柄,通过 ID3D12Device::GetDescriptorHandleIncrementSize 方法得到描述符的大小(依赖于硬件与描述符的类型)。一旦知道了描述符增量的大小,我们就能用两种 CD3DX12_CPU_DESCRIPTOR_HANDLE::Offset 方法之一偏移至第 n 个描述符的句柄处:

cpp 复制代码
// 指定要偏移到的描述符的编号,再将它乘以描述符的增量大小
D3D12_CPU_DESCRIPTOR_HANDLE handle = mCbvHeap->
    GetCPUDescriptorHandleForHeapStart();
handle.Offset(n * mCbvSrvDescriptorSize);

// 或者用另一种等价实现,先指定要偏移到的描述符编号,再设置描述符的增量大小
D3D12_CPU_DESCRIPTOR_HANDLE handle = mCbvHeap->
    GetCPUDescriptorHandleForHeapStart();
handle.Offset(n, mCbvSrvDescriptorSize);

CD3DX12_GPU_DESCRIPTOR_HANDLE 类型有着同样的偏移方法。

  1. 根签名 定义了在绘制调用开始之前,需要与渲染流水线相绑定的资源,以及这些资源将被映射到的具体着色器输入寄存器。绑定到流水线的具体资源要根据着色器程序来确定。在创建 PSO 后,根签名与着色器程序的组合就开始生效了。根签名由一系列根参数所构成。根参数 可以是描述符表根描述符根常量。描述符表在堆中指定了一块描述符的连续范围。根描述符用于直接绑定根签名中的描述符(此过程无需涉及描述符堆)。而根常量则用于直接绑定根签名中的常量数据。出于性能的原因,1 个根签名中所能容纳的数据大小被限制为最多 64 DWORD。每个描述符表占 1 DWORD,每个根描述符用 2 DWORD,而每个 32 位的根常量占用 1 DWORD。硬件会为每次绘制调用而自动保存根实参的快照。这样一来,我们就能在每次绘制调用的过程中安全地修改根实参了。但是,我们也应当尽量缩小根签名的规模,以此降低内存间数据的复制量。

  2. 当顶点缓冲区的内容在运行时需要频繁更新(比如在每一帧,或每 1/30 秒就要更新一次),动态顶点缓冲区就派上了用场。我们可以使用 UploadBuffer 类来实现动态顶点缓冲区,但这次存储的是顶点数组,而非常量缓冲区数组。由于我们在每一帧都要从 CPU 向波浪动态顶点缓冲区上传新数据,所以需要将动态顶点缓冲区存为一种帧资源。在使用动态顶点缓冲区的过程中,难免会产生一些开销,这是因为新数据必将从 CPU 端内存回传至 GPU 端的显存。因此,在静态顶点缓冲区也可以胜任相同工作的情况下,它会比动态顶点缓冲区更受青睐。对此,Direct3D 的最新版本已经引进了一些新的特性,以减少动态缓冲区的使用。

相关推荐
m0_748232392 分钟前
WebRTC学习二:WebRTC音视频数据采集
学习·音视频·webrtc
蜀黍@猿18 分钟前
【C++ 基础】从C到C++有哪些变化
c++
Am心若依旧40919 分钟前
[c++11(二)]Lambda表达式和Function包装器及bind函数
开发语言·c++
zh路西法29 分钟前
【C++决策和状态管理】从状态模式,有限状态机,行为树到决策树(一):从电梯出发的状态模式State Pattern
c++·决策树·状态模式
轩辰~43 分钟前
网络协议入门
linux·服务器·开发语言·网络·arm开发·c++·网络协议
lxyzcm1 小时前
C++23新特性解析:[[assume]]属性
java·c++·spring boot·c++23
蜀黍@猿1 小时前
C/C++基础错题归纳
c++
虾球xz1 小时前
游戏引擎学习第55天
学习·游戏引擎
雨中rain2 小时前
Linux -- 从抢票逻辑理解线程互斥
linux·运维·c++
oneouto2 小时前
selenium学习笔记(二)
笔记·学习·selenium