【DirectX12游戏开发】绘制简单几何体相关概念总结

对龙书第六七章的部分总结

Direct12中资源用ID3D12Resource接口表示,前后台缓冲区,深度模板缓冲区,顶点缓冲区,索引缓冲区,常量缓冲区都是如此。
前后台缓冲区属于交换链,会通过IDXGIFactory::CreateSwapChain跟随交换链一同创建然后通过IDXGISwapChain::GetBuffer函数获取到。其余四个缓冲区都是通过填写D3D12_RESOURCE_DESC结构体再使用ID3D12Device::CreateComittedResource函数创建。

资源都存在于堆中,本质是GPU显存块,在使用上述的CreateComittedResource函数时会创建一个资源和一个堆,并把资源提交到堆中。深度模板缓冲区无需CPU访问,所以直接创建默认堆即可。顶点缓冲区和索引缓冲区在运行时也无需CPU访问,但是要CPU先往里面写入数据,所以需要一个上传堆和一个默认堆,将数据从CPU内存复制到上传堆,再从上传堆复制到默认堆。常量缓冲区需要CPU每帧更新,所以只需一个上传堆即可。
书本源码中封装了一个函数CreateDefaultBuffer,专用于创建一个上传堆一个默认堆并把数据从系统内存复制到上传堆再复制到默认堆这一过程。另外封装了一个UploadBuffer泛型类,专用于创建上传堆,并提供CopyData函数来更新上传堆的资源数据

每一个资源都有自己的描述符(视图)。
顶点缓冲区描述符和索引缓冲区描述符无需描述符堆,分别通过填写D3D12_VERTEX_BUFFER_VIEW和D3D12_INDEX_BUFFER_VIEW结构体创建。
剩下三个缓冲区描述符需要各自的描述符堆。因此,需要先创建描述符堆,填写D3D12_DESCRIPTOR_HEAP_DESC结构体并使用ID3D12Device::CreateDescriptorHeap函数即可创建描述符堆。然后分别使用ID3D12Device::CreateRenderTargetView函数创建后台缓冲区描述符(渲染目标视图),使用ID3D12Deivce::CreateDepthStencilView函数创建深度模板缓冲区描述符,使用ID3D12Device::CreateConstantBufferView函数创建常量缓冲区描述符。

根签名定义了应用程序要绑定到着色器的资源,是C++和hlsl的桥梁,由根参数构成,根参数分为根常量,根描述符,描述符表三种。

首先在程序初始化时需要创建根签名,通过CD3DX12_ROOT_PARAMETER创建根参数,并将其初始化为上面三种中的一种。再通过填写CD3DX12_ROOT_SIGNATURE_DESC结构体,并使用函数D3D12SerializeRootSignature将根签名描述布局序列化,再使用ID3D12Device::CreateRootSignature函数来创建根签名。

然后在每帧的绘制函数中提交根签名。
①如果根参数是描述符表,其实是引用了描述符堆中的一块连续内存,需要通过ID3D12CommandList::SetDescriptorHeaps函数和ID3D12CommandList::SetGraphicsRootSignature函数分别提交描述符堆和根签名,再通过ID3D12CommandList::SetGraphicsRootDescriptorTable函数即可设置需要绑定到流水线的根签名。
②如果根参数是根描述符,则在帧函数中无需提交描述符堆,只需通过ID3D12CommandList::SetGraphicsRootSignature函数提交根签名和ID3D12CommandList::SetGraphicsRootConstantBufferView函数绑定根描述符

流水线状态对象(PSO)集合了GPU流水线所需所有的状态信息,填写结构体D3D12_GRAPHICS_PIPELINE_STATE_DESC再调用ID3D12Device::CreateGraphicsPipelineState函数即可创建,结构体中包含了根签名,着色器,光栅器,输入布局描述,渲染目标数量等多种流水线需要用到的状态信息。

帧资源,将CPU每帧需要更新的资源抽象成一个类,并创建一个数组来保存,CPU可以循环处理每一帧需要更新的资源而无需在每一帧绘制的结尾都去等待GPU,GPU在处理完某一帧继续下一帧时也无需等待CPU提交,当然这是理想情况,帧资源是有限的,实际也还是会有一定的等待时间。
书中用一个FrameResource类表示帧资源,类中含有一个命令分配器,两个常量缓冲区和当前帧的围栏值。并维护一个存放有三个帧资源的数组,和一个指向当前帧的指针。这样在每一帧的绘制函数Draw()结尾时就无需让CPU等待GPU,而只需把当前帧的围栏值加一,并往GPU端设置一个新的围栏值。再在每一帧的更新函数Update()中把p_CurrentFrameResource向前挪动一个,使之指向下一个帧资源,并检测更新此帧与GPU端绘制是否有冲突,如果CPU执行速度超前了GPU三帧,那么还是要停下来等待。

基于资源的更新频率对常量进行分组,把每次渲染过程中需要更新的数据划为一组,例如投影矩阵,渲染目标大小等等,称为过程常量(PassConstant)。将只需更新一次的数据分为一组,例如静态物体的世界矩阵,称为物体常量(ObjectConstant),并将这两个常量的缓冲区都存到帧资源类中。

渲染项,绘制单个物体所需的数据的集合。一个大几何体可能由若干个不可再分的子几何体构成,每一个子几何体都需一个渲染项,书中用一个RenderItem结构体表示,结构体中含有以下数据:
①该物体的世界矩阵。
②int类型的标志位,初始化为帧资源数组的长度,在更新物体常量缓冲区时用于标识是否每一帧的该物体都已更新完。
③指向物体常量缓冲区的索引。例如有3个帧资源,每个帧资源中存储一个物体物体常量缓冲区和一个过程常量缓冲区,对于某一帧来说,假如要绘制22个物体,则需要22个渲染项,但是物体常量缓冲区只有一个,就是该帧资源中的UploadBuffer<ObjectConstant>对象,只是缓冲区长度为22,这个索引表示的就是该物体的常量在这个常量缓冲区的中的位置,在更新帧资源中物体常量缓冲区时需要用这个索引来找到位置。
④一个指向大几何体的指针,这个大几何体就是这个物体所属的几何体,书上有一句注释“绘制一个几何体可能会用到多个渲染项”就是这个意思,一个大几何体由若干个子物体构成,每个子物体都有各自的渲染项,这些子物体的渲染项的该指针变量就都指向同一个大几何体。
⑤图元拓扑
⑥DrawIndexedInstanced函数的参数

文章浅薄,主要是按自己的思维方式理一遍。
有错误欢迎指出

上一篇:在DirectX12中使用Texture


下一篇:在DirectX12中使用Texture