首页 > 代码库 > DirectX 11游戏编程学习笔记之7: 第6章Drawing in Direct3D(在Direct3D中绘制)(重点回顾+勘误)

DirectX 11游戏编程学习笔记之7: 第6章Drawing in Direct3D(在Direct3D中绘制)(重点回顾+勘误)

        本文由哈利_蜘蛛侠原创,转载请注明出处!有问题欢迎联系2024958085@qq.com

 

        注:我给的电子版是700多页,而实体书是800多页,所以我在提到相关概念的时候,会使用章节号而非页码。同样的情况适合于“龙书”第二版。

 

        上一期的地址:

DX 11游戏编程学习笔记之6

 

        这一章应该是本书最长的一章了,可能也是最难的一章,所以大家一定要好好消化,仔细学习!这一章大致相当于“龙书”第二版的第7章和第8章,还添加了一些别的东西。

由于这一章比较长,所以我把习题解答部分放到下一期了。

重点回顾:

===============================================================================

        我们一如既往地把学习目标放在这里:

1、 To discover the Direct3D interfaces’ methods for defining, storing, and drawing geometric data.(发现Direct3D接口用于定义、存储和绘制几何数据的方法。)

2、 To learn how to write basic vertex and pixel shaders.(学习如何书写基本的顶点着色器和像素着色器。)

3、 To find out how to configure the rendering pipeline with render states.(知道如何对渲染管线设置渲染状态。)

4、 Tol earn how the effects framework can be used to logically group shaders and render states into a rendering technique, and how to use the effects framework as a “shader generator”.(知道效果框架如何可以用于将着色器和渲染状态有逻辑地组合成一个渲染技术,以及如何把效果框架当作一个“着色器生成器”来使用。)

 

        这一章的内容与“龙书”第二版相比变化太大了,所以这一期可能很少会提及后者了。

 

6.1 Vertices and Input Layouts

        这一节将如何定义DX 11中的顶点格式。这里讲的跟“龙书”第二版中用D3DVERTEXELEMENT9 数组定义的方法有点相像,但是有以下几个明显的不同点:

1、  D3DVERTEXELEMENT9 数组最后有一个表示数组结束标志的D3DDECL_END(),而DX 11所使用的D3D11_INPUT_ELEMENT_DESC 数组并没有这个东西。

2、  D3D11_INPUT_ELEMENT_DESC 引入了SemanticName 和SemanticIndex 这两个东东,可以让我们自定义顶点结构中每个成分的用途,而不像 D3DVERTEXELEMENT9 数组中只能够用几个预定义好的D3DDECLUSAGE 枚举类型变量。

3、  因为SemanticName是我们自己造的一个词,所以我们不能够像在DirectX 9.0c 中那样通过将顶点成分的用途定义为D3DDECLUSAGE_POSITIONT 来定义表示屏幕坐标的2D 顶点;也就是说,DX 11的所有的顶点都是3维的。

   

        当然,如前所述,这一节的内容变化太大了,具体的大家还是看看书本吧!

 

6.2 Vertex Buffers

        这一节讲了创建一个顶点缓存的三大步骤(其实只讲了两个,第三个漏讲了)。虽然步骤貌似比较繁琐,也不是很好理解,但是实际上可以看作是DirectX 9 中的IDirect3DDevice9::CreateVertexBuffer 方法的升级版(并且这个方法的名字中并不含Vertex,所以它可以创建任何缓存,包括马上要讲到的索引缓存)。初学者感到不适应的话,可以一直用作者给出的代码,因为这些代码对于本书的大部分示例来说是(基本)一样的。

        不过与DirectX 9 不同的是,创建完顶点缓存后,它还需要被绑定到设备的一个input slot 中,这样渲染管线才能够以这些顶点为输入。这时候就需要这个函数的帮忙了:void ID3D11DeviceContext::IASetVertexBuffers。

此后我们就可以调用ID3D11DeviceContext::Draw 函数来绘制这些顶点了。这个函数比DirectX 9 的IDirect3DDevice9::DrawPrimitive 少了一个表示图元类型的参数,因为这个参数在DX11 中是使用另外一个函数进行设置的。

 

