因此,本质上:
如果我有一个名为drawTexturedQuad()的函数,并且我想将着色器应用于结果,我该如何做?
谢谢
ID3DXEffect提供了Begin()
和BeginPass()
方法。只需在此期间调用drawQuad()
即可。任何有关着色器的基本教程都应该展示这样一个示例。
额外提示- 如果有疑问,请参考MSDN。
这个问题的答案令人惊讶地复杂,随着GPU硬件变得越来越强大,它变得越来越困难。D3DX FX系统是需要完成的所有工作的一个例子,因此使用它是实现短期使用的好方法。
着色器是代码,但它们存在于与CPU不同的另一台机器上,因此需要将它们的所有数据编组传输。固定部分:基本渲染状态,如深度状态、模板状态、混合模式、绘图命令;非常容易实现。难点在于为可编程部分建立桥梁:着色器、缓冲区、采样器和纹理。
索引缓冲区只需工作一次,因为您只能有一个或没有(在呈现未索引几何时)。
顶点缓冲区处理起来相对容易,因为硬件可以被编程以过程化方式读取顶点缓冲区。您的顶点缓冲区只需要提供至少与顶点着色器要访问的信息相同的信息即可。修改着色器的顶点输入或顶点格式需要同时编辑两侧,因此相对容易处理。
采样器和纹理是接下来比较“容易”的难点:它们有一个变量名和一个相当严格的类型。例如,编译着色器“foo”时,纹理“myNormalMap”被分配到纹理槽3。您需要通过反射API查找纹理被分配到的槽位,并在运行时将引擎认为是“myNormalMap”的纹理设置为槽位3,当然还要使用API确定是否首先需要该纹理。这就是开始为着色器变量命名约定变得重要的地方,因此可以使用相同的C++代码使多个着色器兼容。
常量缓冲区(或D3D9中的原始着色器常量)更加棘手,特别是在像虚幻引擎这样的可编程着色器框架中。每个给定着色器使用的常量都是完整列表的子集,但C++端通常必须编写为如果需要所有常量。反射API再次需要确定哪些变量实际上在着色器中被引用以及它们的位置。随着cbuffers成为结构体并且比D3D9系统更加稳定,这在D3D10及更高版本中变得更加可管理,但也增加了需要使用反射API来确定cbuffer绑定顺序(以及哪些cbuffers本身也被引用)的步骤。
最终有一个设计可以使所有工作正常运行:
创建一个驱动特定着色器原型的类。对于此类向着色器公开的每个变量(无论是纹理、常量缓冲区等),在反射信息中查找其是否被使用,并找出其位置并设置它。保持这一点快速、灵活和可扩展是一个艰巨的挑战。