DirectX 9.0c游戏开发手记之RPG编程自学日志之8: Drawing with DirectX Graphics (用DirectX图形绘图)(第4节)(A)_哈利_蜘蛛侠的博客-程序员宅基地

技术标签: C++  DirectX  游戏编程  RPG  游戏开发  directx  

        本文由哈利_蜘蛛侠原创,转载请注明出处!有问题请联系[email protected]

 

        这一次我们继续来讲述Jim Adams老哥的RPG编程书籍第二版第二章的第4节:Getting Down to Drawing。这一节可以说是超级长了,所以我们就分3次来讲吧!

 

        由于这一节的内容实在是太多,所以我这一节的各小节的标题列在下面,以供大家参考:


1、Using Vertices (使用顶点)

2、Flexible Vertex Format (灵活顶点格式)

3、Using Vertex Buffers (使用顶点缓存)

4、Vertex Streams (顶点流)

5、Vertex Shaders (顶点着色器)

6、Transformations(变换)

7、The World Transformation (世界变换)

8、The View Transformation (视角变换)

9、The Projection Transformation (投影变换)

10、Materials and Colors (材质和颜色)

11、Clearing the Viewport (清除视口)

12、Beginning and Ending a Scene (开始和结束场景)

13、Rendering Polygons (渲染多边形)

14、Presenting the Scene (展示场景)

 

        这一期要从Using Vertices讲到Vertex Shaders。

 

        原文翻译:

 

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

 

2.4 Getting Down to Drawing (开始进行绘制)

 

        基础部分讲的足够多了;是时候来看看Direct3D到底是怎么绘制图形的。在这一节中,我会介绍使用顶点和多边形来绘制图形的基本知识。你会学到Direct3D使用顶点来绘制多边形的各种方式,如何给这些多边形上色,并最终向用户展示这些图形。正所谓细节决定成败,因此要好好研究处理顶点的方法,再从这里继续前进。

 

 

2.4.1 Using Vertices (使用顶点)

 

        Direct3D给了你很多不同的方式来定义一个顶点。例如,如果你在使用2-D图形,你可以在2-D屏幕坐标(变换后的坐标)中设定坐标。

        另一方面,如果你在使用局部的或者世界的空间坐标,你可以在3-D坐标(未变换的坐标)中设定坐标。那么如何使用颜色和纹理呢?你可以在你的顶点定义中选择包含进这些信息。

        那么你如何跟踪所有的这些信息并且保证Direct3D直到你正在做什么呢?请看灵活顶点格式。

 

 

2.4.2 Flexible Vertex Format (灵活顶点格式)

 

        灵活顶点格式(flexible vertex format,简称FVF)用于构造自定义的顶点数据以便在你的应用程序中使用。使用FVF,你必须决定你的顶点要使用哪些信息——例如3-D坐标、2-D坐标、颜色等等。

        你使用一种标准的结构体来创建FVF,在这个结构体中你只加入你想要的成分。当然有一些限制,例如你必须用某种特定的顺序来列举这些成分,并且某些成分不能与其他的成分相冲突(例如同时使用2-D坐标和3-D坐标)。一旦这个结构体完成后,你建立一个FVF描述器(FVF descriptor),它是描述你的顶点格式的一系列标记的组合。

        下面的代码块包含了一个使用了各种在FVF中允许使用的变量(或者说,至少是所有的我在这本书中用到的变量)的顶点结构体。结构体中的变量列举的顺序与它们在你自己的结构体中出现的顺序严格保持一致;如果你截去了任何变量,保证剩下的保持顺序:

 

