将模型导出到Direct3D

(零)序言
你在3ds
max(以下称作max)或Maya(以下称作maya)中为一款游戏制作各种3D模型,你希望自己所做的模型能在一款3D游戏引擎中被活灵活现地显示出来。在那一刻你会感到非常喜悦,因为玩家那充满热情或惊叹的眼神使你感到自己的作品被承认、被赞赏。嗯,这的确值得我们去想象!

 
不过,先别急着去幻想那些情景啦,因为你在进入梦乡前会有足够的时间来想象这些情景。在此之前,你还是要务实一点,先要为能得到那样的眼神而付出相应程度的努力。

 
不知你是否清楚这样一个事实(或,我现在需要告诉你这个事实),那就是,你在max或maya中保存的.max或.ma、mb格式的文件是不能直接“放到”引擎中去显示的。一般来说,游戏引擎都不支持这些格式,通常它们会各自定义一些专门的文件格式来存储模型,而只有具有这种格式的文件才能被引擎直接使用。因此,你就需要将max或maya制作的模型导出为那些格式。

 
      

要知道一般3D游戏引擎都是基于Direct3D(以下简称D3D)或OpenGL编写的。而由于不同引擎支持的格式各不相同,所以在此很难逐一去讨论。正如文章的标题所示,本文主要讨论如何将模型导出到基于Direct3D编写的应用程序或引擎,再或者更精确地说(如果你了解Direct3D的话)导出到Direct3DX的.X文件格式。本文的首要目的就是让你了解导出一个3D模型的概念和具体做法;其次介绍了一种方法来验证你的模型是否能在D3D游戏中被正确的显示出来;最后指出在max中导出.X格式时会遇到的一个小麻烦及解决方法。

 
最后,我假设你用的3D建模软件是max或maya,整篇文章都是围绕在这两种软件中进行导出来说明的。并且我进一步假设你使用的是当前主流版本的max或maya,即3ds

max 5.0或Maya 4.0~5.0,如果你使用的是其它软件,或者版本号太旧或太新,则以下内容可能不是太适合于你。
 
      
OK,开始吧~!
(一)如何导出?
.X文件格式
.X文件是Direct3D开发包所直接支持的唯一一种3D模型格式,也就是说,如果你是一个程序员,你可以很轻松地写出一个Direct3D程序用于显示.X文件所存储的3D模型。.X文件包含了对于一款3D游戏而言有用的信息,诸如模型中顶点的位置、顶点颜色、材质、UV纹理坐标、动画关键帧等等。另外,.X文件有两种存储方式:文本和二进制,两者在功能和用途上没有任何差别,只是形式不同而已。

安装导出插件
      
另外,以下内容中的一些插图和说明以max为例。
 
l      
3ds max 5.0
1)       
如果max已经启动,则先关闭它。
2)       
复制:
XSkinExp.dle
到:
(3ds max所在目录)\plugins\
3)       
启动max后,选择菜单:
Customize-> Plug-in Manager...
检查弹出对话框的列表框中是否存在“XSKINEXP.DLE”,并且其Status是否为“loaded”,如下图:
 
       

l      
Maya 4.0~5.0
1)       
如果maya已经启动,则先关闭它。
2)       
复制:
xExport.mll
到:
(maya所在目录)\bin\plug-ins\
3)       
复制:
bicubicbezierpatches.mel和xfiletranslatoropts.mel
到:
(maya所在目录)\scripts\others\
4)       
启动maya后,选择菜单:
Window->Settings/Preferences->Plug-in
Manager...
检查弹出对话框的列表框中是否存在“xExport.mll”,并且是否构选了“loaded”和“auto
load”,如下图:
 
导出模型
l      
3ds max 5.0
1)       
选择菜单:
File->Export
在弹出对话框中选择保存类型为“X-File (*.X)”,并指定保存路径和文件名。
2)       
然后,可在弹出的导出选项对话框中进行设置,配置好以后按“Go!”按钮,导出选项和解释如下:
 
 
各个选项含义如下:
     
导出选项描述
     
Text文本
     
Binary
     
Binary Compressed二进制
     
压缩的二进制
     
Export Patch Data如果存在高次(high-order)表面则将导出patche以替代polygon网格
     
Include Animation Data是否同时导出动画数据,需要指定动画采样速率(单位:帧/秒)
     
Looping Animation Data是否循环播放动画

l      
Maya 4.0~5.0
1)       
选择菜单:
File->Export All…
在弹出对话框中选择保存类型为“xExport (*.*)”,并指定保存路径和文件名。
2)       

在文件保存的对话框中,按下左下角的“Options…”按钮后,可在弹出的导出选项对话框中进行设置,配置好以后按“Export”按钮,导出选项和解释如下:

 
 
各个选项含义如下:
     
导出选项描述
     
File Options选择文本、二进制、压缩的二进制
     