6.3 Indices and Index Buffers

        前面说过,DX 11 的CreateBuffer 函数可以用于创建索引缓存,所以这里的内容省略了很多。在创建完索引缓存后,同样需要将其绑定到input assembler stage,而这个是通过ID3D11DeviceContext::IASetIndexBuffer。这个函数的使用比上面的ID3D11DeviceContext::IASetVertexBuffers要简单,毕竟一个带s,一个不带。

        值得注意的是,本书使用的索引缓存的格式一直是32位无符号整数;而在“龙书”第二版中使用的是16位无符号整数。

        如果绘制顶点的时候要使用索引的数据的话,那么前面讲的ID3D11DeviceContext::Draw 就要替换成ID3D11DeviceContext::DrawIndexed 函数了。这个函数比IDirect3DDevice9::DrawIndexedPrimitive 少了3个参数,所以用起来还是比较方便的。

 

6.4 Example Vertex Shader

        这一节介绍了顶点着色器的基本知识。跟“龙书”第二版的差不多,不过有一些句法有差别。

 

6.5 Constant Buffers

        这一节讲了“龙书”第二版中没有用到的东西:cbuffer 对象。在“龙书”第二版中,所有的effect parameters 都是并列的,一方面看上去比较臃肿,另一方面也不好管理。现在可好了!

 

6.6 Example Pixel Shader

        这一节也比较简单,和“龙书”第二版讲得差不多。但是有一个重要的区别:书上说了Notice that the pixel shader input exactly matches the vertex shaderoutput; this is a requirement. 而在“龙书”第二版中,像素着色器的输入参数并不需要表示齐次坐标的坐标位置,也就是说你可以认为float4 posH 在“龙书”第二版中的像素着色器中是作为默认输入参数而存在的。

        我还特地地做了一下实验,把像素着色器的float4 posH 给去掉了,然后就会弹出一个错误窗口,告诉你发生了连接错误。

 

6.7 Render States

        这里讲了渲染状态。这里的和DirectX9 又有很多不同,因为它将很多个不同的渲染状态绑定到一个结构体中了。这样的好处就是设置的时候比较方便(在一开始把你可能会用到的渲染状态都保存好,到时候再随便用),坏处就是少了许多有趣的可能性(不能够单独地对每个渲染状态进行赋值,而你显然不可能填充那么多的渲染结构体)。

        另外几个比较显著的区别:

1、  D3D11_RASTERIZER_DESC 结构体中没有表示shade mode 的成员。在DX 9 中,我们可以将shade mode 设置为D3DSHADE_FLAT 或者D3DSHADE_GOURAUD 来分别获得平面着色或者高洛得着色效果。从示例程序的运行结果来看,DX 11 的shade mode 永远是高洛得着色模式。

2、  D3D11_RASTERIZER_DESC 的D3D11_FILL_MODE 类型的成员变量只有D3D11_FILL_WIREFRAME 和D3D11_FILL_SOLID 这两种类型,而DX 9 中还有 D3DFILL_POINT 这种类型。

 

        此外还有一个注意事项,这个一定要注意,否则很可能会出问题的。就是在创建一个rasterizer state的时候,在ZeroMemory 一个D3D11_RASTERIZER_DESC 结构体后,一定要显式地对FillMode CullMode 进行设置!

 

6.8 Effects

        这一节比较长,需要注意的内容也比较多。

        在最一开始,作者就告诉了我们使用effectsframework 所需要做的准备。具体来说,有以下两件事要去做:

1、 d3dx11Effect.h这个头文件 include 进你的项目中。

2、 Effects11.libEffects11d.lib这两个库文件连接到你的项目中。

        这里有几个需要注意的事情:

1、 d3dx11Effect.h头文件位于DirectX SDK\Samples\C++\Effects11\Inc目录下。

2、 Effects11.lib Effects11d.lib需要你自己将DirectX SDK\Samples\C++\Effects11这个项目打开(用VS 2010VS 2013都可以打开编译;VS 2012应该也可以吧)分别在releasedebug 模式下编译而得到。注意在debug模式下得到的库文件仍然叫做Effects11.lib,所以为了区分,你可以在后面添加一个d字母。

