DirectX 12 学习之路(三)

一、图元拓扑 通过指定图元拓扑(primitive topology,或称基元拓扑)来告知Direct3D如何用顶点数来表示几何图元。
1、点列表 (D3D_PRIMITIVE_TOPOLOGY_POINTLIST)(point list)
所有的顶点都将在绘制调用的过程中被绘制为一个单独的点。
2、线条带(D3D_PRIMITIVE_TOPOLOGY_LINESTRIP)(line strip)
顶点将在绘制调用的过程中被连接为一系列的连续线段,若有n+1个顶点就会生成n条线段。
3、线列表(D3D_PRIMITIVE_TOPOLOGY_LINELIST)(line list)
每对顶点绘制调用的过程中都会组成单独的线段,每2n个顶点就会生成n条线段。
线列表和线条带的区别在于:线列表中的线段可以彼此分开,而线条带中的线段则相连的。
4、三角形带(D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP)(triangle strip)
所绘制的三角形会被连接成带状,处于中间位置的顶点将被相邻的三角形所共同使用。利用n个顶点即可生成n-2个三角形。
在三角形带中,次序为偶数的三角形和为奇数的三角形的绕序(windling order,也译为环绕顺序等,即装配图元的顶点顺序为逆时针或顺时针方向)是不同的,这就是剔除(culling,又称消隐)问题的由来。为了解决这个问题,GPU内部会对偶数三角形中的后(DirectX 中应该是后,OpenGL是前)两个顶点的顺序进行调换,以此使它们与奇数三角形的绕序保持一致。
5、三角形列表(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST)(triangle list)
在绘制调用的过程中会将每3个顶点装配成独立的三角形,所以每3n个顶点会生成n个三角形。
三角形列表与三角形带的区别是:三角形列表中的三角形可以彼此分离,而三角形带中的三角形则是相连的。
6、具有邻接数据的图元拓扑(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST_ADJ)
对于存有邻接数据的三角形列表而言,每个三角形都有3个与之相邻的邻接三角形(adjacent triangle)。
为了使几何着色器可以顺利地获得这些邻接三角形的信息,我们就需要借助顶点缓冲区与索引缓冲区将它们随主三角形一并提交至渲染流水线。
注意,邻接图元的顶点只能用作几何着色器的输入数据,却并不会被绘制出来 。即使程序没有用到几何着色器,但依旧不会绘制邻接图元。
6n个顶点可以生成n个三角形及其邻接数据,线列表、线条带和三角形带也存在有邻接数据的图元拓扑。
7、控制点面片列表(D3D_PRIMITIVE_TOPOLOGY_N_CONTROL_POINT_PATCHLIST)
将顶点数据解释为具有N个控制点(control point)的面片列表( patch list)。此图元常用于渲染流水线的曲面细分阶段(tessellation stage)。

二、在计算三角形法线的方法中,根据观察者的视角去看,顶点绕序为顺时针方向的三角形为正面朝向,而顶点绕序为逆时针方向的三角形为背面朝向。但是,通过对Direct3D渲染状态的设置,我们也可以将这个约定“颠倒”过来。

三、输入布局描述(input layout description):向Direct3D提供顶点结构体的描述,是它了解应怎样来处理结构体中的每个成员。通过语义(semantic)将顶点结构体中的元素与顶点着色器输入签名(签名 signature 可以理解为着色器中输入或输出参数列表)中的元素一一映射起来。

四、对于静态几何体(static geometry,即每一帧都不会发生改变的几何体)而言,我们会将其顶点缓冲区置于默认堆(D3D12_HEAP_TYPE_DEFAULT)中来优化性能。一般来说,游戏中的大多数几何体(如树木、建筑物、地形和动画角色)都是如此处理。
除了创建顶点缓冲区资源本身外,我们还需要用D3D12_HEAP_TYPE_UPLOAD这种堆类型来创建一个处于中介位置的上传缓冲区(uplaod buffer)。通过把资源提交至上传堆,才得以将数据从CPU复制到GPU显存中。在创建了上传缓冲区之后,我们就可以将顶点数据从系统内存复制到上传缓冲区,而后再把顶点数据从上传缓冲区复制到真正的顶点缓冲区中。

