分享
 
 
 

MFC架构下的DirectX 8.0

王朝other·作者佚名  2006-01-08
窄屏简体版  字體: |||超大  

1、MFC框架

(DX8MFC)

这里的MFC框架指的是一个符合游戏开发应用的框架,当然你也可以写一个符合你要求的MFC框架。如果你对MF

C比较熟悉的话可以直接从第二章开始阅读。本框架是以后几个例子的基础,如果你对MFC不是很了解的话,就要认

真阅读本章,以求对这个MFC框架有一个深入的了解。

框架中包括两个类:

CDX8MFCApp类和CFrameWin类,CDX8MFCApp类是应用程序类,CFrameWin类是框架的主类,以后我们的大部分代

码都是从这里扩展的。首先来看一看CDX8MFCApp类,它包括CDX8MFCApp()、ExitInstance()、InitInstance()、On

Idle(LONG lCount)等成员函数和一个Game对象。

InitInstance()成员函数在程序初始化时就被调用,在这里我建立了一个窗口:

ExitInstance()成员函数在程序终止时被调用,在这里我们释放一些对象和指针:

OnIdle(LONG lCount)成员函数会在没有Windows消息要处理的时候被调用,也就是说OnIdle()成员函数会不断

的被调用,这正好被我们用作游戏循环。

BOOL CDX8MFCApp::InitInstance()

{

// The one and only window has been initialized, so show and update it.

m_pMainWnd = new CFrameWin();

m_pMainWnd->ShowWindow(m_nCmdShow);

m_pMainWnd->UpdateWindow();

Game = (CFrameWin*) m_pMainWnd;

Game->Init();

return TRUE;

}

ExitInstance()成员函数在程序终止时被调用,在这里我们释放一些对象和指针:

int CDX8MFCApp::ExitInstance()

{

// TODO: Add your specialized code here and/or call the base class

Game->End();

delete Game;

return CWinApp::ExitInstance();

}

OnIdle(LONG lCount)成员函数会在没有Windows消息要处理的时候被调用,也就是说OnIdle()成员函数会不断

的被调用,这正好被我们用作游戏循环。

BOOL CDX8MFCApp::OnIdle(LONG lCount)

{

// TODO: Add your specialized code here and/or call the base class

if(Game->window_active==TRUE)

{

Game->Active();

Game->window_active=FALSE;

}

Game->Go();

return TRUE;

}

Game对象是一个CFrameWin类指针,我们在InitInstance()成员函数中创建了一个CFrameWin对象并把CFrameWi

n对象的指针值赋给Game。下面我们来看一看CFrameWin类, 它包括Active()、End()、Go()、Init()、Update()等

成员函数。

Init()成员函数,你可以在这里做一些自己的初始化。回顾CDX8MFCApp类的InitInstance()成员函数可知,在

完成窗口初始化后InitInstance()成员函数里就调用了Game->Init(),也就是说Init()在窗口初始化后被调用。

void CFrameWin::Init()

{

AfxMessageBox("Init");

}

Go()成员函数会不断的被循环调用,它又调用了Update()和DestroyWindow()。Update()用于更新窗口,调用D

estroyWindow()则会结束应用程序。如果你把DestroyWindow()语句删除掉,程序会不断的循环。

void CFrameWin::Go() // Game循环

{

AfxMessageBox("Go");

Update();

DestroyWindow();

}

Active()成员函数会在应用程序被击活的时候被调用。

void CFrameWin::Active() //窗口被激活

{

TRACE("Active");

AfxMessageBox("Active");

}

这个程序并不做任何事,只是一个MFC框架。

2、初始化DirectX

(DX8MFC1)

本例将以第一章的MFC框架为基础对CFrameWin类进行扩展。主要加入了DrawScene()、InitDirect3D(HWND hwn

d)和ShutdownDirect3D()三个函数。

InitDirect3D(HWND hwnd)函数对Direct3D进行初始化:

HRESULT CFrameWin::InitDirect3D(HWND hwnd)

{

pID3D = Direct3DCreate8(D3D_SDK_VERSION);

HRESULT hr;

do

{

// we need the display mode so we can get

// the properties of our back buffer

D3DDISPLAYMODE d3ddm;

hr = pID3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &d3ddm);

if(FAILED(hr))

break;

D3DPRESENT_PARAMETERS present;

ZeroMemory(&present, sizeof(present));

present.SwapEffect = D3DSWAPEFFECT_COPY;

present.Windowed = TRUE;

present.BackBufferFormat = d3ddm.Format;

hr = pID3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hwnd,

D3DCREATE_SOFTWARE_VERTEXPROCESSING, &present, &pID3DDevice);