3、 在作者给出的Common文件夹中已经有了这三个文件了,不过请确保你是在VS2010 中进行使用,否则会报错。另外我把VS 2013版的这两个库文件放在了更新版的Common文件夹中了,欢迎大家使用!

 

6.8.2 Compiling Shaders

        这里讲了创建一个effect 所需要采取的两个步骤。步骤一是对定义在一个.fx 文件中的着色器程序进行编译。这是通过D3DX11CompileFromFile 函数进行的。然后我们再通过D3DX11CreateEffectFromMemory 函数来创建一个effect。这个是与DX 9 不同的,在后者中我们只需要用一个D3DXCreateEffectFromFile 函数就搞定了。

 

6.8.3 Interfacing with Effects from the C++ Application

        这一节说的是应用程序内的变量如何与.fx 文件中的变量发生关系。这里面的内容似曾相识,但是用法还是发生了不小的变化。

 

6.8.4 Using Effects to Draw

        这一节讲述使用effect 进行绘制的步骤。和DX 9中的版本的区别还是蛮大的。最明显的就是,不再有BeginPass 和EndPass 函数了。

 

6.8.5 Compiling an Effect at Build Time

        这一小节很重要!它告诉我们如何在建立工程的时候就编译effect。文中列举了三个步骤。如果你使用的是VS 2010 的话,那么就按照书上的做好了。但是如果你用的是VS 2013 的话,那么步骤就有所不同了。

        究其原因是因为,VS 2013 没有给.fx 提供CustomBuild Tool 功能;相反,VS 2013 有一个内置的HLSL Compiler;所以你需要对这个HLSL Compiler 做出一些设定。在今后的每个更新版代码所附带的ReadMe – DX 11.txt 文档中,我都会详细叙述代码的使用方法,包括这一小节所讲述的设置方法。为了方便,我把说明文档中与本小节三个步骤中的后两个步骤相关的部分贴上来:

7、在Solution Explorer中右击项目名称,选择Add -> New Filter,然后将之命名为FX,然后右击FX,选择Add -> Existing Item,再将项目文件夹下的FX子文件夹下的所有.fx文件添加进来。

 

8、右击每个.fx文件,选择Properties,然后在HLSL Compiler栏目下的All Options中,做出以下设置:

 ---- Disable Optimaztions选择为Yes(/Od)(这是在Debug模式下的选项;Release模式不需要选);

 ---- Enable Debugging Information选择为Yes(/Zi)(这是在Debug模式下的选项;Release模式不需要选);

 ---- Entrypoint Name清空;

 ---- Object File Name填写为 %(RelativeDir)\%(Filename).fxo

 ---- Shader Model选择为Shader Model 5 (/5_0)

 ---- Shader Type 选择为Effect (/fx)

 

        细心的读者会发现这里并没有设定outputthe assembly listing。其实我也想来着,但是我发现不管将AssemblerOutput设为什么,都会出错。我猜想是因为某种原因导致生成渲染器的汇编语言时出了问题,但是不知道为何会出问题。希望有高人能够解答呀!

 

6.8.6 The Effects Framework as a “Shader Generator”

        这一小节告诉了我们使用uniform 变量来从几个有限的VS 和PS 来生成很多technique的方法。这样做可以提高运行时的效率。所以这一节也是很重要的!

 

6.8.7 What the Assembly Looks Like

        这一节我是基本看不懂的。令我感到印象深刻的是这里飘逸犀利的注释:原来注释还可以这么玩!

 

