DirectX游戏编程入门——第二部分(游戏编程工具箱) —— 绘制位图

本系列文章由 net_assassin 整理编写,转载请注明出处。

http://blog.csdn.net/net_assassin/article/category/1100363


作者:net_assassin 邮箱: net_assassin@hotmail.com 期待着与志同道合的朋友们相互交流


本章我们学习表面,这是可以绘制在屏幕上的常规位图。回想一下你最爱不释手的那些游戏。它们都是3D游戏吗?很可能不是。牛气冲天的游戏中,2D游戏的数量要比3D游戏多。与其在这里对比、比较2D3D,还不如都学习它们,然后按照游戏的需要来使用。

表面和位图

表面是一个像素点阵,在 Direct3D 中主要用来存储 2D 图形数据。图 1.2 指明了表面的一些成分。由图可以看出表面数据就像一个矩阵,像素数据实际上存储在线性数组里面。

表面的WidthHeight是按像素计算的。Pitch以字节为单位。而且Pitch有可能比Width大且依赖于低层硬件,所以不能单纯的认为Pitch =Width * sizeof (pixelFormat)


双缓冲技术

帧缓冲区位于视频内存中,代表要在显示器上显示的图像。所以,创建图形最简单的方法就是直接修改帧缓存区,这是合情合理的,结果就是我们可以在屏幕上立即看到改变。基本上一切就是这样工作的,但这里漏掉了一个小细节。我们不会直接在帧缓冲区上绘图,因为在绘制、擦除、移动及重绘图形的同时屏幕正在被刷新,这会导致抖动。我们要做的是,在一个离屏缓冲区上绘制所有的一切,然后将这个 “双重” 或者 “后台” 缓冲区非常快速地喷在屏幕上。

上一章我们通过设置呈现参数创建了一个后台缓冲区。而后,通过 使用 Clear函数,将后台缓冲区填满了绿色,并使用Present函数刷新屏幕。在不知不觉中,我们使用了双重/ 后台缓冲区!


d3dpp.SwapEffect=D3DSWAPEFFECT_DISCARD;


d3ddev->Clear(0,NULL,D3DCLEAR_TARGET,D3DCOLOR_XRGB(0,255,0),1.0f,0);

d3ddev->Present(NULL,NULL,NULL,NULL);

这是Direct3D提供的一个美妙的特性——一个内置的后台缓冲区。

创建和绘制表面

要创建Direct3D表面,首先要声明一个指向内存中的表面的变量。

LPDIRECT3DSURFACE9 surface = NULL;
//create surface
    HRESULT result = d3ddev->CreateOffscreenPlainSurface(
        100,                //width of the surface
        100,                //height of the surface
        D3DFMT_X8R8G8B8,    //surface format
        D3DPOOL_DEFAULT,    //memory pool to use
        &surface,           //pointer to the surface
        NULL);              //reserved (always NULL)


清除表面

d3ddev->ColorFill(surface, NULL, D3DCOLOR_XRGB(r,g,b));


绘制表面

d3ddev->StretchRect(surface, NULL, backbuffer, &rect, D3DTEXF_NONE);


示例

代码如下:

#include <windows.h>
#include <d3d9.h>
#include <time.h>
#include <iostream>
using namespace std;

//application title
const string APPTITLE = "Create Surface Program";

//macro to read the keyboard 
#define KEY_DOWN(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 1 : 0)

//screen resolution
#define SCREENW 1024
#define SCREENH 768

//Direct3D objects
LPDIRECT3D9 d3d = NULL; 
LPDIRECT3DDEVICE9 d3ddev = NULL; 
LPDIRECT3DSURFACE9 backbuffer = NULL;
LPDIRECT3DSURFACE9 surface = NULL;

bool gameover = false;


/**
 ** Game initialization function
 **/
bool Game_Init(HWND hwnd)
{
    //initialize Direct3D
    d3d = Direct3DCreate9(D3D_SDK_VERSION);
    if (d3d == NULL)
    {
        MessageBox(hwnd, "Error initializing Direct3D", "Error", MB_OK);
        return false;
    }

    //set Direct3D presentation parameters
    D3DPRESENT_PARAMETERS d3dpp; 
    ZeroMemory(&d3dpp, sizeof(d3dpp));
    d3dpp.Windowed = TRUE;
    d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
    d3dpp.BackBufferFormat = D3DFMT_X8R8G8B8;
    d3dpp.BackBufferCount = 1;
    d3dpp.BackBufferWidth = SCREENW;
    d3dpp.BackBufferHeight = SCREENH;
    d3dpp.hDeviceWindow = hwnd;

    //create Direct3D device
    d3d->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hwnd,
        D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dpp, &d3ddev);

    if (!d3ddev)
    {
        MessageBox(hwnd, "Error creating Direct3D device", "Error", MB_OK);
        return false;
    }

    //set random number seed
    srand(time(NULL));

    //clear the backbuffer to black
    d3ddev->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0,0,0), 1.0f, 0);
    
    //create pointer to the back buffer
    d3ddev->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &backbuffer);

    //create surface
    HRESULT result = d3ddev->CreateOffscreenPlainSurface(
        100,                //width of the surface
        100,                //height of the surface
        D3DFMT_X8R8G8B8,    //surface format
        D3DPOOL_DEFAULT,    //memory pool to use
        &surface,           //pointer to the surface
        NULL);              //reserved (always NULL)

    if (!SUCCEEDED(result)) return false;


    return true;
}