typedef struct {
  FLOAT            x,y, z, rhw;       // 2-D coordinates
  FLOAT            x,y, z;            // 3-D coordinates
  FLOAT            nx,ny, nz;         // Normals
  D3DCOLOR <span style="white-space:pre">	</span>   diffuse;           // Diffuse color
  FLOAT            u,v;               // Texturecoordinates
} sVertex;


        正如你所看到的,唯一的互相冲突的变量是表示坐标的变量,包括法向量(normals)。法向量是用来定义一个方向的并且只能与3-D坐标联合使用的坐标。你需要选择哪些坐标(要么2-D,要么3-D)要保留,哪些坐标要丢弃。如果你在使用2-D坐标,那么你就不能包含进3-D坐标;反之亦然。

        2-D坐标和3-D坐标的唯一的真正的区别是额外的rhw变量,它是其次坐标(homogeneous)W的倒数。用通俗的话来说(原文是In English,这一般代表从观察点到顶点的沿着z-轴的距离。在大多数情况下,你可以安全地将rhw值设为1.0。

        还要注意,sVertex坐标使用了数据类型FLOAT(它是一个浮点值),但是D3DCOLOR是啥数据类型呢?D3DCOLOR是一个DWORD值,在Direct3D中,你用之来储存颜色值。为了构造一个颜色值用于D3DCOLOR,你可以从两个函数中进行选择:D3DCOLOR_RGBA或者D3DCOLOR_COLORVALUE:

D3DCOLORD3DCOLOR_RGBA(Red, Green, Blue, Alpha);
D3DCOLOR D3DCOLOR_COLORVALUE(Red,Green, Blue, Alpha);

        每个函数(事实上它们是宏)取四个参数,代表着要用到的各个颜色成分的量,包括一个alpha值(透明度)。这些值在D3DCOLOR_RGBA中可以从0变到255,而在D3DCOLOR_COLORVALUE中可以从0.0变到1.0(分数)。如果你在使用实心的颜色(不透明的颜色),那么就把alpha值恒取为255(或1.0)。

        作为例子,比如说你只想在你的顶点结构中包含进3-D坐标和一个漫反射颜色成分:

typedef struct {
  FLOAT            x,y, z;
  D3DCOLOR diffuse;
} sVertex;

        创建你的FVF的下一步是使用表格2.3中列出来的任意标记组合来建立FVF描述器。

 

表2.3 灵活顶点格式描述器标记

Flag

Description

D3DFVF_XYZ

包含进了3-D坐标。

D3DFVF_XYZRHW

包含进了2-D坐标。

D3DFVF_NORMAL

包含了法向量(一个向量)。

D3DFV_DIFFUSE

包含进了一个漫反射颜色成分。

D3DFVF_TEX1

包含进了一组纹理坐标。

 

        为了描述一个FVF描述器,你把所有适当的标记组合进一个define语句中(假设你在使用3-D坐标和漫反射颜色成分):

#define VertexFVF(D3DFVF_XYZ | D3DFVF_DIFFUSE)

        只需要保证所有的标记与你加入到你的顶点结构体中的成分相匹配,那么一切就会顺利的。


2.4.3 Using Vertex Buffers (使用顶点缓存)

 

        在你构造你的顶点结构体以及描述器之后,你要创建一个包含了一个顶点数组的对象。Direct3D给了你两个对象来使用:IDirect3DVertexBuffer9和IDirect3DIndexBuffer9。我在这本书中使用的对象是IDiret3DVertexBuffer9,它储存着用于绘制三角形列、三角形带和三角形扇的顶点。(实际上,将它与IDirect3DIndexBuffer9结合起来使用才是比较省效率的方法;这也正是本人在自己的更新版代码中使用的方法。)

 

        当用于三角形列时,IDirect3DVertexBuffer9对象为每个要绘制的多边形储存至少3个顶点(这些顶点以顺时针顺序排序)。对于三角形带,第一个将被绘制的多边形使用3个顶点,而后面的每个要被绘制的多边形只需要使用1个额外的顶点。对于三角形扇,储存有一个中心的顶点,而每一个将被绘制的多边形要储存两个额外的顶点(其实一般来说只要一个额外的顶点,见下面的正方形例子)

 

        注意:

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

        一个多边形可以使用1个、2个或3个顶点,这取决于你在绘制什么图形。像素只需要一个单个的顶点,线段需要两个,而三角形多边形需要三个。在这本书中,我主要处理的是三角形的多边形。

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

 

        图2.11应该可以帮助你更好地理解你如何使用储存的顶点以及你以什么顺序布置这些顶点。在图中,有一个可以用三种方法之一定义的正方形。在第一种方法中,是使用一个三角形列,所以你需要使用6个顶点来定义这个正方形——两个三角形的每一个都要花去三个顶点。


        第二种方法是使用一个三角形带。三角形带只使用4个顶点,这在图中表示得很明白。前三个顶点构造了第一个面,而最后一个多边形定义了第二个面。对于第三种方法,也就是三角形扇方法,你还是使用4个顶点。但是,用三角形扇时,你定义的第一个顶点变成了扇的基点,而其余的顶点定义了扇面。


 

 

2.4.3.1 Creating a Vertex Buffer (创建一个顶点缓存)


        你通过初始化后的IDirect3DDevice9对象来创建一个顶点缓存:

HRESULT IDirect3DDevice9::CreateVertexBuffer(
  UINT              Length,    // # of bytes to use, in multiples
                               //of vertex structure size.
  DWORD      Usage,    <span style="white-space:pre">	</span>       // 0
  DWORD        FVF,            // FVF descriptor
  D3DPOOL   Pool,              // D3DPOOL_MANAGED
  IDirect3DVertexBuffer9 **ppVertexBuffer,         // the vertex buffer
  HANDLE       *pHandle);      //set to NULL

        在CreateVertexBuffer函数的调用中你唯一想要改变的参数是Usage标记,它告诉Direct3D如何对待用于储存顶点数据的内存。为了呆在安全的一边,你应该始终将Usage设为0,但是如果你想提升一点性能的话,将Usage设为D3DCREATE_WRITEONLY。这样做会告诉Direct3D你不准备读取顶点数据,然后Direct3D会适当地储存顶点数据。一般来说,这意味着顶点数据会储存在图形硬件的内存中(称为:快速读取内存,faster-access memory)。


        这是一个简短的例子(建立在我之前——在“灵活顶点格式”这一节中——的顶点格式基础上,它只使用了3-D坐标和漫反射顶点成分),它创建了一个包含4个顶点的顶点缓存:

// g_PD3DDevice = pre– initialized device object
// sVertex =pre-defined vertex structure
// VertexFVF =pre-defined Vertex FVF descriptor
IDirect3DVertexBuffer9*PD3DVB = NULL;
 
// Create the vertexbuffer
if(FAILED(g_PD3DDevice->CreateVertexBuffer(                  \
    sizeof(sVertex) * 4, D3DCREATE_WRITEONLY, VertexFVF,     \
    D3DPOOL_MANAGED, &PD3DVB, NULL))) {
    // Error occurred
}


 

        注意:

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

        在创建你自己的顶点缓存时,确保你设定了合适的缓存大小(CreateVertexBuffer函数调用的第一个参数)。在这里展示的例子中,你开辟了足够的内存来储存4个sVertex格式的顶点。这里的4表示4个实例,而sVertex是你用于储存你的顶点数据的结构体。

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

 

        注意:

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

        跟以前一样,在你用完顶点缓存后,确保通过调用其Release函数来释放这个COM对象。

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

 

2.4.3.2 Locking the Vertex Buffer (锁定顶点缓存)

 

        在你能够将顶点添加进顶点缓存对象之前,你必须锁定该缓存所使用的内存。这保证存储顶点的内存是位于可利用的内存区域。然后你使用一个内存指针来访问顶点缓存内存。你通过调用缓存对象的Lock函数来锁定顶点缓存的内存并返回一个内存指针:

HRESULTIDirect3DVertexBuffer9::Lock(
  UINT    OffsetToLock,  // offset to lock buffer, in bytes
  UINT    SizeToLock,    // how many bytes to lock, 0 = all
  VOID **ppbData,        // pointer to a pointer (to access data)
  DWORD Flags);          //0

        在这里,你让offset位于缓存中你想要访问的位置处(以字节数来计算),并且指定你想要访问的字节数目(0代表全部)。然后你需要做的全部就是向函数传递指向你将用来访问顶点缓存的内存指针的指针(转型成为一个VOID数据类型)。下面是一个锁定整个顶点缓存的调用的示例:

// pD3DVB =pre-intialized vertex buffer object
BYTE *Ptr;
// Lock the vertexbuffer memory and get a pointer to it
if(FAILED(pD3DVB->Lock(0,0, (void**)&Ptr, 0))) {
    // Error
}
 


        注意:

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

        在其创建时使用D3DCREATE_WRITEONLY标记的顶点缓存无法进行读取,只能够写入。如果你想从一个顶点缓存读取数据(就像D3DX中某些函数那样),你应该在你调用CreateVertexBuffer函数时将Usage参数设为0。

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

 

        在你结束了对顶点缓存的访问之后,始终要记得在每个Lock函数的调用之后跟上一个IDirect3DVertexBuffer9::Unlock函数的调用:

HRESULTIDirect3DVertexBuffer9::Unlock();

        解锁一个顶点缓存确保Direct3D可以开始安全地使用它了,因为它知道你不会再修改该缓存内的任何数据了。

 

        当心!

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

        确保最小化在调用Lock函数和调用Unlock函数之间的时间量。你处理被锁定的顶点缓存越快则越好,因为Direct3D必须停下来等你弄完顶点缓存,以便能够访问其内部包含的数据。

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

 

2.4.3.3 Stuffing in Vertex Data (填充顶点数据)


        现在你有了你的顶点结构体,描述器以及缓存,并且你已经锁定了缓存并准备储存顶点数据。因为你已经从Lock函数的调用中得到了指向顶点缓存内存的数据指针,你所需要做的就是将适当数量的顶点复制进顶点缓存。

        继续我的例子,并利用我已经定义的顶点格式(使用3-D格式和漫反射颜色成分),我在一个数组内部创建了一个局部的顶点数据集合:

sVertex Verts[4] = {
  {  -100.0f, 100.0f, 100.0f, D3DCOLOR_RGBA(255, 255,255, 255)  },
  {   100.0f, 100.0f, 100.0f, D3DCOLOR_RGBA(255, 0, 0,255)  },
  {   100.0f, -100.0f, 100.0f, D3DCOLOR_RGBA(0, 255, 0,255)  },
  {  -100.0f,  -100.0f, 100.0f, D3DCOLOR_RGBA(0, 0, 255, 255) }
};


        锁定顶点缓存,这样得到一个指向顶点缓存内存的指针,然后复制这个局部的顶点数据(并且在弄完之后解锁顶点缓存):

 

// pD3DVB =pre-initialized vertex buffer object
BYTE *Ptr;
 
// Lock the vertexbuffer memory and get a pointer to it
if(SUCCEEDED(pD3DVB->Lock(0,0, (void**)&Ptr, 0))) {
 
  // Copy local vertices into vertex buffer
  memcpy(Ptr, Verts, sizeof(Verts));
 
  // Unlock the vertex buffer
  pD3DVB->Unlock();
}


        这就是构建一个顶点缓存并往里填充顶点数据要做的所有事情!要使用顶点信息,现在你只需要指定一个流源(stream source)和顶点渲染器(vertex shader)。

 

 

2.4.4 Vertex Streams (顶点流)

 

        Direct3D让你能够通过一系列不同的流(stream)——称为顶点流(vertex streams)——将顶点喂给(feed to)渲染器(renderer)。你可以通过将多个流的顶点数据整合到一个流中来创造非常令人印象深刻的结果,但是在这本书中,我只是用一个流,因为使用多个流的复杂性已经超出本书范围了。

        为了将你的顶点数据复制到一个流中,你使用IDirect3DDevice9::SetStreamSource函数:

 

HRESULTIDirect3DDevice9::SetStreamSource(
  UINT    StreamNumber,       // 0
  IDirect3DVertexBuffer9* pStreamData,     // Vertex buffer object
  UINT    OffsetInBytes,          // Offset (in bytes) of vertex data
                                                     //in the vertex buffer.
  UINT    Stride);                        // Size of vertexstructure


        你现在设定顶点流源(vertex stream source)要做的所有事情就是调用这个函数,并传递指向顶点缓存对象的指针、提供用于存储顶点结构体的字节数(使用sizeof)。

        如果你在顶点缓存中存储着多余一组多边形(比如几个三角形带),你可以将OffsetInBytes参数设为顶点数据的offset。例如,如果我使用顶点缓存来储存两个三角形带(第一个三角形带位于offset 0处,而第二个三角形带位于offset 6处),那么我通过设置适当的offset(在这种情况下,第二个三角形带的offset为6)来绘制第二个三角形带。

        从上一节中在顶点缓存中储存顶点的例子出发,你可以使用下面的方法:

// g_pD3DDevice –pre-initialized device object
// pD3DVB =pre-initialized vertex buffer
if(FAILED(g_pD3DDevice->SetStreamSource(0,pD3DVB,                         \
0, sizeof(sVertex)))) {
    // Error occurred
}

 

2.4.5 Vertex Shaders (顶点着色器)

 

        作为使用顶点来绘制图形的最后一步,你需要理解顶点着色器的概念。顶点着色器(vertex shader)是这样一种机制,它操控顶点的载入和处理;这具体包括改变顶点坐标、运用颜色和雾化功能以及很多其他的顶点组件。

        一个顶点着色器可以以两种形式出现。它可以是固定的(fixed)顶点着色器(其中所有的用于完成一般功能的函数都已经是内建的了),也可以是可编程的(programmable)顶点着色器(其中你可以在渲染到显示屏之前自定义函数来改变顶点信息)。

        尝试去解释可编程顶点着色器超出了本书的范围。(然而,本人在更新版的代码中一直使用的是用HLSL语言写的可编程顶点着色器。)相反,我集中于使用固定的顶点着色器,因为它们包含了你所需要的所有的功能。(未必;在十年前你可能认为足够了,但是对于新一代的玩过很多酷炫游戏的我们来说,这是远远不够的。)

        为了在你的顶点上使用固定顶点着色器,你将你的自定义顶点的FVF描述器传递给IDirect3DDevice9::SetFVF函数中:

HRESULTIDirect3DDevice9::SetFVF(
  DWORD        FVF);         // Vertex FVF descriptor


        使用前面的函数跟下面一样简单:

// g_pD3DDevice =pre-initialized device object
// VertexFVF =pre-defined vertex FVF descriptor
if(FAILED(g_pD3DDevice->SetFVF(VertexFVF))){
    // Error occurred
}


        你现在已经建立了顶点信息了。下一个步骤是建立将顶点(在局部空间中)放置到其世界空间坐标时所需要的各种各样的变换。当然,只有你在使用3-D坐标的时候才需要这一步。


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

 

        好了,这几个小节的内容总算讲完了。可能读者看得不是太明白,尤其是那些函数的具体参数的意义之类的。其实这个大家可以参考SDK文档,或者参考龙书第二版。另外这里所用的设置顶点的方法在本人的更新版代码中是不会使用的,而是用的是龙书第二版中更加先进、更加灵活的方式,所以看得不太懂也没什么关系。


版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/u011873969/article/details/39270753

智能推荐

使用zTree实现树形下拉框_ztree树形下拉框_Mr.Yakun的博客-程序员宅基地

这两天项目中需要用到选择属性下拉框,树状结构我用了zTree,下拉框本想用插件的,但是没找到好的插件就只能自己搞出来了,正好网上也有一些zTree下拉框的例子,就做出来了HTML:&lt;form id="theform" onsubmit="return false;"&gt; &lt;ul class="forminfo"&gt; &lt;li&gt;&lt;label&g...

Google Earth Engine ——GIMMS NDVI是由NOAA的几个AVHRR传感器为全球1/12度的纬度/龙格生成的数据集_gimms ndvi 3g介绍_此星光明的博客-程序员宅基地

GIMMS NDVI is generated from several NOAA's AVHRR sensors for a global 1/12-degree lat/lon grid. The latest version of the GIMMS NDVI dataset is named NDVI3g (third generation GIMMS NDVI from AVHRR sensors).GIMMS NDVI是由NOAA的几个AVHRR传感器为全球1/12度的纬度/网格生成的。最新

vue前端不通过路由实现面包屑功能,弹窗与弹窗之间不通过路由实现面包屑功能,二次封装element ui的面包屑breadcrumb,不使用路由实现面包屑功能效果_element 面包屑不用路由_ryipei的博客-程序员宅基地

在一个单页面或者一个弹窗里面,不用路由来实现element ui的面包屑的跳转等功能可以实现多个.对element的面包屑进行二次封装,可以实现在一个页面里或者一个弹窗里达到路由的效果,实现点击进入下级,点击上级返回。element ui的面包屑不使用路由在单个实现跳转

android audio arch_q6routing_stream_open_Android系统攻城狮的博客-程序员宅基地

原址ALSA System on Chip(ASoC)ASoC 驱动将一个audio子系统分成四个部分:Machine driver, Platform driver, CPU driver以及Codec driver。Machine 驱动将平台,CPU以及codec驱动绑定在一块实现在kernel/sound/soc/msm/定义前端FE和后端

在JBOSS服务器上使用Myfaces的JSF实现_iteye_7212的博客-程序员宅基地

JBOSS AS服务器默认使用的JSF实现是SUN的RI,要把他替换成Myfaces需要:1.移除JBoss的现有的JSF实现RI 修改JBOSS_HOME下的server\\deploy\jboss-web.deployer\conf\web.xml文件,把JSF的监听器注掉,并删除jboss-web.deployer 目录下的jsf-libs文件夹[color=darkr...

【题解】【PTA-Python题库】第3章-21 判断回文字符串 (15 分)_Tuenity的博客-程序员宅基地

判断回文字符串输入一个字符串,判断该字符串是否为回文。回文就是字符串中心对称,从左向右读和从右向左读的内容是一样的。输入格式:输入在一行中给出一个不超过80个字符长度的、以回车结束的非空字符串。输出格式:输出在第1行中输出字符串。如果它是回文字符串,在第2行中输出Yes,否则输出No。输入样例1:level输出样例1:levelYes输入样例2:1 + 2 = 2 + ...

随便推点

穿越防火墙技术要求简介 _我是一个小胖子的博客-程序员宅基地

<br /> <br />一、问题描述<br />网络地址翻译和防火墙 (NAT/FW)的存在,阻断了包括H.323在内的多种多媒体通信协议,因为NAT设备仅仅完成消息的IP头的地址/端口的翻译,使消息的IP头和消息净荷中的地址/端口信息不一致,导致消息接收方无法正确对该消息做出响应,通信双方的媒体流通道无法正常建立。 <br />H.323多媒体通信中传输层端口动态分配并通过H.245消息相互协商,但网络中的防火墙设备无法预先得知该端口号信息,从而会阻止媒体流通过它。另外,防火墙设备在没有内网发出相应消息

net use 命令使用_独正己身的博客-程序员宅基地

WINDOWS2003(或其他的windows服务器)在局域网共享文件时,有时候测试的时候要更换登陆的用户名。使用NET USE 命令可以方便处理。1)删除已有的连接,避免出现“一个用户使用...多重连接”的问题。net use * /delete如果提示“列表是空的”,表示当前没有任何关联连接;如果提示“您有如下远程连接... 继续运行会取消连接”,输入“Y”或者“y”2)建立非空连接:net use \IP\ipc$ "密码" /user:"用户名" (同样有3个空格).