6.11 Shapes Demo

        这一节讲了生成圆柱(实际上是圆台,包括圆柱和圆锥)和球的方法,并且展示了如何将几个不同物体的顶点缓存和索引缓存分别合并到一个大的顶点缓存和索引缓存中的方法。这一节还是比较精彩的,而且可能不是很好理解,可能大家需要结合后面的习题来慢慢加深理解。

        这里面提到了我们暂时还没有接触到的概念,就是法向量、切向量和纹理坐标。不过不用担心,这个我们以后会慢慢接触到的。另外值得一提的是,因为GeometryGenerator.h 中定义的Vertex 包含了这些我们还没用到的东西,并且不含有我们一直在使用的颜色成分,所以我们需要做出一些转换。

        读过龙书第二版的同学可能会有疑问:在DX9 中,我们可以用D3DXCreateSphere 函数来建立球面,还可以用D3DXCreateCylinder 函数来建立圆柱面(圆台面),那么DX 11 中有没有类似的函数呢?遗憾的是,似乎是没有的;这正是作者要开辟这一节讲述这一内容的原因。不过这样也好,一来我们可以更加清楚地看到这些函数是怎么工作的,另一方面这些函数更加人性化了——它们的顶点包含了更多的成分,比如说纹理坐标,而在DX 9 中我们用内建的D3DXCreateCylinder 创建圆柱面后还需要进行转换操作才能够让其顶点包含纹理坐标;另外D3DXCreateCylinder 函数所生成的圆柱面是沿着Y轴排列的,而不是像DX 9中那样是沿着Z轴排列的,更加人性化。

        此外这一节还讲了另外一种构造球面的方法:geosphere方法。这种方法很不错,不过由于它的面数是呈几何级数增长的,所以我们不要轻易使用过高的细分;另外这也少了一些自由性。这里给出的代码其实大家看看就可以了,而且里面最关键的其实是那个Subdivide 函数,它同时具有细分顶点和为新增的顶点提供索引的功能。

 

6.13 Dynamic Vertex Buffers

        这一节讲了动态顶点缓存。其实这比较高级了,后面在讲粒子系统的时候还会详细讲述,现在大家有一个印象就行了。

   

===============================================================================

 

勘误:

===============================================================================

1、  本章的导言部分的目标的第一条中的interfaces应该为interfaces’。

2、  第6.2 节一开始说要建立一个顶点缓存,需要三个步骤。但是后面作者忘了说第三步怎么用了。我查了查SDK 文档,其实用起来还是很简单的。具体是这样的:

HRESULT CreateBuffer(

 [in]   const D3D11_BUFFER_DESC*pDesc,

 [in]   constD3D11_SUBRESOURCE_DATA *pInitialData,

 [out]  ID3D11Buffer **ppBuffer

);

参数的解释;

pDesc [in]

D3D11_BUFFER_DESC

Pointer to a buffer description.

pInitialData [in]

D3D11_SUBRESOURCE_DATA

Pointer to the initialization data; use NULL toallocate space only (with the exception that it cannot be NULL if the usageflag is D3D11_USAGE_IMMUTABLE).

ppBuffer [out]

ID3D11Buffer

Address of a pointer to the buffer created. Setthis parameter to NULL to validate the other input parameters (S_FALSEindicates a pass).

 

3、  第6.2 节中介绍IASetVertexBuffers函数时,下面的第4条的叙述有问题,就是最后一句 A stride is the size, in bytes, of an element in the correspondingvertex buffer 应该为 A stride is the size, in bytes, of all elements in thecorresponding vertex buffer。

4、  第6.3节解释DrawIndexed函数时,第三个参数BaseVertexLocation 的解释中“before the vertices are fetched” 疑为“after the vertices are fetched”。

5、  第6.6节第一块代码的PS中的return 语句写错了,不应该是pin.Color,而应该是color。

6、  第6.7节给出RSSetState函数的原型后的那句话 The following code shows how to create a rasterize state … 中的rasterize 应为rasterizer。

7、  第6.7节接近结束时的代码块的第一句注释:

// Create renderstate objects as initialization time

中的as 应该为at。

8、  第6.8节中的D3DX11Effects.lib 和D3DX11EffectsD.lib 应该分别改为 Effects11.lib 和Effects11d.lib(并且在debug 模式下生成的.lib 文件仍然叫做Effects11.lib;你需要手动地添加一个d 在最后。)

9、  第6.8 节一开始第二自然段中的In this book, we will only be using the effects framework as is 应该改为In thisbook, we will only be using the effects framework as it is。


===============================================================================

 

        这一期就到这里了!最后我给出这一章的示例代码。示例代码有两个部分,一个是作者的,另一个是本人加工过的,请大家随意使用!(以后的示例代码的发布也是遵循这个规律的。)下载地址:

第6章示例代码下载地址


DirectX 11游戏编程学习笔记之7: 第6章Drawing in Direct3D(在Direct3D中绘制)(重点回顾+勘误)