五、为了将顶点缓冲区绑定到渲染流水线上,我们需要给这种资源创建一个顶点缓冲区视图(vertex buffer view)。和RTV(render texture view)不同的是,我们无须为顶点缓冲区视图创建描述符堆。而且,顶点缓冲区视图是由D3D12_VERTEX_BUFFER_VIEW结构体来表示。
在顶点缓冲区及其对应的视图创建完成后,便可以将它与渲染流水线上的一个输入槽(input slot)相绑定,这样一来,我们就可以向流水线中的输入装配器阶段传递数据了。通过ID3D12GraphicsCommandList::IASetVertexBuffers()来完成。(IA input Assembler 输入装配器)
将顶点缓冲区设置到输入槽上并不会对其执行实际的绘制操作,而是仅为顶点数据送至渲染流水线做好准备而已。这最后一步是通过ID3DGraphicsCommandList::DrawInstanced()或ID3DGraphicsCommandList::DrawIndexedInstanced()方法真正地绘制顶点。

六、被绘制为点、线列表还是三角形列表,图元拓扑状态实由ID3DGraphicsCommandList::IASetPrimitiveToplogy()方法来设置。

七、需要给索引缓冲区资源创建一个索引缓冲区视图(index buffer view)。和顶点缓冲区视图一样,我们也无需给索引缓冲区视图创建描述符堆。但是索引缓冲区视图要由结构体D3D12_INDEX_BUFFER_VIEW来表示。
通过ID3D12GraphicsCommandList::IASetIndexBuffer()(IA input Assembler 输入装配器)方法即可将索引缓冲区绑定到输入装配器阶段。

八、HLSL(High Level Shading Language)高级着色语言。在HLSL中,所有的函数都是内联(inline)函数。
SV_POSITION语义比较特殊(SV表示系统值,system value),它所修饰的顶点着色器输出元素存有齐次裁剪空间中的顶点位置信息。因为,我们必须为输出位置信息的参数附上SV_POSITION语义,使GPU可以在进行例如裁剪、深度测试和光栅化等处理之时,借此实现其他属性所无法介入的有关运算。值得注意的是,对于任何不具有系统输出参数而言,我们都可以根据需求以合法的语义名修饰它。
如果没有使用几何着色器,那么顶点着色器必须使用SV_POSITION语义来输出顶点在齐次裁剪空间中的位置,因为(在没有使用几何着色器的情况下)执行完顶点着色器之后,硬件期望获取顶点位于齐次裁剪空间之中的座标。如果使用了几何着色器,则可以把输出顶点在齐次裁剪空间中的位置的工作交给它来处理。
在顶点着色器(或几何着色器)中是无法进行透视除法的,此阶段只能实现投影矩阵这一环节的运算。而透视除法将在后面交由硬件执行。

九、输入布局中的元素格式确定的是,在数据未进入着色器寄存器之前,应运用何种数据转换算法来确定各元素的具体格式。而着色器输入签名则定义的是,在不修改输入寄存器中所存数据的情况下,顶点着色器将如何来解释这些数据的类型。

十、由于硬件的原因,某些像素片段在移送至像素着色器之前,可能已经被渲染流水线所剔除(例如提前深度剔除, early-z rejection,也有译为早期深度剔除、早期z剔除等)。然而也有一些对情况能够禁止提前深度剔除优化。比如说,倘若在像素着色器中有对像素深度值进行修改的操作,那么像素着色器就必须针对每个像素各执行一次,因为在像素着色器修改像素深度值以前,我们并不知道每个像素的最终深度值。

十一、像素着色器返回一个4D颜色值,而位于此函数参数列表后的SV_TARGET语义则表示该返回值的类型应当与渲染目标格式(render target format)相匹配(该输出值会被存于渲染目标之中)。

十二、常量缓冲区(constant buffer)是一种GPU资源(ID3D12Resource),其数据内容可供着色器程序所引用。和顶点缓冲区和索引缓冲区不同的是,常量缓冲区通常由CPU每帧更新一次。所以,我们会把常量缓冲区创建到一个上传堆而非默认堆中,这样能够使我们从CPU端更新常量。
常量缓冲区对硬件也有特别的需求:常量缓冲区的大小必须为硬件最小分配空间(256B)的整数倍。

十三、Direct3D 12 不仅保证了Map与Unmap函数在多线程中调用的安全性,还令Map函数可以嵌套使用。第一次调用Map函数时,Direct3D会在CPU端分配一块虚拟内存地址范围,用来映射CPU中的资源。而最后一次调用Unmap函数时,则会释放这块CPU虚拟地址范围。Map函数会在必要的时对CPU缓存执行invalidate操作(标记相关缓存无效,令CPU读取主存中的数据),以使CPU端可以读取GPU端对这段地址内容所做的修改;相反地,Unmap函数则会在必要时对CPU缓存进行flush操作(令缓存中的数据写会主存),以令GPU端可以读取CPU端对这层地址内容所做的修改。

十四、一般来讲,物体的世界矩阵将随其移动/旋转/缩放而改变,观察矩阵随虚拟摄像机的移动/旋转而改变,投影矩阵随窗口大小的调整而改变。