if(FAILED(hr))

break;

// we do our own coloring, so disable lighting

hr = pID3DDevice->SetRenderState(D3DRS_LIGHTING , FALSE);

}

while(0);

return hr;

}

IDirect3D是我们首先要用到的接口,你可以这样写:

IDirect3D8 * pID3D = Direct3Dcreate8(D3D_SDK_VERSION);

在你使用pID3D以前,请检查pID3D是否为非空。你下一步通常是创建D3D设备,但在创建D3D设备之前你要调用

GetAdapterDisplayMode方法取得必须的信息:

D3DDISPLAYMODE d3ddm; pID3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &d3ddm);

接下来是取得当前显示模式参数。下面的参数是Surface格式。你可以用这些参数来创建一个D3DPRESENT_PARA

METERS结构:

D3DPRESENT_PARAMETERS present;

ZeroMemory(&present, sizeof(present));

present.SwapEffect = D3DSWAPEFFECT_COPY;

present.Windowed = TRUE;

present.BackBufferFormat = d3ddm.Format;

D3DPRESENT_PARAMETERS描述了显示器Surface的信息,交换机制的类型,应用程序是窗口的还是全屏模式等信

息。在本例中, Surface是以拷贝方法代替页面翻转的,因为它是一个窗口模式的应用程序。把后台表面设置成与

当前显示模式相匹配的格式,一个准备显示的Surface可以Draw在后台表面上。

现在你可以创建一个IDirect3DDevice8接口了:

pID3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hwnd,

D3DCREATE_SOFTWARE_VERTEXPROCESSING, &present, &pID3DDevice);

这个函数有六个参数,幸运的是没有一个是很复杂的。D3DADAPTER_DEFAULT告诉Direct3D使用主显示器,只有

当你使用多显示器时才是必须的。你可以使用一个数值来指定另外一个显示器。调用IDirect3D的GetAdapterCount

将返回系统的适配器数目。

第二个参数,D3DDEVTYPE_HAL,告诉Direct3D使用硬件加速。其它选项包括D3DDEVTYPE_REF和D3DDEVTYPE_SW,

通常你都会希望使用硬件加速的,但有时侯你可能会使用软件加速进行测试。指定窗口取得焦点。如果是全屏应用

程序,你需要一个最顶层窗口。 D3DCREATE_SOFTWARE_VERTEXPROCESSING指定顶点处理类型。你也可以使用硬件加

速或是联合类型,我不使用硬件加速为的是广泛的兼容性。如果你想支持T&L, 则你必须使用硬件加速。最后两个

参数很简单,一个是你以前建立的,而pID3Ddevice是你现在要创建的IDirect3DDevice8接口。如果方法返回D3DER

R_NOTAVAILABLE,则你写的参数是正确的,但你的设备不支持你指定的参数。

最完美的是这个方法自动为你创建后台缓冲(back buffers)和深度缓冲(depth buffers) 。剪裁(Clippi

ng)作为后台表面(backface culling)被自动激活。灯光也被自动激活了,直到你定义顶点颜色之前,你可以禁

止使用灯光:

pID3DDevice->SetRenderState(D3DRS_LIGHTING, FALSE);

// DrawScene() 函数

HRESULT CFrameWin::DrawScene()

{

HRESULT hr;

do

{

// clear back buffer

hr = pID3DDevice->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_RGBA(0,63,0,0), 0, 0);

if(FAILED(hr))

break;

// start drawing

hr = pID3DDevice->BeginScene();

if(FAILED(hr))

break;

// Put all drawing code here

hr = pID3DDevice->EndScene();

if(FAILED(hr))

break;

// flip back buffer to front

hr = pID3DDevice->Present(NULL, NULL, NULL, NULL);

}

while(0);

return hr;

}

Clear会填充你指定的缓冲区。你可以填充Z缓冲区、后台缓冲区或摸板缓冲区(stencil buffer)。在这个例

子中你将用绿色填充后台缓冲区。所以,我们设定D3DCLEAR_TARGET标志和绿色。 在本例中BeginScene和EndScene

并没有做什么,但在以后的例子中我们会用到它的。这两个函数是画图元时的例行公事代码,这个函数不断的翻转

后台表面。我们可以不断的在后台表面画一些东西,然后把后台表面翻转到前台表面。

// ShutdownDirect3D() 函数