matplotlib为图片上添加触发事件进行交互_风 先生的博客-程序员宅基地

这篇文章的目的出于实验的需要,我需要对图片上的部分区域做出涂抹标记,本来是选择用opencv做交互的,但在需要进行图像的输出以及鼠标时间添加时,opencv出现错误。解决方案网上有很多,尝试以后依然bug,这里先做一个记录,有时间再来处理。错误报告如下:OpenCV Error: Unspecified error (The function is not implemented. Rebuild

java并发编程实践学习(四)对象的发布和逸出之this逃逸_likaistart的博客-程序员宅基地

原文来源: https://blog.csdn.net/aitangyong/article/details/25122741《java并发编程实践》的第三章,对象的发布和逸出,作者提到了2种常见的对象逸出情况:在构造函数中注册事件监听,在构造函数中启动新线程。示例代码如下:public class ThisEscape { public ThisEscape(EventSourc...

dyld binding test_weixin_30532759的博客-程序员宅基地

=========================================================================a.c----------------------------------void main (int argc, char **argv) { printf ("Salve, Munde!\n"); pri...

MongoDB学习(七)$操作符表达式大全及实例_代码与酒的博客-程序员宅基地

写在前面        本文基于官网v3.4的文档翻译整理而来,包含了绝大多数常用的操作符,更多详细的使用还请参考官网(戳这里)。本人水平有限,若各位看官发现错误,还望及时留言指出,以免误导!共同学习,共同进步!1.查询和投影1.1 比较操作符$eq语法:{ : { $eq: } }释义:匹配等于(=)指定值的文档举例:查询age=20的文档:

推荐文章

热门文章

相关标签