/**
 ** Game update function
 **/
void Game_Run(HWND hwnd)
{
    //make sure the Direct3D device is valid
    if (!d3ddev) return;

    //start rendering
    if (d3ddev->BeginScene())
    {
        //fill the surface with random color
        int r = rand() % 255;
        int g = rand() % 255;
        int b = rand() % 255;
        d3ddev->ColorFill(surface, NULL, D3DCOLOR_XRGB(r,g,b));

        //copy the surface to the backbuffer
        RECT rect;
        rect.left = rand() % SCREENW/2;
        rect.right = rect.left + rand() % SCREENW/2;
        rect.top = rand() % SCREENH;
        rect.bottom = rect.top + rand() % SCREENH/2;
        d3ddev->StretchRect(surface, NULL, backbuffer, &rect, D3DTEXF_NONE);
    
        //stop rendering
        d3ddev->EndScene();

        //display the back buffer on the screen
        d3ddev->Present(NULL, NULL, NULL, NULL);
    }

    //check for escape key (to exit program)
    if (KEY_DOWN(VK_ESCAPE))
        PostMessage(hwnd, WM_DESTROY, 0, 0);
}

/**
 ** Game shutdown function
 **/
void Game_End(HWND hwnd)
{
    if (surface) surface->Release();
    if (d3ddev) d3ddev->Release();
    if (d3d) d3d->Release();
}


/**
 ** Windows event callback function
 **/
LRESULT WINAPI WinProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
    switch( msg )
    {
        case WM_DESTROY:
            gameover = true;
            PostQuitMessage(0);
            return 0;
    }
    return DefWindowProc( hWnd, msg, wParam, lParam );
}


/**
 ** Windows entry point function
 **/
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    //create the window class structure
    WNDCLASSEX wc;
    wc.cbSize = sizeof(WNDCLASSEX); 
    wc.style         = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc   = (WNDPROC)WinProc;
    wc.cbClsExtra     = 0;
    wc.cbWndExtra     = 0;
    wc.hInstance     = hInstance;
    wc.hIcon         = NULL;
    wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
    wc.lpszMenuName  = NULL;
    wc.lpszClassName = APPTITLE.c_str();
    wc.hIconSm       = NULL;
    RegisterClassEx(&wc);

    //create a new window
    HWND window = CreateWindow(APPTITLE.c_str(), APPTITLE.c_str(), 
       WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
       SCREENW, SCREENH, NULL, NULL, hInstance, NULL);

    //was there an error creating the window?
    if (window == 0) return 0;

    //display the window
    ShowWindow(window, nCmdShow);
    UpdateWindow(window);
    
    //initialize the game
    if (!Game_Init(window)) return 0;


    // main message loop
    MSG message;
    while (!gameover)
    {
        if (PeekMessage(&message, NULL, 0, 0, PM_REMOVE)) 
        {
            TranslateMessage(&message);
            DispatchMessage(&message);
        }

        Game_Run(window);
    }

    return message.wParam;
}


从磁盘装载位图

事实上,Direct3D不知道如何装载位图。幸运的是,在D3DX助手库中有将位图装载到表面的函数。

HRESULT D3DXLoadSurfaceFromFile(
             LPDIRECT3DSURFACE9 pDestSurface,
             CONST PALETTEENTRY * pDestPalette,
             CONST RECT*  pDestRect,
             LPCTSR pSrcFile,
             CONST RECT* pSrcRect,
             DWORD Filter,
             D3DCOLOR ColorKey,
             D3DXIMAGE_INFO*  pSrcInfo
);

示例:



代码:

#include <windows.h>
#include <d3d9.h>
#include <d3dx9.h>
#include <time.h>
#include <iostream>
using namespace std;

//program values
const string APPTITLE = "Load Bitmap Program";
const int SCREENW = 1024;
const int SCREENH = 768;

//key macro
#define KEY_DOWN(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 1 : 0)

//Direct3D objects
LPDIRECT3D9 d3d = NULL; 
LPDIRECT3DDEVICE9 d3ddev = NULL; 
LPDIRECT3DSURFACE9 backbuffer = NULL;
LPDIRECT3DSURFACE9 surface = NULL;

