DirectX 9 Overview
Graphics
DirectX Graphics is a combination of Direct Draw 2D graphics & Direct 3D 3D graphics.
All
2D graphics are now done with Direct3D.
Example First DirectX 9.0 pgm:
/****************************************************************
* shows the user how to setup a windowed directx application
* which clears the window to a
blue color
****************************************************************/
#include <windows.h>
// include directx9
#include
<d3d9.h>
// global variables
HINSTANCE hInst; // application instance
HWND wndHandle; // application window handle
LPDIRECT3D9 pD3D; // the Direct3D Object
LPDIRECT3DDEVICE9 pd3dDevice; // the Direct3D Device
// forward declarations
bool initWindow(HINSTANCE hInstance);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
// DirectX functions
bool
initDirect3D();
void render(void);
int
WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine, int nCmdShow)
{
if (!initWindow(hInstance))
{
MessageBox(NULL,
"Unable to create window", "ERROR", MB_OK);
return false;
}
if (!initDirect3D())
{
MessageBox(NULL,
"Unable to init Direct3D", "ERROR", MB_OK);
return false;
}
// Main
message loop:
MSG msg;
ZeroMemory(
&msg, sizeof(msg) );
while( msg.message!=WM_QUIT )
{
if( PeekMessage( &msg, NULL, 0U, 0U, PM_REMOVE ) )
{
TranslateMessage( &msg );
DispatchMessage(
&msg );
}
else
{
render();
}
}
// release the device and the direct3D
object
if( pd3dDevice !=
NULL)
pd3dDevice->Release();
if( pD3D != NULL)
pD3D->Release();
return (int) msg.wParam;
}
/*********************************************************************
* initWindow
*********************************************************************/
bool initWindow(HINSTANCE hInstance)
{
WNDCLASSEX wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style =
CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = (WNDPROC)WndProc;
wcex.cbClsExtra =
0;
wcex.cbWndExtra =
0;
wcex.hInstance =
hInstance;
wcex.hIcon =
0;
wcex.hCursor =
LoadCursor(NULL,
IDC_ARROW);
wcex.hbrBackground =
(HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = NULL;
wcex.lpszClassName =
"DirectXExample";
wcex.hIconSm =
0;
RegisterClassEx(&wcex);
// create the
window
wndHandle = CreateWindow("DirectXExample",
"DirectXExample",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
640,
480,
NULL,
NULL,
hInstance,
NULL);
if (!wndHandle)
return false;
ShowWindow(wndHandle, SW_SHOW);
UpdateWindow(wndHandle);
return true;
}
/*********************************************************************
* WndProc
*********************************************************************/
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_DESTROY:
PostQuitMessage(0);
break;
}
return DefWindowProc(hWnd, message, wParam, lParam);
}
/*********************************************************************
* initDirect3D --
initializes direct3D
*********************************************************************/
bool
initDirect3D()
{
pD3D = NULL;
pd3dDevice = NULL;
// create the directX
object
if(( pD3D = Direct3DCreate9(
D3D_SDK_VERSION )) == NULL)
{
return
false;
}
// fill the presentation parameters
structure
D3DPRESENT_PARAMETERS d3dpp;
ZeroMemory(
&d3dpp, sizeof(d3dpp) );
d3dpp.Windowed
= TRUE;
d3dpp.SwapEffect
= D3DSWAPEFFECT_DISCARD;
d3dpp.BackBufferFormat
= D3DFMT_UNKNOWN;
d3dpp.BackBufferCount = 1;
d3dpp.BackBufferHeight = 480;
d3dpp.BackBufferWidth = 640;
d3dpp.hDeviceWindow = wndHandle;
// create a default directx
device
if( FAILED( pD3D->CreateDevice(
D3DADAPTER_DEFAULT,
D3DDEVTYPE_REF, wndHandle,
D3DCREATE_SOFTWARE_VERTEXPROCESSING,
&d3dpp,
&pd3dDevice ) ) )
{
return false;
}
return true;
}
/*********************************************************************
* render
*********************************************************************/
void render(void)
{
// check to make sure we have a valid
Direct3D Device
if( NULL ==
pd3dDevice ) return;
// Clear the backbuffer
to a blue color
pd3dDevice->Clear(0,NULL,D3DCLEAR_TARGET,D3DCOLOR_XRGB(0,0,255),
1.0f, 0 );
// Present the backbuffer
contents to the display
pd3dDevice->Present(
NULL, NULL, NULL, NULL );
}
Libraries
To
use you must include: d3d9.lib
Surfaces
Front & back buffers are
automatically created for you in CreateDevice() if you set the backBufferCount & backBufferFormat
fields of the D3DPRESENT_PARAMETERS structure parameter.
A pointer to the backbuffer is returned by a call to GetBackBuffer().
Offscreen surfaces (area of video or system RAM) are created with a call
to CreateOffscreenPlainSurface().
Bitmaps
Loading
Bitmaps are easily load to an
offscreen surface by calling the library function: D3DXLoadSurfaceFromFile().
Displaying
Once loaded, bitmaps can be blitted to the backbuffer with a
call to StretchRect(). This function
can also be used to select an area of the surface to blit
to another area (instead of the whole bitmap).
Example:
;
void render(void)
{ // load offscreen
surface w/bitmap
IDirect3DSurface9* surface = NULL;
hResult
= D3DXLoadSurfaceFromFile(surface, NULL, NULL,
filename.c_str(), NULL, D3DX_DEFAULT,
0, NULL);
if (FAILED(hResult))
return NULL
// check to make sure we have a valid
Direct3D Device
if ( NULL ==
pd3dDevice )
return;
//
Clear the backbuffer to a blue color
pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET,
D3DCOLOR_XRGB(0,0,255), 1.0f, 0 );
IDirect3DSurface9* backbuffer;
pd3dDevice-> GetBackBuffer(0,0,
D3DBACKBUFFER_TYPE_MONO,
&backbuffer);
HRESULT hResult
= pd3dDevice->StretchRect(bkgrd, NULL,
backbuffer, NULL, D3DTEXF_NONE);
if (hResult != D3D_OK)
MessageBox(NULL,"failed",
"error", MB_OK);
// Present the backbuffer
contents to the display
pd3dDevice->Present( NULL, NULL, NULL, NULL );
}
Timing
As
an alternative to GetTickCount() – which has a granularity of 10 milliseconds, QueryPerformanceCounter() can be used.
Example:
LARGE_INTEGER
timeStart, timeEnd, timeLapsed,
timerFreq;
float rate;
QueryPerformanceCounter(&timeStart);
//
do something cool
…
QueryPerformanceCounter(&timeEnd);
timeLapsed = timeEnd.QuadPart
– timeStart.QuadPart;
rate = timeLapsed / QueryPerformanceFrequency(&timerFreq);
3D
A
point is 3D space (x, y, z) is definded
as in openGL except the positive z axis points away
from the viewer (left-handed).
Defining a point
struct
{ float x, y, z; // coord
float r, g, b,
a; // color
}
vertex;
Vertex Buffer
Area
of memory used to create a 3D object. To create one, call CreateVertexBuffer(). Then you need to load your object
data into the buffer. First you must call Lock() to enable writing to the buffer. Then you can memcpy() your data to the buffer. Afterwards
you will Unlock() the buffer.
Drawing the shape
Drawing
is a 3 step procedure.
·
Set
the stream source. SetStreamSource();
·
Configuring
the vertex shader. SetFVF();
·
Drawing
to the screen. DrawPrimitive();
Drawing a triangle
#include <windows.h>
#include <mmsystem.h>
#include <d3d9.h>
#include <d3dx9tex.h>
#include <string>
using namespace std;
HINSTANCE hInst; // holds the instance for
this app
HWND wndHandle; // global window
handle
LPDIRECT3DVERTEXBUFFER9 g_pVB = NULL; // Buffer to hold vertices
// A structure for our custom vertex type
struct
CUSTOMVERTEX
{
FLOAT x, y, z, rhw; // The untransformed, 3D position for the vertex
DWORD
color; // The
vertex color
};
// Our custom FVF, which
describes our custom vertex structure
#define D3DFVF_CUSTOMVERTEX
(D3DFVF_XYZRHW|D3DFVF_DIFFUSE)
LPDIRECT3D9
pD3D;
LPDIRECT3DDEVICE9
pd3dDevice;
////////////////////////////////////////////// forward
declarations
bool initWindow(HINSTANCE
hInstance);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
// direct3D functions
HRESULT SetupVB();
bool
initDirect3D(HWND hwnd);
void shutdown(void);
LPDIRECT3DVERTEXBUFFER9 createVertexBuffer(int size, DWORD usage);
void drawVB(LPDIRECT3DVERTEXBUFFER9
buffer);
int
WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance, LPTSTR lpCmdLine,
int nCmdShow)
{
// call our
function to init and create our window
if (!initWindow(hInstance))
{
MessageBox(NULL,
"Unable to create window", "ERROR", MB_OK);
return false;
}
// init direct3d
initDirect3D(wndHandle);
// setup the
vertex buffer and add the triangle to it.
SetupVB();
// Main
message loop:
// Enter the message loop
MSG msg;
ZeroMemory(
&msg, sizeof(msg) );
while( msg.message!=WM_QUIT )
{
// check
for messages
if( PeekMessage( &msg, NULL, 0U, 0U, PM_REMOVE ) )
{
TranslateMessage(
&msg );
DispatchMessage(
&msg );
}
// this
is called when no messages are pending
else
{
//
call our render function
pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0,0,0), 1.0f,
0 );
pd3dDevice->BeginScene();
//
draw the contents of the vertex buffer
drawVB(g_pVB);
pd3dDevice->EndScene();
pd3dDevice->Present( NULL, NULL, NULL, NULL );
}
}
// shutdown
the directx manager
shutdown();
return (int) msg.wParam;
}
HRESULT SetupVB()
{
HRESULT hr;
// Initialize
three vertices for rendering a triangle
CUSTOMVERTEX g_Vertices[]
=
{
{ 320.0f, 50.0f,
0.5f, 1.0f, D3DCOLOR_ARGB(0,255,0,0), }, // x, y, z, rhw,
color
{ 250.0f, 400.0f, 0.5f, 1.0f, D3DCOLOR_ARGB(0,0,255,0), },
{ 50.0f, 400.0f,
0.5f, 1.0f, D3DCOLOR_ARGB(0,0,0,255), },
};
// Create the vertex buffer
g_pVB = createVertexBuffer(3*sizeof(CUSTOMVERTEX),
D3DFVF_CUSTOMVERTEX);
// Fill the
vertex buffer.
VOID* pVertices;
hr = g_pVB->Lock( 0, sizeof(g_Vertices), (void**)&pVertices, 0 );
if FAILED (hr)
return E_FAIL;
// copy the
vertices into the buffer
memcpy( pVertices, g_Vertices, sizeof(g_Vertices) );
// unlock the
vertex buffer
g_pVB->Unlock();
return S_OK;
}
bool
initDirect3D(HWND hwnd)
{
HRESULT hr;
if( NULL == ( pD3D = Direct3DCreate9( D3D_SDK_VERSION ) ) )
{
return false;
}
D3DPRESENT_PARAMETERS
d3dpp;
ZeroMemory(
&d3dpp, sizeof(d3dpp) );
d3dpp.Windowed
= TRUE;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;
d3dpp.BackBufferCount =
1;
d3dpp.BackBufferHeight
= 480;
d3dpp.BackBufferWidth =
640;
d3dpp.hDeviceWindow = hwnd;
hr = pD3D->CreateDevice(
D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hwnd,
D3DCREATE_SOFTWARE_VERTEXPROCESSING,
&d3dpp, &pd3dDevice );
if FAILED (hr)
{
return false;
}
return true;
}
LPDIRECT3DVERTEXBUFFER9 createVertexBuffer(int size, DWORD usage)
{
HRESULT hr;
LPDIRECT3DVERTEXBUFFER9
buffer;
// Create the vertex buffer.
hr = pd3dDevice->CreateVertexBuffer(
size,
0,
usage,
D3DPOOL_DEFAULT,
&buffer,
NULL );
if FAILED ( hr)
return NULL;
return buffer;
}
void drawVB(LPDIRECT3DVERTEXBUFFER9
buffer)
{
pd3dDevice->SetStreamSource(
0, buffer, 0, sizeof(CUSTOMVERTEX) );
pd3dDevice->SetFVF(
D3DFVF_CUSTOMVERTEX );
pd3dDevice->DrawPrimitive(
D3DPT_TRIANGLESTRIP, 0, 1 );
}
void shutdown(void)
{
if( pd3dDevice != NULL)
{
pd3dDevice->Release();
pd3dDevice
= NULL;
}
if( pD3D != NULL)
{
pD3D->Release();
pD3D =
NULL;
}
}
bool initWindow(HINSTANCE hInstance)
{
WNDCLASSEX wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style =
CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc =
(WNDPROC)WndProc;
wcex.cbClsExtra =
0;
wcex.cbWndExtra =
0;
wcex.hInstance =
hInstance;
wcex.hIcon =
0;
wcex.hCursor =
LoadCursor(NULL,
IDC_ARROW);
wcex.hbrBackground =
(HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName =
NULL;
wcex.lpszClassName =
"DirectXExample";
wcex.hIconSm =
0;
RegisterClassEx(&wcex);
wndHandle = CreateWindow("DirectXExample",
"DirectXExample",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
640,
480,
NULL,
NULL,
hInstance,
NULL);
if (!wndHandle)
return false;
ShowWindow(wndHandle, SW_SHOW);
UpdateWindow(wndHandle);
return true;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_DESTROY:
PostQuitMessage(0);
break;
}
return DefWindowProc(hWnd, message, wParam, lParam);
}
Primitive types
·
point
list – a set of points
·
line
list - a set of point pairs defining
lines
·
line
strip – a set of points defining connected lines
·
triangle
list – a set of tupples defining triangles
·
triangle
strip – a set of points where each 3 consecutive points define a triangle
·
triangle
fan – triangles w/common point
To
draw primitives you need to encapsulate them in a scene block.
BeginScene()
// specify primitives
EndScene()
Transformations
·
D3DXMatrixTranslation()
·
D3DXMatrixRotation()
·
D3DXMatrixScaling()
Once
you specify your desired transformations you multiply them together using D3DXMatrixMultiply() to create your final transformation
matrix. Then you apply it to the rendering pipeline using SetTransformatiom().
Camera
Creating
the camera projections (matProj) can be done manually
(ich!) or by using the helper function D3DXMatrixPerspectiveFov() and setting the projection matrix
with SetTransformation(D3DTS_PROJECTION,
&matProj).
Positioning
the camera is done by calling D3DXMatrixLookAtLH().
Shading
Supports
the following shade models:
·
Flat
·
Gouraund
·
Phong
·
Force_dword
Objects
can be solid or wireframe by calling SetRenderState() to choose.
Lighting
Supports
the following light modes:
·
Ambient
·
Directional
·
Point
·
Spotlight
Each
light source will have one or more modes along with properties to set position,
direction, attenuation, etc. Use SetLight() to define a light source, SetRenderState() to apply it to the pipeline and LightEnable() to turn it on/off.
Materials
Support
the following reflections
·
Diffuse
·
Ambient
·
Specular
·
Emissive
Texture mapping and meshes are also supported, but I’m not
going there in this overview.