void CFrameWin::ShutdownDirect3D()

{

HELPER_RELEASE(pTexture);

HELPER_RELEASE(pIndexBuffer);

HELPER_RELEASE(pStreamData);

HELPER_RELEASE(pID3DDevice);

HELPER_RELEASE(pID3D);

}

ShutdownDirect3D释放所有的接口。将来你可能要加入额外的代码来关闭Direct3D接口,但现在已经够了。如

果你运行程序,你将得到一个绿色背景的窗口。你可以按“ESC”键来退出应用程序。

3、画三角形

(DX8MFC2)

定义你的顶点格式,Direct3D引入了一种可变形顶点格式(flexible vertex format)(FVF)的概念。在FVF

中,你定义一个结构其中包括所需要的顶点组成部分。这个结构会随着你的程序而改变,但在这里你将初步把它定

义成这个样子:

struct MYVERTEX

{

FLOAT x, y, z; // The transformed position

FLOAT rhw; // 1.0 (reciprocal of homogeneous w)

DWORD color; // The vertex color

};

示例的开始定义了一个顶点结构,顶点的名称,和每一个三角形的顶点。在你的InitDirect3D函数中,你必须

创建一个顶点缓冲区:

int num_elems = sizeof(vertices) / sizeof(vertices[0]);

pID3DDevice->CreateVertexBuffer(sizeof(MYVERTEX) * num_elems,

D3DUSAGE_WRITEONLY,

D3DFVF_XYZRHW|D3DFVF_DIFFUSE, D3DPOOL_DEFAULT, &pStreamData);

函数的第一个参数是顶点结构的字节大小。在应用程序还不能读取顶点之前,传一个D3DUSAGE_WRITEONLY标记

给它。这里可以有不同的标记来指定如何处理你的顶点,但现在你可以确信Direct3D已经能正确的工作了。下一步

,指定我们用的是什么FVF格式。当你还没有使用坐标系预转换之前,指定为D3DFVF_XYZRHW标记。以后你使用自己

的矩阵坐标系转换时,把它改成D3DFVF_XYZ。D3DFVF_DIFFUSE告诉Direct3D,我们将为每一个顶点指定颜色。

D3DPOOL_DEFAULT指定内存的管理模式。最后一个参数是顶点缓冲区的指针,在例子1中你已经定义了它,但

并没有用上。 如果你不向顶点缓冲区填入有用数据的话,顶点缓冲区是没有用的:

MYVERTEX *v;

pStreamData->Lock(0, 0, (BYTE**)&v, 0);

for(int ii = 0; ii < num_elems; ii++)

{

v[ii].x = vertices[ii].x;

v[ii].y = vertices[ii].y;

v[ii].z = vertices[ii].z;

v[ii].rhw = vertices[ii].rhw;

v[ii].color = vertices[ii].color;

}

pStreamData->Unlock();

这是不难理解的,Lock返回一个你想写入顶点数据的指针。下一步你从你的顶点阵列中拷贝数据。然后,反还

这个指针。这一对的调用可以告诉Direct3D你的FVF格式, 并设定顶点阵列为当前的活动顶点阵列。(你可以有多

个顶点阵列)。

pID3DDevice->SetVertexShader(D3DFVF_XYZRHW | D3DFVF_DIFFUSE);

pID3DDevice->SetStreamSource(0, pStreamData, sizeof(MYVERTEX));

SetVertexShader告诉Direct3D使用与CreateVertexBuffer同样的格式。

SetStreamSource告诉Direct3D使用pStreamData作为当前顶点阵列,并取得所有元素的大小。

你现在可以加入画三角形的代码了。在DrawScene()的BeginScene和EndScene之间加入如下代码:

int num_elems = sizeof(vertices) / sizeof(vertices[0]);

pID3DDevice->DrawPrimitive(D3DPT_TRIANGLELIST, 0, num_elems / 3);

D3DPT_TRIANGLELIST标记将命令Direct3D画不连续的三角形。你指定从索引的第0个顶点开始, 指定所要画的

三角形数目。如果正确的话,你会看到一个三角形画在先前的绿色背景窗口上。

4、画索引三角形

(DX8MFC3)

上一章的画三角形方式运行效率是较低的,而实际上我们都会使用DrawIndexedPrimitive()而不是DrawPrimit

ive()。想一想,如果要画两个相连的三角形,共有四个顶点。用DrawIndexedPrimitive()画要画四个顶点,而用D

rawPrimitive()画则要画六个顶点。

