3D游戏编程(二)

从普通的VC编程到DX SDK9, 以前的技术成了现在的工具. 接触图形学和DX有一个月的时间了, 真正开始理论与实践相结合, 是从转到金容俊的<3D游戏编程>开始的.

开发工具: Visual Studio 2005 (VC 8.0) + DirectX SDK Oct. 2006. (注意: 需要添加DirectX SDK的include和lib到解决方案中).

第一步: Win32 SDK程序框架

对于Windows的SDK, 我们脑中都有一个很清晰的框架. 这是借之进行DX编程的第一步:

// 此代码模块中包含的函数的前向声明:
ATOM                MyRegisterClass(HINSTANCE hInstance);
BOOL                InitInstance(HINSTANCE, 
int
);
LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK    About(HWND, UINT, WPARAM, LPARAM);

int
 APIENTRY _tWinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPTSTR    lpCmdLine,
                     
int
       nCmdShow)
{
    UNREFERENCED_PARAMETER(hPrevInstance);
    UNREFERENCED_PARAMETER(lpCmdLine);

     
// TODO: 在此放置代码。

    MSG msg;
    HACCEL hAccelTable;

    
// 初始化全局字符串

    LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
    LoadString(hInstance, IDC_WIN32SDK, szWindowClass, MAX_LOADSTRING);
    MyRegisterClass(hInstance);

    
// 执行应用程序初始化:

    if (!InitInstance (hInstance, nCmdShow))
    
{
        
return
 FALSE;
    }


    hAccelTable 
= LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_WIN32SDK));

    
// 主消息循环:

    while (GetMessage(&msg, NULL, 00))
    
{
        
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &
msg))
        
{
            TranslateMessage(
&
msg);
            DispatchMessage(
&
msg);
        }

    }


    
return (int) msg.wParam;
}


//
//    函数: InitInstance(HINSTANCE, int)
//

//    目的: 保存实例句柄并创建主窗口
//

//    注释:
//

//         在此函数中,我们在全局变量中保存实例句柄并
//
        创建和显示主程序窗口。
//
BOOL InitInstance(HINSTANCE hInstance, int
 nCmdShow)
{
   HWND hWnd;

   hInst 
= hInstance; // 将实例句柄存储在全局变量中


   hWnd 
= CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
      CW_USEDEFAULT, 
0, CW_USEDEFAULT, 0
, NULL, NULL, hInstance, NULL);

   
if (!
hWnd)
   
{
      
return
 FALSE;
   }


   ShowWindow(hWnd, nCmdShow);
   UpdateWindow(hWnd);

   
return TRUE;
}


//
//   函数: WndProc(HWND, UINT, WPARAM, LPARAM)
//

//   目的: 处理主窗口的消息。
//

//   WM_COMMAND    - 处理应用程序菜单
//
  WM_PAINT    - 绘制主窗口
//
  WM_DESTROY    - 发送退出消息并返回
//

//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    
int
 wmId, wmEvent;
    PAINTSTRUCT ps;
    HDC hdc;

    
switch
 (message)
    
{
    
case
 WM_COMMAND:
        wmId    
=
 LOWORD(wParam);
        wmEvent 
=
 HIWORD(wParam);
        
// 分析菜单选择:

        switch (wmId)
        
{
        
case
 IDM_ABOUT:
            DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
            
break
;
        
case
 IDM_EXIT:
            DestroyWindow(hWnd);
            
break
;
        
default
:
            
return
 DefWindowProc(hWnd, message, wParam, lParam);
        }

        
break;
    
case
 WM_PAINT:
        hdc 
= BeginPaint(hWnd, &
ps);
        
// TODO: 在此添加任意绘图代码...

        EndPaint(hWnd, &ps);
        
break
;
    
case
 WM_DESTROY:
        PostQuitMessage(
0
);
        
break
;
    
default
:
        
return
 DefWindowProc(hWnd, message, wParam, lParam);
    }

    
return 0;
}

 

对这一框架我就不多说.  

第二步: 创建D3D设备

我安装的是DirectX SDK Oct. 2006, 在Visual Studio 2005下使用VC的SDK编程.  

 对于D3D, 因和硬件相关, 需要对其进行初始化. 我将初始化工作放在InitInstance中. 在程序退出之前, 又需将其从存储区中释放:

#include <d3d9.h>

#pragma comment (lib, "d3d9.lib")
LPDIRECT3D9 g_pD3D = NULL;      // 创建D3D设备的D3D对象参数
   LPDIRECT3DDEVICE9 g_pD3DDevice = NULL;   // 渲染中使用的D3D设备

BOOL InitInstance(HINSTANCE hInstance, 
int  nCmdShow)
{
    ......
    
// if (!hWnd)

    if (SUCCEEDED(InitD3D(hWnd)))            // 初始化D3D
    {
        ......
    }

    ......
}


BOOL InitD3D(HWND hWnd)
{
    
if(NULL == (g_pD3D =
 Direct3DCreate9(D3D_SDK_VERSION)))
    
{
        
return
 E_FAIL;
    }


    D3DPRESENT_PARAMETERS D3Dpp;      
// 创建设备的结构体
    ZeroMemory(&D3Dpp, sizeof(D3Dpp));         // 将结构体清零
    D3Dpp.Windowed = TRUE;                              // 创建窗口模式
    D3Dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;   // 最有效的Swap效果
    D3Dpp.BackBufferFormat = D3DFMT_UNKNOWN;        // 建立与当前显示模式匹配的后置缓冲

    
if(FAILED(g_pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd,
        D3DCREATE_SOFTWARE_VERTEXPROCESSING,
        
&D3Dpp, &
g_pD3DDevice)))
    
{
        
return
 E_FAIL;
    }

    return S_OK;
}


LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    
switch
 (message)
    
{
        ......
    
case
 WM_PAINT:
        Render();                
// 绘图函数

        ValidateRect(hWnd, NULL);
        
break
;
    
case
 WM_DESTROY:
        ClearUp();              
// 按栈序释放所有对象

        PostQuitMessage(0);
        
break
;
    
default
:
        
return
 DefWindowProc(hWnd, message, wParam, lParam);
    }

    
return 0;
}


void  Render()
{
    
if(NULL ==
 g_pD3DDevice)
        
return
;

    
// 清除后置缓冲区, 同时设置为蓝色 (0, 0, 255)

    g_pD3DDevice->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(000), 1.0f0);

    
// 开始渲染

    if(SUCCEEDED(g_pD3DDevice->BeginScene()))
    
{
        ......
        
// 结束渲染

        g_pD3DDevice->EndScene();
    }


    
// 显示后置缓冲的画面
    g_pD3DDevice->Present(NULL, NULL, NULL, NULL);
}

1) 包含头文件<d3d9.h>和链接库文件"d3d9.lib";

2) 初始化D3D对象, 并创建D3D设备对象;

3) 设备后置缓冲及渲染操作;

4) 释放DIRECT3D9和DIRECT3DDEVICE9对象以及窗口类.

第三步: 绘制基本二维图形

三角形是构成其他几何图形 (二维或三维) 的基本元素. 在绘制三角形的过程中, 最主要的是对顶点 (Vertex) 进行操作. 在第二步中我将绘图函数Render() 置于WM_PAINT消息中处理, 在游戏编程中由于用户对画面的FPS (每秒钟帧数) 要求较高. 相比之下, CPU对消息的处理速度是比较快的, 因此利用消息循环的闲置时间进行绘图是可行的:

LPDIRECT3D9 g_pD3D  =  NULL;         //  创建D3D设备的D3D对象参数
LPDIRECT3DDEVICE9 g_pD3DDevice  =  NULL;     //  渲染中使用的D3D设备
LPDIRECT3DVERTEXBUFFER9 g_pVB  =  NULL;     //  存储顶点的顶点缓冲

int  APIENTRY _tWinMain(HINSTANCE hInstance,
                       HINSTANCE hPrevInstance,
                       LPTSTR    lpCmdLine,
                       
int        nCmdShow)
{
    ......
    
// 执行应用程序初始化:
    if (!InitInstance (hInstance, nCmdShow))
    
{
        
return FALSE;
    }


    hAccelTable 
= LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_TETRIS2007));

    ZeroMemory(
&msg, sizeof(msg));        // 必须将消息清零

    
// 主消息循环:
    while (WM_QUIT != msg.message)     // 不再用传统的GetMessage来判断
    {
        
if (PeekMessage(&msg, NULL, 0U0U, PM_REMOVE))
        
{
            TranslateMessage(
&msg);
            DispatchMessage(
&msg);
        }

        
else
        
{
            Render();        
// 没有消息处理则绘图
        }

    }

    
return (int) msg.wParam;
}


BOOL InitInstance(HINSTANCE hInstance, 
int  nCmdShow)
{
    ......
    
if (SUCCEEDED(InitD3D(hWnd)))
    
{
        
if(SUCCEEDED(InitVB()))        // 创建顶点缓冲并写入
        {
            ShowWindow(hWnd, nCmdShow);
            UpdateWindow(hWnd);
        }

    }

    
return TRUE;
}


HRESULT InitVB()
{
    
// 渲染三角形顶点
    CUSTOMVERTEX vertices[] = 
    
{
        
{150.0f50.0f0.5f1.0f0xffff0000},    // x, y, z, rhw, color
        {250.0f250.0f0.5f, `.0f, 0xff00ff00},
        
{50.0f250.0f0.51.0f0xff0000ff}
    }
;

    
// 创建顶点缓冲
    if(FAILED(g_pD3DDevice->CreateVertexBuffer(3 * sizeof(CUSTOMVERTEX),
        
0, D3DFVF_CUSTOMVERTEX, D3DPOOL_DEFAULT, &g_pVB, NULL)))
    
{
        
return E_FAIL;
    }


    
// 写入顶点缓冲
    VOID *pVertices;
    
if(FAILED(g_pVB->Lock(0sizeof(vertices), (void **)&pVertices, 0)))
    
{
        
return E_FAIL;
    }

    memcpy(pVertices, vertices, 
sizeof(vertices));
    g_pVB
->Unlock();

    
return S_OK;
}


void  Render()
{
    ......
    
if(SUCCEEDED(g_pD3DDevice->BeginScene()))
    
{
        
// 创建矩阵
        SetupMatrices();

        
// 绘制顶点缓冲三角形
        
// 绑定设备数据流
        g_pD3DDevice->SetStreamSource(0, g_pVB, 0sizeof(CUSTOMVERTEX));

        
// 指定顶点着色信息(FVF)
        g_pD3DDevice->SetFVF(D3DFVF_CUSTOMVERTEX);

        
// 输出
        g_pD3DDevice->DrawPrimitive(D3DPT_TRIANGLELIST, 01);

        
// 结束渲染
        g_pD3DDevice->EndScene();
    }


    
// 显示后置缓冲的画面
    g_pD3DDevice->Present(NULL, NULL, NULL, NULL);
}

 

1) 在WinMain中, 在消息循环之前需将msg所在存储区清零;

2) 在无消息处理时进行绘图;

3) 掌握Free Vertex Format并进行Vertex Buffer初始化;

4) 在写顶点缓冲时, 需对VB进行加锁Lock(), 写完释放Unlock();

5) 在绘图时, 绑定设备数据流SetStreamSource(), 指定顶点格式SetFVF(), 设定输出几何信息DrawPrimitive().

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值