首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 网络游戏 >

【Visual C++】游戏开发札记四十四 浅墨DirectX教程十二 网格模型和X文件使用面面观

2013-02-20 
【Visual C++】游戏开发笔记四十四 浅墨DirectX教程十二 网格模型和X文件使用面面观 本系列文章由zhmxy555(

【Visual C++】游戏开发笔记四十四 浅墨DirectX教程十二 网格模型和X文件使用面面观

 

本系列文章由zhmxy555(毛星云)编写,转载请注明出处。  

文章链接: http://blog.csdn.net/zhmxy555/article/details/8586540

作者:毛星云(浅墨)    邮箱: happylifemxy@163.com  



在本篇文章中,我们一起详细探索了Direct3D网格相关的知识,对当前两款主流的三维建模软件(3DS Max和Maya)进行了介绍,了解了如何从3DS Max 中导出X文件,以及如何从X文件加载三维模型到DirectX游戏程序中。文章最后,我们依旧配了一个比较好玩的demo来让大家对本篇文章所学的知识融会贯通,最后提供了这个demo详细注释的源代码下载。


首先我们复习一个之前讲过的概念。

在计算机所描绘的3D世界中,所有的物体模型(如树木,人物,山峦)都是通过多边形网格来逼近表示的,就像这幅DOTA中的英雄幻影刺客(PA)的对比图一样:

【Visual C++】游戏开发札记四十四 浅墨DirectX教程十二 网格模型和X文件使用面面观【Visual C++】游戏开发札记四十四 浅墨DirectX教程十二 网格模型和X文件使用面面观

幻影刺客镇文~嗯,我们开讲~

 


一、网格模型技术的前生今世

网格模型是一种将物体模型的顶点数据、纹理、材质等信息存储在一个外部文件中的3D物体模型。对于那些简单的图元描述的图形,比如点,线,三角形等等,我们可以通过写代码指定顶点数据,索引数据,法线向量,纹理和材质等等信息。但对于复杂的3D物体的话,采用这种方式显然是不现实的。因此,Direct3D提供了一种称作网格模型的技术,可以从各种特定的文件格式中读取和绘制3D图形,极大地方便了游戏的开发。

使用网格模型最普遍的方式是从外部的3D模型文件中加载一个网格。而这些3D模型通常都是由3D建模软件生成的,比较复杂的网格数据。目前市面上主流的3D建模软件有3DS Max和Maya。而目前流行的3D模型文件格式有.3ds、.max、.obj以及.mb。其中.3ds、.max为3DS Max常用的格式,.mb为Maya常用的格式,而.obj为3DSMax和Maya通用的文件格式。

 

 


二、认识三维建模软件

再不厌其烦地说一遍,我们在通常的三维游戏开发中,常常要涉及到非常复杂的三维物体数据模型,如果用我们之前讲的知识,顶点缓存索引缓存,通过写代码来构造这些三维模型,显然是不合实际的。

这些复杂物体的模型通常需要利用专业的三维建模软件来制作。目前市面上主流的3D建模软件有3DS Max和Maya。这两款在三维建模行业作为竞争对手的软件,目前同为Autodesk公司所有,这倒是有些令人匪夷所思。下面我们先来分别介绍一下当前市场上主流的三维建模软件(当然,不仅仅是用于三维建模这么简单)3DS Max和Maya。

Ⅰ、3DS Max软件简介【Visual C++】游戏开发札记四十四 浅墨DirectX教程十二 网格模型和X文件使用面面观

3D Studio Max,常简称为3dsMax或MAX,是Autodesk公司开发的基于PC系统的三维动画渲染和制作软件。其前身是基于DOS操作系统的3D Studio系列软件,最新版本是2012。在Windows NT出现以前,工业级的CG制作被SGI图形工作站所垄断。3D Studio Max + Windows NT组合的出现一下子降低了CG制作的门槛,首选开始运用在电脑游戏中的动画制作,后更进一步开始参与影视片的特效制作,例如X战警II,最后的武士等。

3ds Max软件截图:

【Visual C++】游戏开发札记四十四 浅墨DirectX教程十二 网格模型和X文件使用面面观


Ⅱ、Maya软件简介【Visual C++】游戏开发札记四十四 浅墨DirectX教程十二 网格模型和X文件使用面面观

Maya是美国Autodesk公司出品的世界顶级的三维动画软件,应用对象是专业的影视广告,角色动画,电影特技等。Maya功能完善,工作灵活,易学易用,制作效率极高,渲染真实感极强,是电影级别的高端制作软件。

Maya软件截图:

【Visual C++】游戏开发札记四十四 浅墨DirectX教程十二 网格模型和X文件使用面面观

 

 

Ⅲ、Maya和3ds Max的区别与联系

MAYA和3d Max都是高端3D软件,两者之间都有很多相同的功能,像创建模型,渲染材质,动画制作等等.但就运用实际情况而言,Max更加适合于游戏,建筑学,室内设计等等,而MAYA可以说是专门为影视特效而生的一款软件,这也是当初Alias设计MAYA的意图.MAYA在如角色动画/运动学模拟/以及完美的材质系统,使得MAYA所创造出来的3维效果如此逼真,可以说全世界的电影特效工作室都必须用到MAYA

MAYA 的用户界面也比3dsmax要人性化点,Maya 是 Alias|Wavefront ( 2003 年 7月 更名为 Alias )公司的产品,作为三维动画软件的后起之秀,深受业界欢迎和钟爱。

MAYA软件应用主要是动画片制作、电影制作、电视栏目包装、电视广告、游戏动画制作等。3dsmax软件应用主要是动画片制作、游戏动画制作、建筑效果图、建筑动画等。 MAYA的基础层次更高,3dsmax属于普及型三维软件,有条件当然学MAYA。

Maya的CG功能十分全面,建模、粒子系统、毛发生成、植物创建、衣料仿真等等。可以说,当3dsmax用户匆忙地寻找第三方插件时,Maya用户已经可以早早地安心工作了。可以说,从建模到动画,到速度,Maya都非常出色。Maya主要是为了影视应用而研发的。

3d MAX和Maya都是功能强大的三维制作软件,各有很大优点,但是,不同的行业用不同的软件会让制作比较轻松,所以说,3d MAX和Maya究竟哪个好,哪个更好用与所从事的行业有着一定联系。

3d MAX在建筑动画效果上要强于Maya,而Maya在影视动画上又略胜于3d max,两款软件没有太大的本质性区别,只是看个人掌握哪个软件比较顺手罢了。

目前,业内已开始对软件使用做了一些细分。3d max主要用来做建筑,国内多数公司喜欢拿他做游戏,附带着可以做影视动画,建模比较方便。Maya主要用来做影视动画,在动画和动力学模块上很强大,国内不少游戏公司慢慢将max转Maya了。其他的栏目包装,广告等两者都可用到。最终没有谁好谁坏之分,只有用处不同,两软件都是相通的。以前国内学Maya的不多,因为没有中文版,教程也少,现在不一样了,教程也是不少了。

3d MAX和Maya各有其优势,用户可以根据自身的项目需求自主选择软件,其最大的区别就是3d MAX可以不费力气得到你想要的东西,Maya可能要花很多精力但是可以做出随心所欲的东西,灵活性要强。

Maya和3ds Max都可以做出如下效果(虽然这些模型本身是3ds Max做出来的):

擎天柱

【Visual C++】游戏开发札记四十四 浅墨DirectX教程十二 网格模型和X文件使用面面观


大黄蜂

【Visual C++】游戏开发札记四十四 浅墨DirectX教程十二 网格模型和X文件使用面面观

好了,认识到3D制作软件的神奇了,下面我们对本文的主角——X文件来一个介绍。



三、对X文件的认识

 

在Direct3D中,我们一般都采用.X文件来存储网格数据。X文件格式是微软定义的3D模式文件格式,其中包括网格的纹理,动画以及用户定义对象的一些数据等。需要注意的是,.X文件通常并未存储具体的纹理数据,它只包含纹理贴图的文件名,所以我们常常会发现,X文件会和一些图片文件比如.BMP、.DDS一同出没,放在一起,比如本次demo中我们使用的初音模型,除了.X文件本身miki.X之外,还有数十张BMP位图纹理图片配合着miki.X。

 【Visual C++】游戏开发札记四十四 浅墨DirectX教程十二 网格模型和X文件使用面面观

 

四、从3DS Max中导出.X文件方法详解

由于我们是初次接触三维模型文件的载入,当然得先学走,也就是先学Direct3D自带的.X模型文件的载入和使用,.X文件作为微软定义的3D模式文件格式,Direct3D的默认三维模型格式,Direct3D自然对其有着非常好的支持度,有大量的函数辅助着.X文件的使用。

至于学到后头了,我们就可以通过一些代码的编写(或者说使用游戏引擎的时候),直接在Direct3D中使用3ds、.max、.obj以及.mb等主流三维建模软件本身的文件。不过现在我们作为初学者,当前掌握好.X模型文件使用的各方各面,就能应付绝大多数场合了。

 

想要从3DS Max中导出.X文件,简单来说,有三个要素,我们分别来详细说一说。

 

第一个要素,当然是3DS Max软件的下载和安装。浅墨目前安装的是3DS Max 2012的32位版。大家可以百度一下,下载并安装,至于软件的破解,网上教程很多,这里就不具体讲了。

第二个要素,Panda插件的下载和配置。这个Panda插件熟话说就是能让3DS Max支持导出.X格式文件的一个插件。通常我们在3DS Max中打开一个3D模型后,点左上角的【主菜单【Visual C++】游戏开发札记四十四 浅墨DirectX教程十二 网格模型和X文件使用面面观】->【导出】之后,在弹出的导出选项框中并没有我们想要的.X文件。所以我们需要下载并配置一下与我们3DS Max 相对应的Panda插件,来让导出选项中有我们的.X文件,这样才能完成3DS中的模型到.X文件的导出。以便我们在我们的DirectX游戏程序中通过.X文件来载入好看的三维模型。

3DS Max 2012版本的Panda插件浅墨已经为大家准备好了,链接如下:

点击转到下载页面

下载之后,打开压缩包,把里面的对应安装版本位数的dle文件(32位对应X86字样,64位对应X64字样)放到3ds Max安装目录下的plugins之中,然后重启3ds Max即可。

例如浅墨自己的3ds Max2012安装在D盘的D:\Program Files\Autodesk\目录之下,那么对应的就在D:\Program Files\Autodesk\3ds Max 2012\plugins目录中添加dle插件文件,如图:

【Visual C++】游戏开发札记四十四 浅墨DirectX教程十二 网格模型和X文件使用面面观

 

然后,重启一下3DS max 2012,我们就可以发现,导出选项中有了X文件导出项了。

【Visual C++】游戏开发札记四十四 浅墨DirectX教程十二 网格模型和X文件使用面面观

 

第三个要素,也就是导出X文件的具体步骤了。

首先,正所谓巧妇难为无米之炊,我们需要有一个导出的对象,也就是一个三维的模型。模型嘛,可以自己用3ds Max现场做(想做出个像模像样的模型一时半会儿显然不现实,而且这一般都是美工童鞋的工作,我们可是写程序的筒子们),也可以自己去网上下载,这里推荐一个论坛,有大量的3D模型资源:http://www.cgmodel.cn/。比如我们在这个论坛下载了Dota英雄幻影刺客的.max模型。(记住目前主流的格式分.max和.mb两种,我们3ds Max用的是.max,而Maya用的是.mb,而另外一种.obj是万金油格式,3ds Max和Maya两者都可用,所以想在3ds Max与Maya之间通用的话,就用.obj)

好了,继续讲,我们下载好了Dota英雄幻影刺客的.max模型,解压缩包,点击HeroWarden.max模型文件,于是就打开了这个模型,如图。

【Visual C++】游戏开发札记四十四 浅墨DirectX教程十二 网格模型和X文件使用面面观

 

然后,就像之前提到的,点击左上角的【 主菜单【Visual C++】游戏开发札记四十四 浅墨DirectX教程十二 网格模型和X文件使用面面观】->【导出】。如图:

 【Visual C++】游戏开发札记四十四 浅墨DirectX教程十二 网格模型和X文件使用面面观

这时就会弹出一个【选择要导出的文件】对话框。在弹出的这个对话框的【保存类型】中选择【Panda DirectX(*.X)】,然后自己取一个文件名(这里我们取名Warden,魔兽3中幻影刺客模型所对应的人物名称——守望者),设定好保存的路径,点【保存】。如图。

【Visual C++】游戏开发札记四十四 浅墨DirectX教程十二 网格模型和X文件使用面面观

然后会弹出一个名为【PandaSoft DirectX Exporter】的窗口,在里面我们可以进行一些导出模型参数的设定和修改。如下:

【Visual C++】游戏开发札记四十四 浅墨DirectX教程十二 网格模型和X文件使用面面观

如果没有什么需要特殊设置的,点【确定】就行了。

 然后经过3ds Max的处理,我们就发现在我们设定的保存路径下多了.X文件以及配套的纹理了。

 【Visual C++】游戏开发札记四十四 浅墨DirectX教程十二 网格模型和X文件使用面面观

 

这样,如果我们想要在我们写的游戏程序中使用这个幻影刺客(守望者)的三维模型,把这两个文件一起放到我们工程中相应的地方就可以了。

 

之前讲了那么多无非是在为.X文件的诞生造势,下面就开始隆重介绍如何在Direct3D程序中载入X文件的具体知识吧~

 

而想利用.X文件来在游戏程序中载入三维模型的话,首先就需要将.X文件中的各种数据分别加载到内存中,而这些数据主要包括顶点数据、材质数据和纹理数据等等。首先,我们需要介绍一下与网格模型相关的一个重要的接口——ID3DXMESH。

 


五、网格模型接口ID3DXMESH

在Direct3D中,微软为我们提供了ID3DXMesh接口表示网格,这个接口继承自ID3DXBaseMesh接口。网格模型接口ID3DXMesh实际上是三维物体的顶点缓存的集合,他将为我们创建顶点缓存、定义灵活顶点格式和绘制顶点缓冲区等功能封装在一个COM对象中,这样复杂三维物体的绘制就显得非常简便了。

其中,ID3DXMESH接口中的D3DXCreateMesh()可用于创建一个Direct3D网格模型对象,我们可以在MSDN中查到该函数声明是这样的:

 


好了,下面我们就依次贴出四个文件的代码,大家重点看放在最后的main.cpp就可以了:


1.D3DUtil.h
//*****************************************************************************************// Desc: D3DUtil.h头文件,用于公共辅助宏的定义// 2013年 2月03日  Create by 浅墨 //*****************************************************************************************#pragma once#ifndef HR#define HR(x)    { hr = x; if( FAILED(hr) ) { return hr; } }         //自定义一个HR宏,方便执行错误的返回#endif#ifndef SAFE_DELETE#define SAFE_DELETE(p)       { if(p) { delete (p);     (p)=NULL; } }       //自定义一个SAFE_RELEASE()宏,便于指针资源的释放#endif    #ifndef SAFE_RELEASE#define SAFE_RELEASE(p)      { if(p) { (p)->Release(); (p)=NULL; } }     //自定义一个SAFE_RELEASE()宏,便于COM资源的释放#endif



然后是封装了DirectInput的类DInputClass的头文件:

2.DirectInputClass.h
//*****************************************************************************************////【Visual C++】游戏开发笔记系列配套源码四十四 浅墨DirectX教程十二 网格模型和X文件使用面面观// VS2010版// 2013年 2月17日  Create by 浅墨 //图标及图片素材: 《仙剑奇侠传五前传》 龙溟////***************************************************************************************** //*****************************************************************************************// Desc: 宏定义部分   //*****************************************************************************************#define SCREEN_WIDTH800//为窗口宽度定义的宏,以方便在此处修改窗口宽度#define SCREEN_HEIGHT600//为窗口高度定义的宏,以方便在此处修改窗口高度#define WINDOW_TITLE_T("【Visual C++】游戏开发笔记系列配套源码四十四 浅墨DirectX教程十二 网格模型和X文件使用面面观") //为窗口标题定义的宏//*****************************************************************************************// Desc: 头文件定义部分  //*****************************************************************************************                                                                                       #include <d3d9.h>#include <d3dx9.h>#include <tchar.h>#include <time.h> #include "DirectInputClass.h"//*****************************************************************************************// Desc: 库文件定义部分  //***************************************************************************************** #pragma comment(lib,"d3d9.lib")#pragma comment(lib,"d3dx9.lib")#pragma comment(lib, "dinput8.lib")     // 使用DirectInput必须包含的库文件,注意这里有8#pragma comment(lib,"dxguid.lib")#pragma comment(lib, "winmm.lib") //*****************************************************************************************// Desc: 全局变量声明部分  //*****************************************************************************************LPDIRECT3DDEVICE9g_pd3dDevice = NULL; //Direct3D设备对象LPD3DXFONTg_pTextFPS=NULL;    //字体COM接口LPD3DXFONTg_pTextAdaperName           = NULL;  // 显卡信息的2D文本LPD3DXFONTg_pTextHelper          = NULL;  // 帮助信息的2D文本LPD3DXFONTg_pTextInfor           = NULL;  // 绘制信息的2D文本floatg_FPS= 0.0f;       //一个浮点型的变量,代表帧速率wchar_tg_strFPS[50]={0};    //包含帧速率的字符数组wchar_tg_strAdapterName[60]={0};    //包含显卡名称的字符数组D3DXMATRIXg_matWorld;   //世界矩阵DInputClass*g_pDInput = NULL;         //一个DInputClass类的指针LPD3DXMESH          g_pMesh     = NULL; // 网格对象D3DMATERIAL9*       g_pMaterials    = NULL; // 网格的材质信息LPDIRECT3DTEXTURE9* g_pTextures     = NULL; // 网格的纹理信息DWORD               g_dwNumMtrls    = 0;    // 材质的数目//*****************************************************************************************// Desc: 全局函数声明部分 //***************************************************************************************** LRESULT CALLBACKWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam );HRESULTDirect3D_Init(HWND hwnd,HINSTANCE hInstance);HRESULTObjects_Init();voidDirect3D_Render( HWND hwnd);voidDirect3D_Update( HWND hwnd);voidDirect3D_CleanUp( );floatGet_FPS();voidMatrix_Set();//*****************************************************************************************// Name: WinMain( )// Desc: Windows应用程序入口函数//*****************************************************************************************int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,LPSTR lpCmdLine, int nShowCmd){//开始设计一个完整的窗口类WNDCLASSEX wndClass = { 0 };//用WINDCLASSEX定义了一个窗口类,即用wndClass实例化了WINDCLASSEX,用于之后窗口的各项初始化    wndClass.cbSize = sizeof( WNDCLASSEX ) ;//设置结构体的字节数大小wndClass.style = CS_HREDRAW | CS_VREDRAW;//设置窗口的样式wndClass.lpfnWndProc = WndProc;//设置指向窗口过程函数的指针wndClass.cbClsExtra= 0;wndClass.cbWndExtra= 0;wndClass.hInstance = hInstance;//指定包含窗口过程的程序的实例句柄。wndClass.hIcon=(HICON)::LoadImage(NULL,_T("icon.ico"),IMAGE_ICON,0,0,LR_DEFAULTSIZE|LR_LOADFROMFILE); //从全局的::LoadImage函数从本地加载自定义ico图标wndClass.hCursor = LoadCursor( NULL, IDC_ARROW );    //指定窗口类的光标句柄。wndClass.hbrBackground=(HBRUSH)GetStockObject(GRAY_BRUSH);  //为hbrBackground成员指定一个灰色画刷句柄wndClass.lpszMenuName = NULL;//用一个以空终止的字符串,指定菜单资源的名字。wndClass.lpszClassName = _T("ForTheDreamOfGameDevelop");//用一个以空终止的字符串,指定窗口类的名字。if( !RegisterClassEx( &wndClass ) )//设计完窗口后,需要对窗口类进行注册,这样才能创建该类型的窗口return -1;HWND hwnd = CreateWindow( _T("ForTheDreamOfGameDevelop"),WINDOW_TITLE,//喜闻乐见的创建窗口函数CreateWindowWS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, SCREEN_WIDTH,SCREEN_HEIGHT, NULL, NULL, hInstance, NULL );//Direct3D资源的初始化,调用失败用messagebox予以显示if (!(S_OK==Direct3D_Init (hwnd,hInstance))){MessageBox(hwnd, _T("Direct3D初始化失败~!"), _T("浅墨的消息窗口"), 0); //使用MessageBox函数,创建一个消息窗口 }MoveWindow(hwnd,200,50,SCREEN_WIDTH,SCREEN_HEIGHT,true);   //调整窗口显示时的位置,窗口左上角位于屏幕坐标(200,50)处ShowWindow( hwnd, nShowCmd );    //调用Win32函数ShowWindow来显示窗口UpdateWindow(hwnd);  //对窗口进行更新,就像我们买了新房子要装修一样//进行DirectInput类的初始化g_pDInput = new DInputClass();g_pDInput->Init(hwnd,hInstance,DISCL_FOREGROUND | DISCL_NONEXCLUSIVE,DISCL_FOREGROUND | DISCL_NONEXCLUSIVE);//消息循环过程MSG msg = { 0 };  //初始化msgwhile( msg.message != WM_QUIT )//使用while循环{if( PeekMessage( &msg, 0, 0, 0, PM_REMOVE ) )   //查看应用程序消息队列,有消息时将队列中的消息派发出去。{TranslateMessage( &msg );//将虚拟键消息转换为字符消息DispatchMessage( &msg );//该函数分发一个消息给窗口程序。}else{Direct3D_Update(hwnd);         //调用更新函数,进行画面的更新Direct3D_Render(hwnd);//调用渲染函数,进行画面的渲染}}UnregisterClass(_T("ForTheDreamOfGameDevelop"), wndClass.hInstance);return 0;  }//*****************************************************************************************// Name: WndProc()// Desc: 对窗口消息进行处理//*****************************************************************************************LRESULT CALLBACK WndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )   //窗口过程函数WndProc{switch( message )//switch语句开始{case WM_PAINT: // 客户区重绘消息Direct3D_Render(hwnd);          //调用Direct3D_Render函数,进行画面的绘制ValidateRect(hwnd, NULL);   // 更新客户区的显示break;//跳出该switch语句case WM_KEYDOWN:                // 键盘按下消息if (wParam == VK_ESCAPE)    // ESC键DestroyWindow(hwnd);    // 销毁窗口, 并发送一条WM_DESTROY消息break;case WM_DESTROY://窗口销毁消息Direct3D_CleanUp();     //调用Direct3D_CleanUp函数,清理COM接口对象PostQuitMessage( 0 );//向系统表明有个线程有终止请求。用来响应WM_DESTROY消息break;//跳出该switch语句default://若上述case条件都不符合,则执行该default语句return DefWindowProc( hwnd, message, wParam, lParam );//调用缺省的窗口过程来为应用程序没有处理的窗口消息提供缺省的处理。}return 0;//正常退出}//*****************************************************************************************// Name: Direct3D_Init( )// Desc: 初始化Direct3D// Point:【Direct3D初始化四步曲】//1.初始化四步曲之一,创建Direct3D接口对象//2.初始化四步曲之二,获取硬件设备信息//3.初始化四步曲之三,填充结构体//4.初始化四步曲之四,创建Direct3D设备接口//*****************************************************************************************HRESULT Direct3D_Init(HWND hwnd,HINSTANCE hInstance){//--------------------------------------------------// 【Direct3D初始化四步曲之一,创接口】:创建Direct3D接口对象, 以便用该Direct3D对象创建Direct3D设备对象//--------------------------------------------------LPDIRECT3D9  pD3D = NULL; //Direct3D接口对象的创建if( NULL == ( pD3D = Direct3DCreate9( D3D_SDK_VERSION ) ) ) //初始化Direct3D接口对象,并进行DirectX版本协商 return E_FAIL;//--------------------------------------------------// 【Direct3D初始化四步曲之二,取信息】:获取硬件设备信息//--------------------------------------------------D3DCAPS9 caps; int vp = 0;if( FAILED( pD3D->GetDeviceCaps( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, &caps ) ) ){return E_FAIL;}if( caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT )vp = D3DCREATE_HARDWARE_VERTEXPROCESSING;   //支持硬件顶点运算,我们就采用硬件顶点运算,妥妥的elsevp = D3DCREATE_SOFTWARE_VERTEXPROCESSING; //不支持硬件顶点运算,无奈只好采用软件顶点运算//--------------------------------------------------// 【Direct3D初始化四步曲之三,填内容】:填充D3DPRESENT_PARAMETERS结构体//--------------------------------------------------D3DPRESENT_PARAMETERS d3dpp; ZeroMemory(&d3dpp, sizeof(d3dpp));d3dpp.BackBufferWidth            = SCREEN_WIDTH;d3dpp.BackBufferHeight           = SCREEN_HEIGHT;d3dpp.BackBufferFormat           = D3DFMT_A8R8G8B8;d3dpp.BackBufferCount            = 2;d3dpp.MultiSampleType            = D3DMULTISAMPLE_NONE;d3dpp.MultiSampleQuality         = 0;d3dpp.SwapEffect                 = D3DSWAPEFFECT_DISCARD; d3dpp.hDeviceWindow              = hwnd;d3dpp.Windowed                   = true;d3dpp.EnableAutoDepthStencil     = true; d3dpp.AutoDepthStencilFormat     = D3DFMT_D24S8;d3dpp.Flags                      = 0;d3dpp.FullScreen_RefreshRateInHz = 0;d3dpp.PresentationInterval       = D3DPRESENT_INTERVAL_IMMEDIATE;//--------------------------------------------------// 【Direct3D初始化四步曲之四,创设备】:创建Direct3D设备接口//--------------------------------------------------if(FAILED(pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hwnd, vp, &d3dpp, &g_pd3dDevice)))return E_FAIL;//获取显卡信息到g_strAdapterName中,并在显卡名称之前加上“当前显卡型号:”字符串 wchar_t TempName[60]=L"当前显卡型号:";   //定义一个临时字符串,且方便了把"当前显卡型号:"字符串引入我们的目的字符串中 D3DADAPTER_IDENTIFIER9 Adapter;  //定义一个D3DADAPTER_IDENTIFIER9结构体,用于存储显卡信息 pD3D->GetAdapterIdentifier(0,0,&Adapter);//调用GetAdapterIdentifier,获取显卡信息 int len = MultiByteToWideChar(CP_ACP,0, Adapter.Description, -1, NULL, 0);//显卡名称现在已经在Adapter.Description中了,但是其为char类型,我们要将其转为wchar_t类型 MultiByteToWideChar(CP_ACP, 0, Adapter.Description, -1, g_strAdapterName, len);//这步操作完成后,g_strAdapterName中就为当前我们的显卡类型名的wchar_t型字符串了 wcscat_s(TempName,g_strAdapterName);//把当前我们的显卡名加到“当前显卡型号:”字符串后面,结果存在TempName中 wcscpy_s(g_strAdapterName,TempName);//把TempName中的结果拷贝到全局变量g_strAdapterName中,大功告成~if(!(S_OK==Objects_Init())) return E_FAIL;SAFE_RELEASE(pD3D) //LPDIRECT3D9接口对象的使命完成,我们将其释放掉return S_OK;}HRESULT Objects_Init(){//创建字体D3DXCreateFont(g_pd3dDevice, 36, 0, 0, 1000, false, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, DEFAULT_QUALITY, 0, _T("Calibri"), &g_pTextFPS);D3DXCreateFont(g_pd3dDevice, 20, 0, 1000, 0, false, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, DEFAULT_QUALITY, 0, L"华文中宋", &g_pTextAdaperName); D3DXCreateFont(g_pd3dDevice, 23, 0, 1000, 0, false, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, DEFAULT_QUALITY, 0, L"微软雅黑", &g_pTextHelper); D3DXCreateFont(g_pd3dDevice, 26, 0, 1000, 0, false, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, DEFAULT_QUALITY, 0, L"黑体", &g_pTextInfor); // 从X文件中加载网格数据LPD3DXBUFFER pAdjBuffer  = NULL;LPD3DXBUFFER pMtrlBuffer = NULL;D3DXLoadMeshFromX(L"miki.X", D3DXMESH_MANAGED, g_pd3dDevice, &pAdjBuffer, &pMtrlBuffer, NULL, &g_dwNumMtrls, &g_pMesh);// 读取材质和纹理数据D3DXMATERIAL *pMtrls = (D3DXMATERIAL*)pMtrlBuffer->GetBufferPointer(); //创建一个D3DXMATERIAL结构体用于读取材质和纹理信息g_pMaterials = new D3DMATERIAL9[g_dwNumMtrls];g_pTextures  = new LPDIRECT3DTEXTURE9[g_dwNumMtrls];for (DWORD i=0; i<g_dwNumMtrls; i++) {//获取材质,并设置一下环境光的颜色值g_pMaterials[i] = pMtrls[i].MatD3D;g_pMaterials[i].Ambient = g_pMaterials[i].Diffuse;//创建一下纹理对象g_pTextures[i]  = NULL;D3DXCreateTextureFromFileA(g_pd3dDevice, pMtrls[i].pTextureFilename, &g_pTextures[i]);}SAFE_RELEASE(pAdjBuffer)SAFE_RELEASE(pMtrlBuffer)// 设置光照D3DLIGHT9 light;::ZeroMemory(&light, sizeof(light));light.Type          = D3DLIGHT_DIRECTIONAL;light.Ambient       = D3DXCOLOR(0.5f, 0.5f, 0.5f, 1.0f);light.Diffuse       = D3DXCOLOR(1.0f, 1.0f, 1.0f, 1.0f);light.Specular      = D3DXCOLOR(0.0f, 0.0f, 0.0f, 1.0f);light.Direction     = D3DXVECTOR3(1.0f, 0.0f, 1.0f);g_pd3dDevice->SetLight(0, &light);g_pd3dDevice->LightEnable(0, true);g_pd3dDevice->SetRenderState(D3DRS_NORMALIZENORMALS, true);g_pd3dDevice->SetRenderState(D3DRS_SPECULARENABLE, true);// 设置渲染状态g_pd3dDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW);   //开启背面消隐return S_OK;}//*****************************************************************************************// Name:Matrix_Set()// Desc: 设置世界矩阵// Point:【Direct3D四大变换】//1.【四大变换之一】:世界变换矩阵的设置//2.【四大变换之二】:取景变换矩阵的设置//3.【四大变换之三】:投影变换矩阵的设置//4.【四大变换之四】:视口变换的设置//*****************************************************************************************void Matrix_Set(){//--------------------------------------------------//【四大变换之一】:世界变换矩阵的设置//--------------------------------------------------//--------------------------------------------------//【四大变换之二】:取景变换矩阵的设置//--------------------------------------------------D3DXMATRIX matView; //定义一个矩阵D3DXVECTOR3 vEye(0.0f, 0.0f, -250.0f);  //摄像机的位置D3DXVECTOR3 vAt(0.0f, 0.0f, 0.0f); //观察点的位置D3DXVECTOR3 vUp(0.0f, 1.0f, 0.0f);//向上的向量D3DXMatrixLookAtLH(&matView, &vEye, &vAt, &vUp); //计算出取景变换矩阵g_pd3dDevice->SetTransform(D3DTS_VIEW, &matView); //应用取景变换矩阵//--------------------------------------------------//【四大变换之三】:投影变换矩阵的设置//--------------------------------------------------D3DXMATRIX matProj; //定义一个矩阵D3DXMatrixPerspectiveFovLH(&matProj, D3DX_PI / 4.0f,(float)((double)SCREEN_WIDTH/SCREEN_HEIGHT),1.0f, 1000.0f); //计算投影变换矩阵g_pd3dDevice->SetTransform(D3DTS_PROJECTION, &matProj);  //设置投影变换矩阵//--------------------------------------------------//【四大变换之四】:视口变换的设置//--------------------------------------------------D3DVIEWPORT9 vp; //实例化一个D3DVIEWPORT9结构体,然后做填空题给各个参数赋值就可以了vp.X      = 0;//表示视口相对于窗口的X坐标vp.Y      = 0;//视口相对对窗口的Y坐标vp.Width  = SCREEN_WIDTH;//视口的宽度vp.Height = SCREEN_HEIGHT; //视口的高度vp.MinZ   = 0.0f; //视口在深度缓存中的最小深度值vp.MaxZ   = 1.0f;//视口在深度缓存中的最大深度值g_pd3dDevice->SetViewport(&vp); //视口的设置}voidDirect3D_Update( HWND hwnd){//使用DirectInput类读取数据g_pDInput->GetInput();// 按住鼠标左键并拖动,为平移操作static FLOAT fPosX = 0.0f, fPosY = 0.0f, fPosZ = 0.0f;if (g_pDInput->IsMouseButtonDown(0)) {fPosX += (g_pDInput->MouseDX())*  0.08f;fPosY += (g_pDInput->MouseDY()) * -0.08f;}//鼠标滚轮,为观察点收缩操作fPosZ += (g_pDInput->MouseDZ())* 0.02f;// 平移物体if (g_pDInput->IsKeyDown(DIK_A)) fPosX -= 0.005f;if (g_pDInput->IsKeyDown(DIK_D)) fPosX += 0.005f;if (g_pDInput->IsKeyDown(DIK_W)) fPosY += 0.005f;if (g_pDInput->IsKeyDown(DIK_S)) fPosY -= 0.005f;D3DXMatrixTranslation(&g_matWorld, fPosX, fPosY, fPosZ);// 按住鼠标右键并拖动,为旋转操作static float fAngleX = 0, fAngleY =0;if (g_pDInput->IsMouseButtonDown(1)) {fAngleX += (g_pDInput->MouseDY())* -0.01f;fAngleY += (g_pDInput->MouseDX()) * -0.01f;}// 旋转物体 if (g_pDInput->IsKeyDown(DIK_UP)) fAngleX += 0.005f;if (g_pDInput->IsKeyDown(DIK_DOWN)) fAngleX -= 0.005f; if (g_pDInput->IsKeyDown(DIK_LEFT)) fAngleY -= 0.005f; if (g_pDInput->IsKeyDown(DIK_RIGHT)) fAngleY += 0.005f;D3DXMATRIX Rx, Ry;D3DXMatrixRotationX(&Rx, fAngleX);D3DXMatrixRotationY(&Ry, fAngleY);g_matWorld = Rx * Ry * g_matWorld;g_pd3dDevice->SetTransform(D3DTS_WORLD, &g_matWorld);Matrix_Set();}//*****************************************************************************************// Name: Direct3D_Render()// Desc: 进行图形的渲染操作// Point:【Direct3D渲染五步曲】//1.渲染五步曲之一,清屏操作//2.渲染五步曲之二,开始绘制//3.渲染五步曲之三,正式绘制//4.渲染五步曲之四,结束绘制//5.渲染五步曲之五,翻转显示//*****************************************************************************************void Direct3D_Render(HWND hwnd){//--------------------------------------------------// 【Direct3D渲染五步曲之一】:清屏操作//--------------------------------------------------g_pd3dDevice->Clear(0, NULL, D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(100, 150, 100), 1.0f, 0);//定义一个矩形,用于获取主窗口矩形RECT formatRect;GetClientRect(hwnd, &formatRect);//--------------------------------------------------// 【Direct3D渲染五步曲之二】:开始绘制//--------------------------------------------------g_pd3dDevice->BeginScene();                     // 开始绘制//--------------------------------------------------// 【Direct3D渲染五步曲之三】:正式绘制//--------------------------------------------------// 用一个for循环,进行网格各个部分的绘制for (DWORD i = 0; i < g_dwNumMtrls; i++){g_pd3dDevice->SetMaterial(&g_pMaterials[i]);g_pd3dDevice->SetTexture(0, g_pTextures[i]);g_pMesh->DrawSubset(i);}//在窗口右上角处,显示每秒帧数formatRect.top = 5;int charCount = swprintf_s(g_strFPS, 20, _T("FPS:%0.3f"), Get_FPS() );g_pTextFPS->DrawText(NULL, g_strFPS, charCount , &formatRect, DT_TOP | DT_RIGHT, D3DCOLOR_RGBA(0,239,136,255));//显示显卡类型名g_pTextAdaperName->DrawText(NULL,g_strAdapterName, -1, &formatRect, DT_TOP | DT_LEFT, D3DXCOLOR(1.0f, 0.5f, 0.0f, 1.0f));// 输出绘制信息 formatRect.top = 30;static wchar_t strInfo[256] = {0};swprintf_s(strInfo,-1, L"模型坐标: (%.2f, %.2f, %.2f)", g_matWorld._41, g_matWorld._42, g_matWorld._43);g_pTextHelper->DrawText(NULL, strInfo, -1, &formatRect, DT_SINGLELINE | DT_NOCLIP | DT_LEFT, D3DCOLOR_RGBA(135,239,136,255));// 输出帮助信息formatRect.left = 0,formatRect.top = 380;g_pTextInfor->DrawText(NULL, L"控制说明:", -1, &formatRect, DT_SINGLELINE | DT_NOCLIP | DT_LEFT, D3DCOLOR_RGBA(235,123,230,255));formatRect.top += 35;g_pTextHelper->DrawText(NULL, L"    按住鼠标左键并拖动:平移模型", -1, &formatRect, DT_SINGLELINE | DT_NOCLIP | DT_LEFT, D3DCOLOR_RGBA(255,200,0,255));formatRect.top += 25;g_pTextHelper->DrawText(NULL, L"    按住鼠标右键并拖动:旋转模型", -1, &formatRect, DT_SINGLELINE | DT_NOCLIP | DT_LEFT, D3DCOLOR_RGBA(255,200,0,255));formatRect.top += 25;g_pTextHelper->DrawText(NULL, L"    滑动鼠标滚轮:拉伸模型", -1, &formatRect, DT_SINGLELINE | DT_NOCLIP | DT_LEFT, D3DCOLOR_RGBA(255,200,0,255));formatRect.top += 25;g_pTextHelper->DrawText(NULL, L"    W、S、A、D键:平移模型 ", -1, &formatRect, DT_SINGLELINE | DT_NOCLIP | DT_LEFT, D3DCOLOR_RGBA(255,200,0,255));formatRect.top += 25;g_pTextHelper->DrawText(NULL, L"    上、下、左、右方向键:旋转模型 ", -1, &formatRect, DT_SINGLELINE | DT_NOCLIP | DT_LEFT, D3DCOLOR_RGBA(255,200,0,255));formatRect.top += 25;g_pTextHelper->DrawText(NULL, L"    ESC键 : 退出程序", -1, &formatRect, DT_SINGLELINE | DT_NOCLIP | DT_LEFT, D3DCOLOR_RGBA(255,200,0,255));//--------------------------------------------------// 【Direct3D渲染五步曲之四】:结束绘制//--------------------------------------------------g_pd3dDevice->EndScene();                       // 结束绘制//--------------------------------------------------// 【Direct3D渲染五步曲之五】:显示翻转//--------------------------------------------------g_pd3dDevice->Present(NULL, NULL, NULL, NULL);  // 翻转与显示 }//*****************************************************************************************// Name:Get_FPS()函数// Desc: 用于计算帧速率//*****************************************************************************************float Get_FPS(){//定义四个静态变量static float  fps = 0; //我们需要计算的FPS值static int    frameCount = 0;//帧数static float  currentTime =0.0f;//当前时间static float  lastTime = 0.0f;//持续时间frameCount++;//每调用一次Get_FPS()函数,帧数自增1currentTime = timeGetTime()*0.001f;//获取系统时间,其中timeGetTime函数返回的是以毫秒为单位的系统时间,所以需要乘以0.001,得到单位为秒的时间//如果当前时间减去持续时间大于了1秒钟,就进行一次FPS的计算和持续时间的更新,并将帧数值清零if(currentTime - lastTime > 1.0f) //将时间控制在1秒钟{fps = (float)frameCount /(currentTime - lastTime);//计算这1秒钟的FPS值lastTime = currentTime; //将当前时间currentTime赋给持续时间lastTime,作为下一秒的基准时间frameCount    = 0;//将本次帧数frameCount值清零}return fps;}//*****************************************************************************************// Name: Direct3D_CleanUp()// Desc: 对Direct3D的资源进行清理,释放COM接口对象//*****************************************************************************************void Direct3D_CleanUp(){//释放COM接口对象for (DWORD i = 0; i<g_dwNumMtrls; i++) SAFE_RELEASE(g_pTextures[i]);SAFE_DELETE(g_pTextures); SAFE_DELETE(g_pMaterials); SAFE_RELEASE(g_pMesh);SAFE_RELEASE(g_pd3dDevice);SAFE_DELETE(g_pDInput);SAFE_RELEASE(g_pTextAdaperName)SAFE_RELEASE(g_pTextHelper)SAFE_RELEASE(g_pTextInfor)SAFE_RELEASE(g_pTextFPS)SAFE_RELEASE(g_pd3dDevice)}


上面代码的运行截图如下,可以发现这个X文件中存放的模型的细节非常的细腻:

【Visual C++】游戏开发札记四十四 浅墨DirectX教程十二 网格模型和X文件使用面面观

【Visual C++】游戏开发札记四十四 浅墨DirectX教程十二 网格模型和X文件使用面面观

【Visual C++】游戏开发札记四十四 浅墨DirectX教程十二 网格模型和X文件使用面面观

【Visual C++】游戏开发札记四十四 浅墨DirectX教程十二 网格模型和X文件使用面面观

【Visual C++】游戏开发札记四十四 浅墨DirectX教程十二 网格模型和X文件使用面面观

【Visual C++】游戏开发札记四十四 浅墨DirectX教程十二 网格模型和X文件使用面面观





文章最后,依旧是放出本篇文章配套源代码的下载:

 

本节笔记配套源代码请点击这里下载:

 

 

【浅墨DirectX提高班】配套源代码之十二下载 




文章最后,依然是【每文一语】栏目,今天的句子是: 


当处在低谷时一定要清醒,要忍耐,要淡定。这是生活给你一个难得的自省机会,利用这些时间多学习暗地里提高自己,为即将到来的高峰做准备! 祝大家新年快乐,迎接又一个崭新的一年~~

【Visual C++】游戏开发札记四十四 浅墨DirectX教程十二 网格模型和X文件使用面面观



下周一,让我们离游戏开发的梦想更近一步。

下周一,游戏开发笔记,我们,不见不散。


9楼maguiwa7小时前
…mark,目测很高深的样子。回去试试。。。
Re: zhmxy55514分钟前
回复maguiwan其实不难的
8楼ddzz5217小时前
支持浅墨大哥!
Re: zhmxy5557小时前
回复ddzz521n谢谢~
7楼zhuceyigemajiawan7小时前
请问浅墨老师,你对游戏祸害人这个问题怎么看?游戏祸害人你还愿意去做吗?
Re: zhmxy5557小时前
回复zhuceyigemajiawann任何一物都有两面性。游戏陶冶情操,丰富生活,这是好的一面。
Re: ljm_5039093787小时前
回复zhuceyigemajiawann游戏者需自律,不能自律的请远离游戏
Re: GRAY_XIAOXIAO7小时前
回复zhuceyigemajiawann游戏害人?哎!一个人要想堕落的话,游戏只是个借口。游戏自古就有,本来是用于消遣娱乐的,可如今怎会如此啊!真是有点匹夫无罪,怀璧有责的意思了。
6楼zzh504804昨天 16:40
游戏害人?哎!一个人要想堕落的话,游戏只是个借口.n支持
Re: zhmxy555昨天 17:08
回复zzh504804n:D
5楼yirancpp昨天 13:35
试问作者有出书的打算吗
4楼AndroidBox昨天 13:03
帅气。。。
3楼GRAY_XIAOXIAO昨天 11:19
哈哈 终于要到加载模型了,持续跟进中!博主,新年快乐啊,刚开年就能看到你的博客,顶......
Re: zhmxy555昨天 11:20
回复GRAY_XIAOXIAOn哈哈,谢谢你一直以来的支持,新年快乐~
2楼ljm_503909378昨天 10:55
终于看到加载3D模型了,一直好奇如何加载模型的,现在明了!支持老师!
Re: zhmxy555昨天 11:09
回复ljm_503909378n嗯,不客气~
1楼dbtxdxy昨天 10:13
一直在期待这篇~
Re: zhmxy555昨天 10:52
回复dbtxdxyn让你等到了~

热点排行