bool gameover = false;

/**
 ** Game initialization function
 **/
bool Game_Init(HWND window)
{
    //initialize Direct3D
    d3d = Direct3DCreate9(D3D_SDK_VERSION);
    if (!d3d)
    {
        MessageBox(window, "Error initializing Direct3D", "Error", MB_OK);
        return false;
    }

    //set Direct3D presentation parameters
    D3DPRESENT_PARAMETERS d3dpp; 
    ZeroMemory(&d3dpp, sizeof(d3dpp));
    d3dpp.Windowed = TRUE;
    d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
    d3dpp.BackBufferFormat = D3DFMT_X8R8G8B8;
    d3dpp.BackBufferCount = 1;
    d3dpp.BackBufferWidth = SCREENW;
    d3dpp.BackBufferHeight = SCREENH;
    d3dpp.hDeviceWindow = window;

    //create Direct3D device
    d3d->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, window,
        D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dpp, &d3ddev);

    if (!d3ddev)
    {
        MessageBox(window, "Error creating Direct3D device", "Error", MB_OK);
        return 0;
    }

    //clear the backbuffer to black
    d3ddev->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0,0,0), 1.0f, 0);
    
    //create surface
    HRESULT result = d3ddev->CreateOffscreenPlainSurface(
        SCREENW,                //width of the surface
        SCREENH,                //height of the surface
        D3DFMT_X8R8G8B8,    //surface format
        D3DPOOL_DEFAULT,    //memory pool to use
        &surface,           //pointer to the surface
        NULL);              //reserved (always NULL)

    if (!SUCCEEDED(result)) return false;

    //load surface from file into newly created surface
    result = D3DXLoadSurfaceFromFile(
        surface,            //destination surface
        NULL,               //destination palette
        NULL,               //destination rectangle
        "legotron.bmp",     //source filename
        NULL,               //source rectangle
        D3DX_DEFAULT,       //controls how image is filtered
        0,                  //for transparency (0 for none)
        NULL);              //source image info (usually NULL)

    //make sure file was loaded okay
    if (!SUCCEEDED(result)) return false;

    return true;
}

/**
 ** Game update function
 **/
void Game_Run(HWND hwnd)
{
    //make sure the Direct3D device is valid
    if (!d3ddev) return;

    //create pointer to the back buffer
    d3ddev->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &backbuffer);

    //start rendering
    if (d3ddev->BeginScene())
    {
        //draw surface to the backbuffer
        d3ddev->StretchRect(surface, NULL, backbuffer, NULL, D3DTEXF_NONE);
     
        //stop rendering
        d3ddev->EndScene();
        d3ddev->Present(NULL, NULL, NULL, NULL);
    }

    //check for escape key (to exit program)
    if (KEY_DOWN(VK_ESCAPE))
        PostMessage(hwnd, WM_DESTROY, 0, 0);
}

/**
 ** Game shutdown function
 **/
void Game_End(HWND hwnd)
{
    if (surface) surface->Release();
    if (d3ddev) d3ddev->Release();
    if (d3d) d3d->Release();
}

/**
 ** Windows event handling function
 **/
LRESULT WINAPI WinProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
    switch( msg )
    {
        case WM_DESTROY:
            gameover = true;
            PostQuitMessage(0);
            return 0;
    }
    return DefWindowProc( hWnd, msg, wParam, lParam );
}

/**
 ** Windows entry point function
 **/
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    //initialize window settings
    WNDCLASSEX wc;
    wc.cbSize = sizeof(WNDCLASSEX); 
    wc.style         = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc   = (WNDPROC)WinProc;
    wc.cbClsExtra	 = 0;
    wc.cbWndExtra	 = 0;
    wc.hInstance     = hInstance;
    wc.hIcon         = NULL;
    wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
    wc.lpszMenuName  = NULL;
    wc.lpszClassName = APPTITLE.c_str();
    wc.hIconSm       = NULL;
    RegisterClassEx(&wc);

    //create a new window
    HWND window = CreateWindow( APPTITLE.c_str(), APPTITLE.c_str(),
       WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
       SCREENW, SCREENH, NULL, NULL, hInstance, NULL);

    //was there an error creating the window?
    if (window == 0) return 0;

    //display the window
    ShowWindow(window, nCmdShow);
    UpdateWindow(window);
	
	//initialize the game
    if (!Game_Init(window)) return 0;


    // main message loop
	MSG message;
	while (!gameover)
    {
        if (PeekMessage(&message, NULL, 0, 0, PM_REMOVE)) 
	    {
		    TranslateMessage(&message);
		    DispatchMessage(&message);
	    }

        //process game loop 
        Game_Run(window);
    }

	return message.wParam;
}


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值