directx8:MFC架构下的DirectX8



章 MFC框架 
(DX8MFC) 
这里MFC框架指个符合游戏开发应用框架当然你也可以写个符合你要求MFC框架如果你对MFC比较熟悉话可以直接从第 2章开始阅读本框架是以后几个例子基础如果你对MFC不是很了解就要认真阅读本章以求对这个MFC框架有个深入了解 
框架中包括两个类: 
CDX8MFCApp类和CFrameWin类CDX8MFCApp类是应用CFrameWin类是框架主类以后我们大部分代码都是从这里扩展首先来看看CDX8MFCApp类它包括CDX8MFCApp、ExitInstance、InitInstance、OnIdle(LONG lCount)等成员个Game对象 
InitInstance成员化时就被在这里我建立了个窗口: 

BOOL CDX8MFCApp::InitInstance 

    // The one and only window has been initialized, so show and update it. 
    m_pMainWnd =  CFrameWin
    m_pMainWnd->ShowWindow(m_nCmdShow); 
    m_pMainWnd->UpdateWindow
    Game = (CFrameWin*) m_pMainWnd; 
    Game->Init

     TRUE; 


ExitInstance成员终止时被在这里我们释放些对象和指针: 
 CDX8MFCApp::ExitInstance 

    // TODO: Add your specialized code here and/or call the base  
    Game->End
    delete Game; 

     CWinApp::ExitInstance


OnIdle(LONG lCount)成员会在没有Windows消息要处理时候被也就是说OnIdle成员会不断这正好被我们用作游戏循环 
BOOL CDX8MFCApp::OnIdle(LONG lCount) 

    // TODO: Add your specialized code here and/or call the base  
    (Game->window_activeTRUE) 
    { 
        Game->Active
        Game->window_active=FALSE; 
    } 

    Game->Go

     TRUE; 


Game对象是个CFrameWin类指针我们在InitInstance成员中创建了个CFrameWin对象并把CFrameWin对象指针值赋给Game 
下面我们来看看CFrameWin类它包括Active、End、Go、Init、Update等成员 
Init成员你可以在这里做些自己回顾CDX8MFCApp类InitInstance成员可知在完成窗口化后InitInstance成员里就了Game->Init也就是说Init在窗口化后被 
void CFrameWin::Init 

    AfxMessageBox("Init"); 


Go成员会不断被循环它又了Update和DestroyWindowUpdate用于更新窗口DestroyWindow则会结束应用如果你把DestroyWindow语句删除掉会不断循环 
void CFrameWin::Go //Game循环 

    AfxMessageBox("Go"); 
    Update
    DestroyWindow


Active成员会在应用被击活时候被 
void CFrameWin::Active //窗口被激活 

    TRACE("Active\\n"); 
    AfxMessageBox("Active"); 


这个并不做任何事只是个MFC框架你可以从 border=0>http://gamedev.363.net 下载例子或通过E-mail:[email protected] 向本文作者索取 

第 2章 化DirectX8 
(DX8MFC1) 
本例将以第MFC框架为基础对CFrameWin类进行扩展主要加入了DrawScene、InitDirect3D(HWND hwnd)和ShutdownDirect3D 3个 
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); 
        (FAILED(hr)) 
            

        D3DPRESENT_PARAMETERS present; 
        ZeroMemory(&present, (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); 

        (FAILED(hr)) 
            

        // we do our own coloring, so disable lighting 
        hr = pID3DDevice->SetRenderState(D3DRS_LIGHTING, FALSE); 

    } while(0); 

     hr; 


IDirect3D是我们首先要用到接口你可以这样写: 
IDirect3D8 * pID3D = Direct3Dcreate8(D3D_SDK_VERSION); 
在你使用pID3D以前请检查pID3D是否为非空 
你下步通常是创建D3D设备但在创建D3D设备的前你要GetAdapterDisplayMode思路方法取得必须信息: 
D3DDISPLAYMODE d3ddm; 
pID3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &d3ddm); 
接下来是取得当前显示模式参数下面参数是Surface格式你可以用这些参数来创建个D3DPRESENT_PARAMETERS结构: 
D3DPRESENT_PARAMETERS present; 
ZeroMemory(&present, (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); 
这个有 6个参数幸运是没有个是很复杂D3DADAPTER_DEFAULT告诉Direct3D使用主显示器只有当你使用多显示器时才是必须你可以使用个数值来指定另外个显示器IDirect3DGetAdapterCount将返回系统适配器数目 
第 2个参数D3DDEVTYPE_HAL告诉Direct3D使用硬件加速其它选项包括D3DDEVTYPE_REF 和 D3DDEVTYPE_SW通常你都会希望使用硬件加速但有时侯你可能会使用软件Software加速进行测试 
指定窗口取得焦点如果是全屏应用你需要个最顶层窗口 
D3DCREATE_SOFTWARE_VERTEXPROCESSING指定顶点处理类型你也可以使用硬件加速或是联合类型我不使用硬件加速为是广泛兼容性如果你想支持T&L则你必须使用硬件加速 
最后两个参数很简单个是你以前建立而pID3Ddevice是你现在要创建IDirect3DDevice8接口如果思路方法返回D3DERR_NOTAVAILABLE则你写参数是正确但你设备不支持你指定参数 
最完美是这个思路方法自动为你创建后台缓冲(back buffers)和深度缓冲(depth buffers)剪裁(Clipping)作为后台表面(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); 
        (FAILED(hr)) 
            

        // start drawing 
        hr = pID3DDevice->BeginScene
        (FAILED(hr)) 
            

        // Put all drawing code here 

        hr = pID3DDevice->EndScene
        (FAILED(hr)) 
            

        // flip back buffer to front 
        hr = pID3DDevice->Present(NULL, NULL, NULL, NULL); 
    } while(0); 

     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章 画 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 
}; 
举例开始定义了个顶点结构顶点名称和每个 3角形顶点在你InitDirect3D你必须创建个顶点缓冲区: 
 num_elems = (vertices) / (vertices[0]); 