Tesselation
Options选择高次(high-order)和细分(subdivided)表面,或none为忽略这些表面
     
Material Options选择是否同时导出材质、是否翻转UV纹理坐标轴、纹理文件路径是采用限定路径,还是不限定路径
     
Animation Options“Export
     
Animation”用于选择是否保存动画属性。如果选择保存动画属性和“per-frame”后可以通过“Frame
     
Step”或拖动条来设置采样率;默认值是1,表示1:1的关系。该值与Maya的当前回放(playback)速度有关。
     
Skinning Options“Export
     
Skinning”允许你选择是否保存网格中skinning信息。如果没有选择该选项,则网格不能自我变形(deform),但如果网格继承于带有动画的层级的话,则它只可以在空间中移动。

观察导出结果
1)       
确认已安装了DirectX 9.0运行库(如果这个链接已失效,请点击这里来寻找最新的DirectX)。
2)       
运行Mesh Viewer,选择菜单:
File->Open Mesh File
选择相应的.X文件
1)       

你可尝试通过鼠标左、中、右键来旋转、缩放、移动地观察模型;你还可以选择显示法线、是否剔除(Culling)背面、以何种方式(点、线框、面)来显示模型等,以此来查看模型是否正确。

 
如果在Mesh
Viewer中一切显示正常,那么在基于Direct3D的程序中,一切也会表现良好。如果有些面没有正常显示或导出时就失败了,则说明你的模型存在一些问题,需要进一步检查一下模型,看看法线是否正确、多边形的边是否合拢、是否存在缝隙等。在max中进行导出时你会遇到一个小问题,你在导出前多做一件事情来解决这个问题。继续前进,来让我们看一下在max中导出时发生了什么“怪”事情。

(二)max的导出问题
起因
如果你以为将max制作的模型导出到.X文件后会有一样的外观的话,那你可把世事想得过于完美了。你至少会发现一个非常明显的错误,那就是:模型被关于XY平面作了镜像,譬如拿XY平面比作一面镜子,你在Direct3D中看到的将是那个在镜子中的模型(注意,我在这里没有提及maya,那是因为maya的导出插件已经处理了这个问题,真要感谢编写那个插件的那位作者!)。

 
以一个max模型(该模型系杨·为一同志所做,感谢他提供这个模型,正因为在导出这个模型时显露出来的错误使我更深入地研究了本文所涉及的主题J)为例,它显示出来导出前后模型的变换,导出前我们通过XY平面看到了狮子的背面,而导出后通过XY平面看到的却是狮子的腹部。如果想象原来的狮子站在一面镜子上,那么导出的就好比是那个镜子中的狮子。

 
         
(左图:在max中的模型;右图:导出后在Mesh Viewer中的模型)
 
好,现在你应该活动一下筋骨了,来尝试导出一个模型到.X文件吧。我建议你的试验模型最好不要是对称物体,并且具有一点能标识方向的几何体,我在这里提供了一个模型,如果你想偷点懒的话可以直接用它来做这个试验。然后亲身感受一下这个问题。

经过
      

那是什么原因造成了以上的问题呢?如果你是一个喜欢打破砂锅问到底的人,那这一段正是为你准备的。如果你是一个讲求实效或以“可用即可”作为行事准则的家伙,那么你可以直接跳到“结果”那段,“经过”对于这类人不是最重要的。

 
      

max和maya都使用了“右手坐标系”,以你计算机屏幕的左下角为坐标系原点的话,那么右手坐标系中X、Y、Z轴的正方向依次为向右、向上和向外(即指向你),如下左图。而Direct3D默认使用却是“左手坐标系”,与右手坐标系的唯一区别在于Z轴正方向相反,即向内(指向显示器内部)。另外,OpenGL使用的是右手坐标系,所以这些问题在OpenGL编程中不存在,无论你用max还是maya,直接导出原始的顶点数据即可通过OpenGL绘制出来。

 
(D3D使用的左手坐标系和max、maya、OpenGL使用的右手坐标系)
 
      
好了,当你明白这些知识后,让我们来看一下在从max或maya的右手坐标系转换成D3D的左手坐标系的过程中发生了什么。
 
      
考虑在使用右手坐标系的max中有一个顶点为(0, 0,
1),你应该可以想象它是位于Z轴的正方向那段上。当这个顶点被不加修改地以左手坐标系来解释的话,那么它还是位于Z轴的正向段的Z轴,但是绝对位置却发生了变换。那么当构成一个模型的所有顶点都被这样进行转变的话,那么得到的结果就好比是一个物体关于XY轴所构成的平面作了镜像转换。如下图所示:

        
(左图:建模完成的物体;右图:原来处于右手坐标系中的物体,直接转换到左手坐标系后的情况)
 
      

