用DirectX构建3D引擎需要了解的概念.doc_第1页
用DirectX构建3D引擎需要了解的概念.doc_第2页
用DirectX构建3D引擎需要了解的概念.doc_第3页
用DirectX构建3D引擎需要了解的概念.doc_第4页
用DirectX构建3D引擎需要了解的概念.doc_第5页
免费预览已结束,剩余1页可下载查看

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1. 顶点格式DirectX SDK中使用D3DFVF可变顶点格式来定义顶点数据,所需要提供的顶点数据有很多,下面列出一些常用的:顶点坐标、顶点法向量、顶点着色的RGBA值、该顶点对应贴图的二维坐标UV值。2. 顶点索引由顶点生成面是通过构成三角形来绘制的。每个三角形含有3个顶点数据,这三个顶点数据可以连续写入,但是更常见的做法是创建顶点缓冲区,用顶点索引值来读取顶点数据。3. 关于渲染(rendering)D3D的具体渲染过程请参见教程或查询有关书籍。我这里只是概要地介绍一下容易混淆的概念。首先,灯光的值是赋给设备的,D3D中所有的方法都封装在IDirect3D的超类中的,其中所有关于渲染的方法都封装在IDirect3DDevice子类中。渲染分成多个渲染管道(pass)来完成,每个管道完成一种材质的匹配。因此在设计渲染过程的时候要注意,除了合理设计顶点和三角面格式外,还要注意如何处理一个角色的不同材质的各部分的渲染顺序,以及在适当的时候打开Z缓冲(z-buffer),或者采用画家算法。4. D3D的三种坐标系不要误会,这里说的三种坐标系和计算机图形学上面的世界坐标系(world)、本地坐标系(local)、万向节坐标系(gimbal)等不同。使用D3D渲染时,你根本不用处理这些底层的东西。D3D中需要了解的坐标系只有3个:世界(D3DTS_WORLD)、视(D3DTS_VIEW)、投影(D3DTS_PROJECTION)。此外,要注意的是,D3DTS_WORLD是一个宏,除了上述三个变换之外还有8个贴图变换,这些暂时不用管它。D3DTS_WORLD对应于整个世界坐标系中各物体的坐标变换,这意味着,渲染物体的位置变化时,你不一定需要去改变顶点数据,仅仅将世界坐标系往相反方向变换后渲染即可,这点在后面详细叙述。D3DTS_VIEW对应于摄像机(camera),D3DTS_PROJECTION对应于视口(viewport)。如果你使用或学习过其他3D引擎,你应该对视口的概念很熟悉。视口是相对于摄像机而言的。一个视口返回一个摄像机的取景,然后对其作相应变化,例如设定近截面和远截面、或者局部地拉伸坐标等等。最后,视口处理好的数据发送到指定窗口的指定位置。5. 处理顶点数据顶点数据就是第一条中列出的那些,大部分时候如果你自己写引擎的话需要自己从相关建模工具中导出顶点数据。对于市面上常见的建模工具,从3ds Max和Maya这些软件中导出模型还是相当简单的。但是另一些,像Blender,poser等等,则有点困难。保存数据的时候,如果能转成xml格式当然再好不过了,ascii编码的文件次之,但是如果mesh数据是二进制的,要转换这样的文件将是十分困难的。美工人员选择建模工具时要注意,尽量选择那些有求顶点数据值的脚本的建模工具。对我个人而言,如果让我仅仅为了写一个mesh文件转化的插件而去学习一种mesh格式的文件的话,我会十分不乐意的(*.max、*.obj、*.mb、*.x文件格式是什么样的我统统不懂,而且以后也没学习的打算)。6. 三角带化(strip-trianglize)D3D(其实不只是D3D,openGL也是,我所知道的3D处理工具都是)中处理mesh网格数据主要有三种格式:三角链(triangle list)、三角带(triangle strip)、三角扇(triangle fan)。关于这三种格式具体怎么样,请参见D3D帮助文档。三角链是常规格式,后两者都是为了节省index空间而设置的。我曾经写过一个将mesh数据转存在二叉树中的方法,这种方法似乎更省空间,但是,为了使用硬件加速,我们的选择不多,只能将数据转成三角带或者三角扇。其中,三角带的格式是最常见最通用的。下面介绍一些三角带化的基本思路。用D3D渲染时,使用DrawIndexedPrimitive方法时有个参数是设定所渲染的物体的数据类型的。由于将大量数据放入同一个渲染通道来渲染可以加快渲染数据,所以我们要寻找一种方法可以尽量减少三角带数量,或者,干脆把所有三角形都放入一个三角带中。理论上,不可能把任意构型的mesh数据处理成一个三角带,但是,通过一点点的变通,这是可以实现的。看下面这张图:这张图是之前写网格数据转成二叉树格式存取的插件时用过的,所以别管右边的东西,只管看图就行了。如果要将这样一张网格转换成三角带的话,至少需要两条三角带,其中的一种转换方法可以转成:三角带11726534、三角带2796854。如果了解这样一个规则:三角形带中的坏三角形不渲染输出,我们就可以把这张mesh转成一个三角形带:1726534458697。当然,如果有多个相同材质的mesh,你也可以把它们全都转成一个三角形带,拿这个例子打个比方,如果要在534和796间拟合,你也可以把这个mesh转成:172653447796854。这两条三角带分别有344、445两个和344、447、477、779四个坏三角形不参与绘制。能被互不干扰地三角带化的顶点必须具备这个条件:该顶点的度数(degree)必须是3的整数倍,更确切地说,应该是该顶点的邻接面个数是3的整数倍,并且邻接面之前如果有镂空,则相邻两个镂空面之间的面数也应该是3的整数倍。如果你想用图论的概念处理这些数据,你会非常不愿意看到有镂空面的网格,这处理起来将相当麻烦。所以我们将这个概念一般化:如果三角形的索引为0,则改三角形表示镂空面。一个顶点的度数除以3取余,会有0、1、2三种结果。0表示该点可以被正常三角带化;余数为1、2时,表示该顶点一定是一条常规三角带(不含坏三角)的起点或终点,也就是说,如果要将经过该顶点的多条三角带变为一条三角带,必须把该顶点处理成坏三角顶点。我们需要的三角带是数量越少越好的,但是在消隐算法的过程中会有部分三角形不参与绘制,如果在每次消隐后三角带化,则开销太大,不值得。我们希望从建模工具导出mesh数据时就直接给出各个三角带,因此,有这样一个原则:先关注三角带的长度,而三角带数次之。我个人认为,好的mesh文件格式应该不是给出含有坏三角的三角带,而是给出拥有最长三角带的一系列三角带。因此,先完成最长三角带的计算,然后在此基础上给出一种含有最少三角带数的三角带化划分,最后,将该划分的各个三角带都最长化(也就是说,不管某三角形有没有归入一条三角带,使每条三角带都最长)。至于这样做的原因,下面再分析。7. 消隐算法(cull algorithm)消隐算法有多种称法,hidden surface cutting、culling等等,自己看外文书的时候注意点,这个还是很好区分的。主要的消隐算法分为三类:Z缓冲(z-buffer)、画家算法(paint)和树查询算法(query)。最常见最简单的方法是Z缓冲法,包括W缓冲等等类似方式的各种算法。这种方法规则简单,硬件上容易实现。但是计算数据量很大,所以,尽量减少Z缓冲的计算量可以提高渲染速度。其次是画家算法。这种方法通过调用不同的渲染顺序,由于后绘制的总是覆盖在先绘制的上面,据说这种方式类似于画家作画的方法,所以被称为画家算法。画家算法并没有减少绘制量(绘制和渲染都是指render),使用这类方法主要是为了节省查询时间并保证渲染的结果正确。十分复杂的场景单纯用Z缓冲和画家算法都不合适,场景查询本身是有CPU和内存开销的,但是如果这个开销能大大减少绘制的三角形数量并加快渲染速度的话,就是值得的。因此产生了各种各样的树算法。二元空间分割法(就是所谓的bsp)是最常用最经典的算法,这种方法不仅可以作为画家算法的依据,还可以一次消除很多隐藏面。如果地形是平坦的,并且墙壁都垂直于地面、墙壁之间也互相垂直的话,这种方法可以退化成红黑树的算法,不要以为这样的情况不存在,记得以前玩的红警、帝国时代都是这样四四方方的。这样可以保证一个常量级的上界。八叉树(octree)的算法是bsp在速度方面的改进,通过大量消耗内存来保证常规场景查询能有一个线形阶的上界。四叉树(quadtree)则是八叉树的退化,类似于二叉树到红黑树的退化。所有这些树算法中,最经典的应该是bsp,当然在三维空间中构建凸包的做法可能有点难懂,bsp是通过构建二维空间的凸包推导到三维空间的。具体的树算法请查阅计算机图形学方面的书籍,这类资料非常多,我不赘述。以后我将在我自己的引擎中实现其中至少一种算法。除了上述三类方式之外,还有各种各样的边界扫描法和边界盒查询法。边界扫描主要是为了保证细致的渲染结果的,内存开销太大不适合做游戏引擎。边界盒查询则很简单,通过查询一个规则图形(通常是矩形体或圆柱)来确定一个物体是否在视范围内、是否和其他物体碰撞、是否被其他物体遮盖等等。它需要Z缓冲或其他方法辅助才能正确地显示物体。8. 可渲染物体的分类、静态和动态几何体(staticdynamic objects)几乎所有的3D引擎都是这样分类的,这样做的原因是为了减少计算量。通常,地形以及地形覆盖物都是静态几何体,这样的几何体相互之间的位置都不变,如果你要写一个场景管理器的话,静态物体都是处在根节点的层面上的。BSP等等消隐算法只能对这些物体进行划分,如果你将一个动态几何体划入BSP的话,那么每次该物体移动时,你都要重新生成BSP树,简单地说,这是不可能的。BSP只划分静态几何体,动态几何体处在BSP的哪个子空间(sub space)中是通过查询得到的,通常渲染一个场景(stage)的流程是:绘制静态几何体(其中具体过程是先通过树查询方法排除一些隐藏面,然后计算法向量和摄像机lookAt相量的乘积,排除一些背对着lookAt相量的面,最后通过画家算法或者Z缓冲算法渲染输出),然后通过树查询确定动态几何体的位置,绘制动态几何体,最后根据上次查询的结果决定是否要重画部分静态几何体。如果想要省去最后重画的步骤,可以在一开始就打开Z缓冲一直到最后,如果显卡足够好或者数据足够少是可以这样做的。或者用alpha混合(alpha blend)将部分渲染好的静态几何体置0或置1与动态几何体混合,或者使用D3D中的stencil模版来使静态几何体透过或不透过动态几何体。我尝试过使用alpha混合,stencil还没有用过,但是使用起来应该是很简单的,具体方法参见D3D帮助文档。现在你应该知道步骤6划分的原因了,通过树查询和法线查询的排除,相当多一部分三角形是不参与绘制的。要使余下来的三角形变成三角带并送入渲染管道,你可以直接在已有的三角带上剔除,例如上文所说的三角带1726534和796854,最长化后变成1726534和7968543,这样,假设剔除了三角形256,则两条三角带分别为17266534和7968543,合并成17266534458697。这里还看不出最长三角带化的好处,如果一个mesh足够大,并且被剔除的面正好是两条或两条以上三角带的公共面,那么很容易就能选择在哪个三角带中剔除该面并且合并这些三角带最好。9. 动画(animation)在设定mesh文件格式的一开始,就必须知道该文件格式不光要能渲染成静止的物体,也要能方便地支持动画操作。最简单的方法就是分层。下面详细地介绍下几种动画的分类和原理。从程序的角度看,动画分为两种类型:刚体运动(transform)和变形动画(morph)。最理想的动画是刚体的,刚体运动时,该物体的各顶点之间的相对位置都不变,这里要从D3D的锁存机制说起。D3D写入顶点数据时,必须在Lock()和Unlock()函数之间写入数据,并且,一段顶点数据废弃后,重写顶点数据还要调用Lock()和Unlock()一次,这还是比较好的情况,如果顶点数量有变化,将要毁掉内存池重新建立。众所周知,CPU速度和内存速度不是一个数量级的,也就是说大量的读写和分配内存操作会极大地降低程序运行速度,因此,制作动画的时候,顶点数据写入后最好就不再改变,直接设定世界坐标系的变换由CPU和GPU渲染输出,这样可以节省很多开销。骨骼动画就是这样出现的,建立一个骨骼的层次模型,将每个顶点都绑定到某个或某些骨骼上(通常,每个顶点由一个骨骼约束是最理想的,这样做动画时角色的身体会裂开,但是计算量最小,而且不用改变顶点坐标,其他还有一些辅助方案),每根骨骼的运动带动一系列顶点的运动,但是这些顶点间的相互位置是不变的,这样,就可以将这些顶点归入该角色的某一部分,它在场景图中作为一个孤立的动态物体被放置在场景图的某个子节点上,渲染输出时,先SetTransform(D3DTS_WORLD, &matrix),然后渲染该部分物体,再SetTransform,渲染另外一部分物体,通过这样一个迭代的过程直到完全渲染好一个角色为止。说明一点,D3D的*.x格式文件就是层次模型的,ID3DXMesh类提供了DrawSubSet的方法,不过我不喜欢用D3D封装好的函数,我更想用底层的方法来实现。倒不是因为我装B,而是我不会用那些高级方法,我不了解*.x文件格式,更不了解D3D自己实现的动画原理,所以如果我使用那些方法的话,我不知道怎样写程序更有效率。更何况,我只是一个学生,如果我现在多了解一些东西的话,以后我才可以走得更远。和骨骼模型相对的,如果一个物体的形态发生改变,也就是说各顶点的相对位置都发生改变的话,骨骼动画是无能为力的。这时候,我们只有两个办法去实现这种morph动画:要么将这段动画的各个阶段的顶点位置插值预先计算出来,然后创建顶点数据的时候预先把这些顶点都写入,在绘制动画的时候根据精灵(spirit)的顶点数据(换句话说就是创建了很多点的副本)重新写入mesh的index值;要么就设定几个关键帧,根据时间插值,在每次渲染前计算并重新写入顶点数据。这是两种非常猪的方法。前一种方法简直就是拿内存当纸烧,后一种方法是用锤子砸CPU。不幸的是,很多计算机图形学方面的东西在我看来都是非常非常猪的方法:三维变换中使用44矩阵反计算原来的数学公式,然后发现该矩阵中有很多0,为了尽量还原三维变换的计算,又用稀疏矩阵的算法去优化计算过程。知道吗,XYZ三个坐标本来只需要三步计算就能完成,用44矩阵计算,乘法和加法各需要16*4=64步,一共多费了(128-3)*n步计算。牢骚发到这里结束。矩阵计算费时间我不管,因为这些是D3D封装在底层的东西,我想改也改不了。Morph和关键帧动画(key frame)也是现在非常常用的算法,因为这些算法产生的动画效果相当好,至少不会像骨骼动画那样角色的身体从中裂开。当然,骨骼动画也可以通过在关节处的顶点关键帧插值的方法来改善。10. 关于定时器在Windows NT 4.0以上的环境下,似乎我们并没有什么选择。也许你用timeGetTime()或者QueryPerformanceCounter()来查询时间来实现帧计数。但是比较好的做法应该是采用SetTimer()的方法。这样做的CPU开销最小。需要了解的是,CPU每55毫秒会产生一个时钟中断,8086本身有条8H中断,如果能截获这个中断,将回调函数(callback)的地址写到中断向量表(IVT与IDT),然后截获1CH中断来调用函数,这样做可以用最少的CPU开销实现定时器。很久以前的DOS模式下的游戏都是这样来构建定时器的。可惜现在再也不行了,winNT内核对这些指令实行了保护。我想,如果能用DDK实现这样一个功能,游戏的效率可以提高很多,至少不用使用busyloop来对帧计数了。11. 写给美工的很久很久以前我也学过3DS和MAYA,也有过游戏建模有什么要求这样的疑问。现在我大概都了解了,所以打算写一点小贴士,希望对大家有帮助。首先,一个模型有多少面是由程序指定的。一般,坐标存成float,索引存成unsigned short,贴图坐标存成float的话,每个点要占去4*3+2+4*2=22字节的空间,如果还要加上顶点法向量的三个float的话,就是要34字节的空间,每个面有3个顶点,占去3个unsigned short型索引,不包含分层信息的话,一个面还要用6字节空间来存储,大概有多少内存就能存多少这样的数据。一般1GB内存的机器上,大概可以有500MB左右的空间可以放顶点数据,角色和场景大概是各占一半的,在室外的场景中,场景占的比例多点,室内是角色占的比例多点。大概就是这样子的。其次,贴图的张数,D3D9支持最多8个贴图通道,就是说,每个角色最多可以有8张贴图,当然,大部分情况下,一个角色的贴图是1张。画贴图的时候要特别注意,有时候在MAYA中看效果很正常,但是在游戏中看就贴歪了,这种情况很正常,因为贴图本身就是一种近似的表现。何况,不同贴图的采样(sampler采样器)方式会导致不同结果,这时候就需要你重新绘制贴图了。因为不可能在程序中为每张贴图使用不同的调整算法。关于法线贴图,如果你在欧美的大公司工作的话,这也许是要掌握的技巧。总的来说,法线贴图是很耗资源的,如果我写引擎的话,我可能不会考虑实现这个功能。你需要了解的是,法线贴图实际上是一系列像素级的值,你可以把一张法线图保存为一张黑白的png图,或者是更省资源的raw图,总之,程序可以根据这些图读取法线的三个坐标值,然后送入渲染管道。通常绘制法线贴图可以手绘,也可以建立一个更多面数的模型,也就是从高模到低模地生成。MAYA中有这样的指令。当然,手绘法线贴图效率会更高,但是需要相当的美术技巧和对法线贴图的理解。关于材质,你所知道的材质有phong、blinn、lambert、anisotropic等等,但是要知道,这些效果都不是在游戏中能表现出来的,游戏是实时渲染的,而这些离线渲染的方法是不适用的。所以,和很多程序员说这些东西他们可能根本不知道这是什么。对于程序而言,所有的材质无非就是4个值:diffuse、ambient、emission和specular,最多加上个alpha混合值。所以,建模的时候点渲染察看效果是没有太大用处的,游戏中渲染能达到的效果大概就是你在建模窗口里看到的效果。对于程序员,也许他们知道的材质只有一种,Gouraud算法。所以,通常游戏建模的时候都是用phong或者lambert材质来观看效果,这些材质比较接近于游戏中所能表现出来的效果,并且注意,把高光调低一点,游戏中能体现出来的高光效果是很有限的。贴图比材质更重要,具体的材质设定还需要你在看了角色在游戏中的情况再调整。关于材质的补充说明,也不是所有的引擎都不区分phong、blinn、lambert、anisotropic这些材质类型的。GLSL着色器实现了不同材质不同着色的功能,也就是说nVidia某型号以上的芯片中是有各种材质的渲染计算电路的。所以上面所说的也不绝对,看程序的要求而定。不过那样高级的引擎应该不多见。关于网格布线,三角形四边形甚至多边形都可以,反正最后都可以转到三角形的。游戏建模和电影建模不同,后者要考虑贴图的准确性和方便性,所以大都使用四边形建模,甚至采用NU

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论