pID3DDevice->CreateVertexBuffer((MYVERTEX) * num_elems, D3DUSAGE_WRITEONLY, 
                            D3DFVF_XYZRHW|D3DFVF_DIFFUSE, D3DPOOL_DEFAULT, &pStreamData); 
个参数是顶点结构字节大小在应用还不能读取顶点的前个D3DUSAGE_WRITEONLY标记给它这里可以有区别标记来指定如何处理你顶点但现在你可以确信Direct3D已经能正确工作了 
指定我们用是什么FVF格式当你还没有使用坐标系预转换的前指定为D3DFVF_XYZRHW标记以后你使用自己矩阵坐标系转换时把它改成D3DFVF_XYZD3DFVF_DIFFUSE告诉Direct3D我们将为每个顶点指定颜色D3DPOOL_DEFAULT指定内存管理模式 
最后个参数是顶点缓冲区指针在例子1中你已经定义了它但并没有用上 
如果你不向顶点缓冲区填入有用数据顶点缓冲区是没有用
MYVERTEX *v; 
pStreamData->Lock(0, 0, (BYTE**)&v, 0); 
for( 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, (MYVERTEX)); 
SetVertexShader告诉Direct3D使用和CreateVertexBuffer同样格式 
SetStreamSource告诉Direct3D使用pStreamData作为当前顶点阵列并取得所有元素大小 
你现在可以加入画 3角形代码了在DrawSceneBeginScene和EndScene的间加入如下代码: 
 num_elems = (vertices) / (vertices[0]); 
pID3DDevice->DrawPrimitive(D3DPT_TRIANGLELIST, 0, num_elems / 3); 
D3DPT_TRIANGLELIST标记将命令Direct3D画不连续 3角形你指定从索引第0个顶点开始指定所要画 3角形数目 
如果正确你会看到个 3角形画在先前绿色背景窗口上 

第 4章 画索引 3角形 
(DX8MFC3) 
画 3角形方式运行效率是较低而实际上我们都会使用DrawIndexedPrimitive而不是DrawPrimitive如果要画两个相连 3角形共有 4个顶点用DrawIndexedPrimitive画要画 4个顶点而用DrawPrimitive画则要画 6个顶点 
如果你可以顶点建立索引你就可以用DrawIndexedPrimitive画 3角形了我们可以为个 3角形建立这样索引: 
WORD indices = { 0, 1, 2 }; 
它表示 3角形中个顶点对应于顶点阵列第0个顶点; 3角形中第 2个顶点对应于顶点阵列第1个顶点; 3角形中第 3个顶点对应于顶点阵列第2个顶点; 
要画索引 3角形首先要建立索引缓冲: 
num_elems = (indices) / (indices[0]); 
pID3DDevice->CreateIndexBuffer((WORD) * num_elems, D3DUSAGE_WRITEONLY, 
                        D3DFMT_INDEX16, D3DPOOL_DEFAULT, &pIndexBuffer); 
第 2步是用顶点填充这个索引缓冲: 


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, 
                                (indices) / (indices[0]), 0, 
                                (indices) / (indices[0]) / 3); 
运行还是个 3角形 

第 5章 加入帖图 
(DX8MFC4) 
首先在MYVERTEX结构中加入帖图坐标系tu和tv并给顶点阵列赋以适当 
设置你帖图: 
D3DXCreateTextureFromFile(pID3DDevice, "dx5_logo.bmp", &pTexture); 
pID3DDevice->SetTexture(0, pTexture); 
其中"dx5_logo.bmp"是帖图文件你可以用其他文件代替它运行你会看到个带帖图 3角形 

第 6章 帖图立方体 
(DX8MFC5) 
现在样我们来进入 3维世界吧!这里你要启用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中有多种矩阵在这里只使用其中 3个:世界、视图和投影矩阵世界矩阵变换会把正方体放在世界坐标系中视图矩阵变换把正方体放在可视空间内投影矩阵使正方体看起来有深度感 
BuildMatrices将建立这 3个矩阵: 
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); 

运行本章例子你将看到个旋转正方体
Tags:  directx8.0c directx8sdk directx8.1b directx8

延伸阅读

最新评论

发表评论