由上右图你可以看到,“镜子中”物体(即转换到左手坐标系后的物体)不是“现实”物体(原右手坐标系中的物体,也就是你建模完得到的物体)的副本,我希望你能明白我在这里所谓“副本”的含义,我指的是可以与原物体完全吻合的另一个物体、另一个复制品。而现在“现实”中物体无论如何旋转或移动是永远都不可能得到“镜子中”物体的,如果你不信可以想象或比划一下,是不是呢?不是吗?是的。J

结果
想知道解决方法吗?或许聪明的你已经知道该怎么做了。我这里有三种解决方法,可能你会更倾向于其中的某一种吧。
l      
美工解决(推荐)
众所周知,有一句话曰“负负得正”。既然导出后的结果是原模型的镜像结果,那何不在导出前先对模型作一次镜像呢?两次镜像的结果就是原来的样子。

 
在用max制作完一个模型准备导出前,做如下步骤:
1)       
选中整个模型
2)       
完成镜像操作,选择菜单:
Tools->Mirror
在弹出对话框中对于“Mirror Axis”选择“Z”,然后按“OK”按钮
3)       
导出
 
这样做的有两个前提:一、应选中场景中所有的物体来作镜像;二、物体应处于世界坐标系的原点。除非你知道当违反这两个前提时会得到什么结果,并对这样结果有所预期。不然你就应该保证这两点。

 
另外,你可以想象一下如果没有这两个前提的保证下会发生什么情况。对于不满足前提一来说,某些在max作了镜像的物体能如max中所显示的那样,而某些未作镜像的却在导出后被镜像了;而对于前提二来说,如果物体不处于世界原点,则导出结果的模型本身正确了,但位置却作了关于XY平面的镜像。

 
这个方法简单有效,不是吗?而且这种解决方法不是发生在游戏运行时刻,可以节省大量的运行时开销。顺便还能让程序员们可以轻松点!想想他们在成堆的代码中已经够可怜了(当然,有的程序员视为享受,天堂和地狱只在一念之间J),饶了他们吧。

l      
程序解决
你们注定是不平凡的,但同时也注定你们是最辛苦的。因为理论上任何开发相关的事情都可以由你们来完成,只是开发/运行效率存在高低之别而已。

 
      

在当前要解决的问题中,可以通过在程序中作一次镜像来完成,概念上与上述的“负负得正”一样。大致思想是:在进行单个导出物体的世界变换之前,先作关于XY平面的镜像(XY平面可以是世界坐标系的,也可以模型局部坐标系的,两者效果是不一样的),如果选择作关于世界坐标系的XY平面的镜像,则还需要将镜像后的结果移回到原来的位置,即按Z轴平移。以下代码演示了关于世界坐标系的一个镜像,然后将对象移回到原来位置。

 
D3DXMATRIX matWorld;
……
D3DXPLANE plane( 0.0f, 0.0f, 1.0f, 0.0f ); // 关于XY平面的镜像
D3DXMATRIX matReflect;
D3DXMatrixReflect( &matReflect,
&plane ); // 构建一个关于XY平面的反射(镜像)矩阵
float fZ = matWorld._43; // 记录下物体的原Z轴位置
D3DXMATRIX matMoveBack;
D3DXMatrixTranslation( &matMoveBack, 0, 0, -2 * fZ
); // 构建移回矩阵
matWorld = matReflect * matMoveBack * matWorld;
pDevice->SetTransform( D3DTS_WORLD,
&matWorld );
 
这段代码有几处值得进一步优化,但我现在这么写纯粹是出于让你看懂的目的,在你理解之后可作进一步的优化。
 
这种方法具有更多的灵活性,但你需要为此付出运行时开销。如果你用不到该方法带来的灵活性的话,还是从美工的角度来解决这个问题吧。

l      
工具解决
就我知道存在一个转换工具――Deep
Exploration(http://www.righthemisphere.com/),可以将诸如max、maya等多种格式的模型文件转换称.X文件,并且它已经处理了这个镜像的问题。该软件使用非常简单,选择相应的源文件,然后将其另存为.X类型的文件即可,在此就不展开讨论了。

 
可能还有更多工具有待你的发掘,如果找到别忘了告诉我一声。
(三)附录
l      
建模建议
1)       
如果建模软件支持更改坐标系的话,应尽量选择使用左手坐标系来制作将会在D3D用到的模型,由此避免了转换;
2)       
建模过程应尽量使物体的头方向朝着Y轴正方向,右方向朝着X轴正方向,前方向朝着Z轴正方向,这样也可以避免程序在运行时对物体模型进行旋转调整;

3)       
建模时应尽量使用相同比例的坐标单位,这也可以避免运行时的缩放;
4)       
导出模型前检查所有法线朝向是否正确,在max据说所知可以通过加一个XForm来检查。
文章转载自:

Homepage:http://www.zhouweidi.name
email & MSN Messenger:zhouweidi@hotmail.com