用Direct3D实现三维漫游.docx
- 文档编号:10137593
- 上传时间:2023-02-08
- 格式:DOCX
- 页数:29
- 大小:35.21KB
用Direct3D实现三维漫游.docx
《用Direct3D实现三维漫游.docx》由会员分享,可在线阅读,更多相关《用Direct3D实现三维漫游.docx(29页珍藏版)》请在冰豆网上搜索。
用Direct3D实现三维漫游
用Direct3D实现三维漫游
成都理工学院17#岳朝伟
朋友,喜欢玩游戏吗?
自从计算机诞生以来,人们对计算机游戏就充满了热情,以前对字符型的游戏就乐此不疲,随着时代的发展,人们的要求也越来越高,三维实时游戏和多媒体应用已经成为人们追求的热点。
而Microsoft公司推出的DirectX正是这样一个热点的集合。
如果你想抓住时代的潮流,并喜欢迎接挑战的话,本文将是一个很好的去处。
同时,本人将以此文讲述用Direct3D保留模式的各种编程技巧和应用,而对于有关基本的DirectX的介绍,请参阅相关的文章。
程序调试环境:
Win98、VisualC++6.0,它们都内嵌了DirectX5.0,同时,为了大家调试的方便和确保程序正常运行,我们将使用最低级的DirectX驱动程序。
一、Direct3DRM中的基本概念
1.0、Direct3D简介
Direct3D是Microsoft公司推出的DirectX的一部分,它又包括立即模式(Direct3DIM)和保留模式(Direct3DRM)。
同时,即使是一个最基本的Direct3D例程也必须包含DirectDraw对象,它们是紧密的结合在一起的,因此,你在编写Direct3D应用程序之前还应先对DirectDraw有一定的认识!
Direct3D立即模式是一种较低级的三维模式,因而牵涉到各种复杂的三维图形学的知识,而保留模式是建立在一系列的类调用的基础之上,其中大量的底层操作和运算都被封装在这些类当中,因此适合快速的创建一个三维环境并希望能实时的操作它。
本程序就是利用它建立了一个可以用方向键控制的三维漫游程序!
2.0、Direct3DRM中有关图形学的基本内容介绍
Direct3DRM中牵涉到很多三维图形学的东西,这里本人对程序当中将要涉及到的最重要的部分作一简单介绍,让大家对Direct3DRM尽快的有一个感性的认识。
照相机(camera)场景(scene)视点(viewport)框架(frame)的概念及它们之间的关系:
(这些概念与一些三维图形软件中的概念相当一致,如3DS ,因此你要是有这方面软件、的使用经验的话,你会更容易的理解这些概念)
框架(frame):
它是将各种三维网格托住的支架,同时,它还将灯光,照相机,物体等都虚拟成现实世界中的东西,它们都需要放在某一特定的物理框架上才能被托住,不至于落下来。
各个框架之间的关系也非常符合现实世界中的各物体的位置关系,即要想被看见,则它必须在照相机框架的前面,同理,物体要想被灯光照着也必须在灯光框架的前方。
各个框架之间可以形成一个树结构,多个框架组合在一起就形成了场景。
场景(scene):
是一个根框架,场景中的所有其它框架都应建立在它的基础之上,它是所有其它框架的父框架,而它没有父框架,且一个视点只能有一个场景。
照相机:
它是建立在视点当中的用于可视的框架,在现实世界中可以将它想象成人的眼睛。
同时,可以任意设置场景中的任一框架为照相机,方法有两种,一种是在创建Viewport时直接给定某一框架为Camera,二是用Viewport对象提供的SetCamera()设置,因此,当你需要改变不同的视角时就可以用SetCamera()来调整观察的相对视角,但在适时漫游当中无需这样做,用第一种方法就足够了。
视点:
它是用于决定在场景中的什么位置可以看到物体,这里的物体实质上已经变成了框架,而框架当中往往又包含了某一可见的网格。
而视点实质上是一个二维矩形区域,且只有沿X,Y的正方向上可见的区域才能被渲染,且所有可见的物体被着色时都将自动变换成照相机坐标,这一点与人眼的作用相对应。
某一框架(在场景当中,且能被Viewport所见)
框架当中的网格
Viewport(视点),Camera附属于Viewport,通过Camera的坐标调整即实现三维漫游同时场景也是相对于Viewport重合的(实质上比Viewport大)
3.0、回调函数
下面介绍一下Direct3DRM当中经常用到的一类函数----回调函数(CallBackfunction),这种函数必须深入理解才能明白Direct3DRM当中的各个调用过程,以后才能自如的添加你自己的代码。
回调函数的特殊性:
●回调函数的创建不是任意的,都应遵循某一特定的结构和参数列表,且必须以这样的参数形式才能被其它函数正确调用。
●回调函数的调用是附属于某一特定的函数,而不能单独使用,且还不能通过跟踪父函数来达到跟踪回调函数的目的,而这样做只能导致回调函数已经执行了。
因此为了调试或检验回调函数体的代码,必须采用调试窗口当中的RuntoCursor的方法来调试,这样才能让调试器跟踪进入回调函数体。
●回调函数的深入理解:
(只针对设备的查找用到的回调函数),因它是一种自动查找函数,故在其父函数找到某一特定的设备或驱动程序后它就会自动调用给定的回调函数,这样就形成一个循环,:
父函数查找设备—回调函数检验设备—回调函数返回—再由父函数查找。
其中当回调函数返回D3DENUMRET_OK则让父函数继续查找形成循环,返回D3DENUMRET_CANCEL则停止查找父函数结束。
●其它形式的回调函数:
最常用于Direct3DRM当中的是Movecallbackfunction(),它于设备查找回调函数的明显区别在于设备查找回调函数只跟随父函数执行一次,而Movecallback()会在接口IDirect3DRMViewport:
:
:
Move()调用的美一次都要执行各个框架的Movecallback(),这样可以统一管理各个框架的动作,且一个回调函数可以应用于多个框架,同时,它在Direct3DRM当中对动画的应用起着相当重要的作用,即在插值动画与一般的动画当中都必须使用回调函数来逐帧的渲染你设定的每一个动作。
小结:
这里讲述的回调函数只能让你有一个感性的认识,具体认识到具体的程序当中才能体会。
接下来我们将进入代码的添加阶段,并给出详细的注释,你可得仔细看好了。
4.0、窗口框架的生成
我们将利用MFC和DirectXSDK进行建造,这样做的好处是可以结合了它们各自的优点。
同时,大多数的Win32例子均采用SDK编写—晦涩难懂,但代码高效,而MFC却一目了然。
还有我们为了调试的方便,将建立窗口形式的Direct3D应用程序。
这里我们将借助MFC提供的对话框模板加以改进就能实现MFC和DirectXSDK的无缝联接。
具体表现为在对话框类中的InitInstance()中将自己创建的窗口类变为主窗口,则以后对系统的操作将完全转化为对这个自定义窗口的操作,也就任由你摆布了。
具体代码添加过程如下。
初始化基本窗口类模板步骤:
●首先启动VC++6.0,从File下选择New,选择Project下MFCAPPWizard(exe),命名为D3Dtest,除向导第二项全部不选外,接受其它所有缺省值。
●去掉对话框资源,在FileView处去掉D3DtestDlg.cpp和D3DtestDlg.h。
在ResourceView处去掉Dialog栏。
●打开主菜单下Insert,从NewClass下Name栏输入d3dwnd在BaseClass栏选genericCWnd作为基类。
OK
●打开d3dwnd.cpp文件,在View主菜单下选ClassVizard,classname选d3dwnd类,Message分别双击选WM_PAINT、WM_DESTROY、WM_MOVE、WM_SIZE、WM_KEYDOWN默认消息处理函数名,OK。
●打开d3dwnd.cpp创建自定义函数(具体方法是在ClassView列表中d3dwnd处单击右键选addMemberFunction)名为:
voidSetFreshRegion(HWNDwin); //窗口更新
voidShowInfo(); //将渲染结果放到前台
HRESULTD3DGetSurfDesc(LPDDSURFACEDESClpDDSurfDesc,
LPDIRECTDRAWSURFACElpDDSurf);
voidClearBuffer(LPDIRECTDRAWSURFACElpbuffer); //初始化面对象
BOOLCreatefaces(intw,inth);//创建面对象
voidCleanUp(); //释放各个对象
BOOLMyScene(); //初始化场景内容
BOOLCreateDevAndView(LPDIRECTDRAWCLIPPERlpclipper,
intdriver,intwidth,intheight);
BOOLEnumDrivers(); //查询驱动设备
BOOLRenderLoop(); //着色循环
BOOLInitD3D(HWNDwin); //初始化D3DRM
BOOLCreate3DWnd(); //创建3D窗口
●打开D3Dtest.cpp文件,在View主菜单下选ClassVizard,classname选CD3DtestApp类Message栏分别双击InitInstance、ExitInstance、OnIdle然后OK即可。
这里重载这些函数是进行渲染的需要。
以上建立了基本类模板和主窗口的基本消息处理函数,下面是在各个文件中添加的代码:
为了能够正确的编译并连接,我们将作如下的准备:
工程项目设置:
在Project主菜单下选Settings,在link栏的object/librarymodules处加入如下的.lib文件才能保证程序的正确连接:
dinput.libddraw.libd3dxof.libd3drm.libdxguid.libwinmm.lib
网格准备:
这里用DirectXSDK提供的sphere3.x和triplane.x
图片准备:
背景用DirectXSDK提供的lake.bmp,球体帖图用它提供的tutor.bmp(长宽均满足2的整数次幂)
●首先创建一个名为D3Daid.h的头文件,并加入当前工程当中。
这个文件主要记录程序当中用到的各种自定义结构,这里为了方便将它们综合在一个文件当中。
添加代码如下:
structAid_Value
{
CRectclient_rect;//自己建立的窗口客户矩形区域
POINTstartpoint;//窗口客户区的起点(相对整个屏幕)
LPDIRECT3DRMDEVICEdev;//D3D保留模式设备对象指针
LPDIRECTDRAWlpddraw;//DDraw对象指针
LPDIRECTDRAWSURFACElpprimary;//主面对象(大小为800X600)
LPDIRECTDRAWSURFACElpback;//后台面对象(大小与窗口一样)
LPDIRECT3DRMVIEWPORTview;//保留模式视点对象指针
LPDIRECT3DRMFRAMEscene;//场景框架
LPDIRECT3DRMFRAMEcamera;//照相机框架
GUIDDriverGUID[5];//可能找到的全局统一标识符结构
charDriverName[5][50];//可能找到的三维驱动设备名称
D3DVALUEdx,dy,dz;//照相机的漫游坐标
D3DVALUERotate_rad;//旋转弧度
intNumDrivers;//可能找到的驱动设备数目
intCurrDriver;//当前使用的驱动设备
BOOLbRotate;//旋转标志(在三维漫游当中使用)
BOOLbMove;//移动标志(通上)
BOOLbQuit;//程序结束标志
BOOLbInitialized;//程序被初始化标志
BOOLbMinimized;//程序最小化标志
intBPP;//当前系统使用的颜色位数
};
●打开D3Dtest.h添加代码如下:
#include"d3dwnd.h"
classCD3DtestApp:
publicCWinApp
{
public:
d3dwnd*p3Dwnd;//自定义的窗口对象指针
intfailcount;//渲染失败数目
};
●打开d3dwnd.h添加代码如下:
#defineINITGUID//预定义COM构件的全局统一标识符
#include"d3drmwin.h"//D3D保留模式
#include"D3Daid.h"//自定义结构
●打开D3Dtest.cpp添加代码如下:
这里不用系统产生的InitInstance()函数
BOOLCD3DtestApp:
:
InitInstance()
{
p3Dwnd=newd3dwnd;//分配空间
p3Dwnd->Create3DWnd();//创建窗口并初始化D3DRM
m_pMainWnd=p3Dwnd;//让自定义窗口设为当前线程的主窗口
m_pMainWnd->ShowWindow(SW_SHOW);//显示窗口
m_pMainWnd->UpdateWindow();//更新窗口
failcount=0;//初始化渲染失败次数
returnTRUE;
}
BOOLCD3DtestApp:
:
OnIdle(LONGlCount)
{
//TODO:
Addyourspecializedcodehereand/orcallthebaseclass
if(!
p3Dwnd->RenderLoop())//当没有消息处理时就进行渲染
++failcount;
if(failcount>2)
p3Dwnd->CleanUp();//如果失败三次则退出程序
returnTRUE;//这里不执行系统的空闲函数CWinApp:
:
OnIdle(lCount);
}
intCD3DtestApp:
:
ExitInstance()
{
//TODO:
Addyourspecializedcodehereand/orcallthebaseclass
deletep3Dwnd;//退出程序时去掉窗口指针
returnCWinApp:
:
ExitInstance();
}
●打开d3dwnd.cpp添加代码如下:
#include"math.h"
#defineMAX_DRIVERS5//最大驱动器数目
#definePI26.28318f//圆周
#defineRotate_stepPI2/100.0f//旋转步长
#defineMove_step0.5f//移动步长
#defineSAFE_RELEASE(x)if(x!
=NULL){x->Release();x=NULL;}
#defineMSG(str)MessageBox(str,"ApplicationMessage",MB_OK)
//预定义两个常用的宏
HWNDhwnd=NULL;//初始化主窗口句柄
LPDIRECT3DRMlpD3DRM=NULL;//初始化D3D保留模式对象指针
LPDIRECTDRAWCLIPPERlpDDClipper=NULL;//初始化裁剪对象指针
Aid_Valuemyvalue;//自定义结构对象
DWORDBppToDDbd(intbpp)//进行颜色位数匹配
{
switch(bpp)
{
case1:
returnDDBD_1;
case2:
returnDDBD_1;
case4:
returnDDBD_4;
case8:
returnDDBD_8;
case16:
returnDDBD_16;
case24:
returnDDBD_24;
case32:
returnDDBD_32;
default:
return0;
}
}
//设备查找回调函数
//回调函数将找到的一个匹配的设备驱动器后,进行填充并退出设备查找
//匹配准则是:
与当前的系统的颜色位数一致,一般都使用硬件抽象层
HRESULTWINAPIenumDeviceFunc(GUID*lpGuid,
LPSTRlpDeviceDescription,LPSTRlpDeviceName,
LPD3DDEVICEDESClpHWDesc,LPD3DDEVICEDESClpHELDesc,
LPVOIDlpContext)
{
LPD3DDEVICEDESClpdesc;//D3D设备描述结构
int*lpStartDriver=(int*)lpContext;
lpdesc=lpHWDesc->dcmColorModel?
lpHWDesc:
lpHELDesc;
if(!
lpdesc->dwDeviceRenderBitDepth&BppToDDbd(myvalue.BPP))
returnD3DENUMRET_OK;
memcpy(&myvalue.DriverGUID[myvalue.NumDrivers],lpGuid,sizeof(GUID));
lstrcpy(&myvalue.DriverName[myvalue.NumDrivers][0],lpDeviceName);
if(lpdesc==lpHWDesc&&lpdesc->dcmColorModel!
=D3DCOLOR_MONO)
*lpStartDriver=myvalue.NumDrivers;
myvalue.NumDrivers++;
if(myvalue.NumDrivers==5)
return(D3DENUMRET_CANCEL);
returnD3DENUMRET_OK;
}
voidUserControl(LPDIRECT3DRMFRAMEframe,void*arg,D3DVALUEdelta)
{
LPDIRECT3DRMFRAMEscene;
frame->GetScene(&scene);
if(myvalue.bMove)
{
frame->AddTranslation(D3DRMCOMBINE_AFTER,
myvalue.dx,myvalue.dy,myvalue.dz);
frame->SetPosition(scene,myvalue.dx,myvalue.dy,myvalue.dz);
myvalue.bMove=FALSE;
}
if(myvalue.bRotate)
{
frame->AddRotation(D3DRMCOMBINE_REPLACE,
0,1,0,myvalue.Rotate_rad);
frame->SetPosition(scene,myvalue.dx,myvalue.dy,myvalue.dz);
myvalue.bRotate=FALSE;
}
SAFE_RELEASE(scene);
}
//以上是全局函数和变量的声名,应把它们放在d3dwnd:
:
d3dwnd()构造函数之前
//以下是自定义函数的实体和各个消息响应函数
//创建窗口并初始化D3D的各种对象
BOOLd3dwnd:
:
Create3DWnd()
{
LPCTSTRm_name=AfxRegisterWndClass(CS_HREDRAW|CS_VREDRAW,
AfxGetApp()->LoadCursor(IDC_ARROW),//光标
(HBRUSH)GetStockObject(BLACK_BRUSH),//窗口背景
AfxGetApp()->LoadIcon(IDR_MAINFRAME)//图标
);//窗口注册
BOOLbret=CreateEx(WS_EX_APPWINDOW,m_name,"Direct3DRMExample",
WS_VISIBLE|WS_OVERLAPPEDWINDOW,//定义窗口属性
CW_USEDEFAULT,CW_USEDEFAULT,400,400,//窗口大小和起始位置
NULL,NULL,NULL);//创建窗口
if(!
bret)
returnFALSE;
hwnd=GetSafeHwnd();
if(!
InitD3D(hwnd))//初始化D3D
{
MSG("InitDirect3DFailed!
");
OnDestroy();
returnFALSE;
}
returnTRUE;
}
BOOLd3dwnd:
:
InitD3D(HWNDwin)
{
LPDIRECT3DRMTEXTURElptex=NULL;//背景文理帖图
//注意:
D3D中的文理帖图的长宽都必须是2的整数次幂
HDChdc;
ZeroMemory(&myvalue,sizeof(myvalue));//初始化自定义结构对象
hdc=:
:
GetDC(win);//取得当前窗口的设备句柄
myvalue.BPP=:
:
GetDeviceCaps(hdc,BITSPIXEL);//取得系统的当前颜色位数
:
:
ReleaseDC(win,hdc);
SetFreshRegion(win);//更新窗口位置
if(!
EnumDrivers())//查找匹配的驱动设备
returnFALSE;
myvalue.lpddraw->SetCooperativeLevel(win,DDSCL_NORMAL);
//设置协作等级(这里是窗口协作等级)
if(!
Createfaces(myvalue.client_rect.Width(),myvalue.client_rect.Height()))
returnFALSE;//根据窗口的大小来建立各种要用到的面对象
:
:
Direct3DRMCreate(&lpD3DRM);//创建D3D保留模式对象
lpD3DRM->CreateFrame(NULL,&myvalue.scene);//创建场景根框架
lpD3DRM->CreateFrame(myvalue.scene,&myvalue.camera);//创建场景中照相机框架
if(myvalue.scene->SetOrientation(NULL,D3DVAL(0.0),D3DVAL(0.0),D3DVAL(1.0),
D3DVAL(0.0),D3DVAL(-1.0),D3DVAL(0.0))!
=D3DRM_OK)
//设置场景的方向(默认的方向)
returnFALSE;
lpD3DRM->LoadTexture("lake.bmp",&lptex);//创建背景纹理帖图
myvalue.scene->SetSceneBackgroundImage(lptex);//设置背景帖图
/*myvalue.scene->SetSceneBackgroundRGB(0.5f,0.6f,0.7f)
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Direct3D 实现 三维 漫游