本系列文章由 net_assassin 整理编写,转载请注明出处。
http://blog.csdn.net/net_assassin/article/category/1100363
作者:net_assassin 邮箱: net_assassin@hotmail.com 期待着与志同道合的朋友们相互交流
本章我们学习表面,这是可以绘制在屏幕上的常规位图。回想一下你最爱不释手的那些游戏。它们都是3D游戏吗?很可能不是。牛气冲天的游戏中,2D游戏的数量要比3D游戏多。与其在这里对比、比较2D和3D,还不如都学习它们,然后按照游戏的需要来使用。
表面和位图
表面的Width和Height是按像素计算的。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;
}