十五、常量缓冲区描述符都存放在以D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV类型所创建的描述符堆里,这种堆内可以混合存储常量缓冲区描述符、着色器资源描述符和无序访问描述符。创建这种描述符堆和创建渲染目标和深度/模板缓冲区这两种资源描述符堆的过程很相似,但是,有一个重要的区别,那就是在创建供着色器程序访问资源的描述符时,我们需要把标志Flags指定为DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE。

十六、通常来讲,在绘制调用开始执行之前,我们应将不同的着色器程序所需的各种类型的资源绑定到渲染流水线上。 实际上,不同类型的资源会被绑定到特定的寄存器槽(register slot)上,以供着色器程序访问。
寄存器槽就是向着色器传递资源的手段,register(#)中表示寄存器传递的资源类型,可以是t(表示着色器资源视图)、s(采样器)、u(无序访问视图)以及b(常量缓冲区视图),#则为所用的寄存器编号。

十七、根签名(root signature)定义的是:在执行绘制命令之前,那些应用程序将绑定到渲染流水线上的资源,它们会被映射到着色器的对应输入寄存器。根签名一定要与使用它的着色器相兼容(即在绘制开始之前,根签名一定要为着色器提供其执行期间需要绑定到渲染流水线的所有资源),在创建流水线状态对象(pipeline state object)时会对此进行验证。不同的绘制调用可能会用到一组不同的着色器程序,这也就意味着要用到不同的根签名。
如果我们把着色器程序当作一个函数,而将输入资源看作着色器的函数参数,那么根签名则定义了函数签名(这也就是根签名一次的由来)。通过绑定不同的资源作为参数,着色器的输出也将有所差别。例如,顶点着色器的输出取决于实际向它输入的顶点数据以及为它绑定的具体资源。
在Direct3D中,根签名由ID3D12RootSignature接口来表示,并以一组描述绘制调用过程中着色器所需资源的根参数(root parameter)定义而成。根参数可以是根常量(root constant)、根描述符(root descriptor)或者描述符表(descriptor table)。
Direct3D 12规定,必须先将根签名的描述布局进行序列化处理(serialize),待其转换为以ID3DBlob接口表示的序列化数据格式后,才可以将它传入CreateRootSignature方法,正式创建根签名。
根签名只定义了应用程序要绑定到渲染流水线的资源,却没有真正地执行任何资源绑定的操作。只要率先通过命令列表(Command list)设置好根签名,我们就能用ID3D12GraphicsCommandList::SetGraphicsRootDescriptorTable()方法令描述符表与渲染流水线相绑定。
处于性能考虑,我们应当使根签名的规模尽可能的小,除此之外,还要试着尽量减少每帧渲染过程中根签名的修改次数。

十八、光栅化状态不是可编程的,只能接受配置。由结构体D3D12_RASTERIZER_DESC来表示。

十九、大多数控制图形流水线状态的对象被统称为流水线状态对象(pipeline state object,PSO),用接口ID3D12PipelineState来表示,要创建PSO,先填写一份描述其细节的D3D12_GRAPHICS_PIPELINE_STATE_DESC结构体实例。
ID3D12PipelineState对象集合了大量的流水线状态信息,为了保证性能,我们将所有这些对象都集合在一起,一并送至渲染流水线上。通过这样一个集合,Direct3D便可以确定所有的状态是否彼此兼容,而驱动程序则能够据此而提前生成硬件本地指令及其状态。在Direct3D 11的状态模型中,这些渲染状态片段都是要分开配置的,然而这些状态实际都有一定的联系,以致如果其中一个状态发生改变,那么驱动程序可能要为了另一个相关的独立状态而对硬件重新进行编程。由于一些状态在配置流水线时需要改变,因而硬件状态也就可能被频繁地改写。为了避免这些冗余的操作,驱动程序往往会推迟针对硬件状态的编程动作,直到明确整条流水线的状态发起绘制调用后,才正式生成对应的本地指令与状态。但是,这种延迟操作需要驱动在运行时进行额外的记录工作,即追踪状态的变化,而后才能在运行时生成改写硬件状态的本地代码。在Direct3D 12的新模型中,驱动程序可以在初始化期间生成对流水线状态编程的全部代码,这便是我们将大多数的流水线状态指定为一个集合所带来的好处。
Direct3D实质上是一种状态机(state machine),里面的事物会保持他们各自的状态,直到我们将其改变。
考虑到性能问题,我们应当尽量减少改变PSO状态的次数,为此,若能以一个PSO绘制出所有的物体,绝不用第二个PSO。
切记,不要在每次绘制调用时都修改PSO!!!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章