如果你可以顶点建立索引,你就可以用DrawIndexedPrimitive()画三角形了。我们可以为一个三角形建立这样

的索引:

WORD indices[] = { 0, 1, 2 };

它表示三角形中,第一个顶点对应于顶点阵列的第0个顶点;三角形中,第二个顶点对应于顶点阵列的第1个顶

点;三角形中,第三个顶点对应于顶点阵列的第2个顶点; 要画索引三角形,首先要建立索引缓冲:

num_elems = sizeof(indices) / sizeof(indices[0]);

pID3DDevice->CreateIndexBuffer(sizeof(WORD) * num_elems,

D3DUSAGE_WRITEONLY, D3DFMT_INDEX16,

D3DPOOL_DEFAULT, &pIndexBuffer);

第二步是用顶点填充这个索引缓冲:

WORD *pIndex;

pIndexBuffer->Lock(0, 0, (BYTE **)&pIndex, 0);

for(ii = 0; ii < num_elems; ii++)

{

pIndex[ii] = indices[ii];

}

pIndexBuffer->Unlock();

设定索引缓冲:

pID3DDevice->SetIndices(pIndexBuffer, 0);

把DrawScene()的相应的pID3DDevice->DrawPrimitive(...)换成:

pID3DDevice->DrawIndexedPrimitive( D3DPT_TRIANGLELIST, 0, sizeof(indices) / sizeof(indices[0]),

0, sizeof(indices) / sizeof(indices[0]) / 3);

运行程序的到的还是一个三角形。

5、加入帖图

(DX8MFC4)

首先,在MYVERTEX结构中加入帖图坐标系tu和tv,并给顶点阵列赋以适当的值。

下一步,设置你的帖图:

D3DXCreateTextureFromFile(pID3DDevice, "dx5_logo.bmp", &pTexture);

pID3DDevice->SetTexture(0, pTexture);

其中的“dx5_logo.bmp”指的是帖图文件,你可以用其他的文件代替它,运行程序你会看到一个带帖图的三角

形。

6、帖图立方体

(DX8MFC5)

现在样我们来进入三维的世界吧!这里你要启用Z缓冲(z-buffer), 设置立方体的材质,世界坐标系和投影坐

标系。启用Z缓冲(z-buffer),你要在D3DPRESENT_PARAMETERS结构中加入:

present.EnableAutoDepthStencil = TRUE;

present.AutoDepthStencilFormat = D3DFMT_D16;

这里告诉DirectX8使用16位的Z缓冲,下一步:

pID3DDevice->SetRenderState(D3DRS_ZENABLE, TRUE);

到这里Z缓冲已经设置完成。最后你还要在DrawScene()中调用清除Z缓冲内容的代码:

pID3DDevice->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER,

D3DCOLOR_RGBA(0,63,0,0), 1.0, 0);

// MYVERTEX结构改成:

struct MYVERTEX

{

FLOAT x, y, z; // The transformed position

DWORD color; // The vertex color

FLOAT tu, tv; // Texture coordinates

};

在InitDirect3D()也作了相应改动,具体可见源代码。Direct3D中有多种矩阵,在这里只使用其中的三个:世

界、视图和投影矩阵。世界矩阵变换会把正方体放在世界坐标系中,视图矩阵变换把正方体放在可视空间内,投影

矩阵使正方体看起来有深度感。BuildMatrices()函数将建立这三个矩阵:

void CFrameWin::BuildMatrices()

{

D3DXMATRIX matrix;

D3DXMatrixRotationY(&matrix, timeGetTime() / 1000.0f);

pID3DDevice->SetTransform(D3DTS_WORLD, &matrix);

D3DXMatrixLookAtLH(&matrix,

&D3DXVECTOR3(0.0f, 3.0f, -5.0f), // 摄像机的空间位置

&D3DXVECTOR3(0.0f, 0.0f, 0.0f),// 摄象机观察点

&D3DXVECTOR3(0.0f, 1.0f, 0.0f)); // 摄象机向上方向矢量

pID3DDevice->SetTransform(D3DTS_VIEW, &matrix); // 设置我们的平截面为45度角

D3DXMatrixPerspectiveFovLH(&matrix, D3DX_PI / 4, 4.0f / 3.0f, 1.0f, 100.0f);

pID3DDevice->SetTransform(D3DTS_PROJECTION, &matrix);

}

运行本章的例子你将看到一个旋转的正方体。

你可以从gamedev.363.net下载本文所有例子的源程序,或通过E-Mail向本文作者索取。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有