Introduction
In this tutorial I'm going to help you to begin learning Direct3D and create
a Direct3D compatible window. I assume that you are familiar with Win32
Programming. I use Visual Studio.NET to do my works but you can use any C++
compiler, of course you need DirectX SDK 8.0 or higher for compiling the
provided code that can download here (headers and libraries are
sufficient).
The DirectX story begins in 1995 when Microsoft was introducing Windows 95 to
the world, but Windows 95 had many problems such as bad support for different
hardware especially graphics hardware. These problems caused most game
programmers to use MS-DOS instead, but soon Microsoft provided Game SDK for
Windows 95 developers to improve driver support and this project was later
called DirectX. Between 1995 and 1998 Microsoft focused on DirectX and many new
features were added to DirectX and about six different versions were released
during that three year period, and at last they released Windows 98 which was
well setup to support DirectX and made programming easier for game developers,
so that almost all of them moved their projects to support DirectX and that made
Microsoft very happy!
DirectX is a software interface the Hardware Abstraction Layer(HAL) layered
on top of the standard hardware-specific drivers and hardware. DirectX creates a
common hardware independent layer providing support of many different types of
hardware. When using DirectX you write your program on a single hardware and
DirectX will guarantee that your program will be run on all other DirectX
compatible hardware components.
OK, enough about DirectX, Let's begin our work!
Getting started
If you use Microsoft Visual C++ create an empty Win32 project and in the
Project Properties>Linker>Input>Additional Dependencies add d3d8.lib
(main direct3d library), d3dx8.lib (extensions to the main library) and
winmm.lib (windows multimedia library).
Then you can enter this code as a new source file:
#include <windows.h>
#include <d3d8.h>
HWND hWnd;
LPDIRECT3D8 pD3D;
LPDIRECT3DDEVICE8 pDevice;
LRESULT CALLBACK WndProc( HWND hWnd , UINT message ,
WPARAM wParam , LPARAM lParam){
switch(message){
case WM_CLOSE:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hWnd,message,wParam,lParam);
}
bool CreateSimpWindow(int width, int height){
WNDCLASSEX wc;
wc.cbClsExtra = 0;
wc.cbSize = sizeof(wc);
wc.cbWndExtra = 0;
wc.hbrBackground = NULL;
wc.hCursor = NULL;
wc.hIcon = NULL;
wc.hIconSm = NULL;
wc.hInstance = GetModuleHandle(NULL);
wc.lpfnWndProc = WndProc;
wc.lpszClassName = "X3D";
wc.lpszMenuName = NULL;
wc.style = CS_VREDRAW|CS_HREDRAW|CS_OWNDC;
RegisterClassEx(&wc);
hWnd = CreateWindowEx(0,
"X3D", "X3D Direct3D Tutorial 1",
WS_OVERLAPPEDWINDOW|WS_VISIBLE, 100,100,width,height,
NULL,NULL,wc.hInstance,0);
return true;
}
bool InitD3D(){
if((pD3D = Direct3DCreate8(D3D_SDK_VERSION))==NULL){
return false;
}
D3DDISPLAYMODE d3ddm;
pD3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &d3ddm);
D3DPRESENT_PARAMETERS d3dpp;
ZeroMemory(&d3dpp, sizeof(d3dpp));
d3dpp.BackBufferFormat = d3ddm.Format;
d3dpp.Windowed = true;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
if(FAILED(pD3D->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL,
hWnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dpp, &pDevice))){
return false;
}
return true;
}
void RenderD3DScene(){
pDevice->Clear(0,NULL,D3DCLEAR_TARGET,0,1,0);
pDevice->Present(NULL,NULL,NULL,NULL);
}
void MessageLoop(){
MSG msg;
while(true){
RenderD3DScene();
if(PeekMessage(&msg,hWnd,0,0,PM_REMOVE)){
if(msg.message==WM_QUIT)return;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
}
INT WINAPI WinMain( HINSTANCE , HINSTANCE , LPSTR , INT ){
if(!CreateSimpWindow(400, 400))return 1;
if(!InitD3D())return 2;
MessageLoop();
return 0;
}
If every thing goes right, this code will create a window with a black
background. It may make you think that I can create a black window using much
easier method, and of course you can however the goal here is to properly
initialize Direct3D with a minimum amount of code and as simply as possible. In
later tutorials you will do a lot more with Direct3D. To make life easier I
decomposed the code into several functions: CreateSimpWindow
,
MessageLoop
, WndProc
, Init3D
(Initialize
Direct3D), and Render3DScene
(Render Direct3D Scene). In this
tutorial only the Direct3D functions are explained.
We begin with line two where we included the Direct3D 8 header file: d3d8.h,
then we defined two new variables:
LPDIRECT3D8 pD3D;
LPDIRECT3DDEVICE8 pDevice;
pD3D
is a pointer to IDirect3D8
interface that is
really a COM object and you can create other Direct3D interfaces such as
IDirect3DDevice
using it.
Now, let's take a look at the InitD3D
function:
if((pD3D = Direct3DCreate8(D3D_SDK_VERSION))==NULL){
return false;
}
Direct3DCreate8
Macro easily creates an IDirect3D8
interface for us, the only parameter required is D3D_SDK_VERSION
which is a macro specifying the Direct3D version of the header to ensures that
Direct3D header and library files are compatible. Now we have an
IDirect3D8
object, but to begin drawing we need a
IDirect3DDevice8
object. IDirect3D8:CreateDevice
creates an IDirect3DDevice8
object for us, but first we should
provide it's arguments:
D3DDISPLAYMODE d3ddm;
pD3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &d3ddm);
Here we created an object of D3DDISPLAYMODE
structure and then
fill it with default graphics adapter display mode. After running these
commands, d3ddm
will contains some information about desktop
display settings such as resolution, color depth, refresh frequency (you can
customize these values and this will be explained further in later tutorials)
and then we create an object of D3DPRESENT_PARAMETERS
structure:
D3DPRESENT_PARAMETERS d3dpp;
ZeroMemory(&d3dpp, sizeof(d3dpp));
d3dpp.BackBufferFormat = d3ddm.Format;
d3dpp.Windowed = true;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
First we make it empty by calling the ZeroMemory
macro (this
macro simply fill all of allocated memory with zero values), then we set buffer
depth color to the same as the desktop depth color, and we specify that program
will run in a window, and finally we specify D3D_SWAPEFFECT_DISCARD
which makes Direct3D to fill buffer with random values after beginning to draw
(This option usually used for debug purposes to ensure were are properly
clearing the screen). We did all of this to prepare d3dpp
for
passing to the IDirect3D8:CreateDevice
function as the fifth
argument:
if(FAILED(pD3D->CreateDevice( D3DADAPTER_DEFAULT,
D3DDEVTYPE_HAL, hWnd,
D3DCREATE_SOFTWARE_VERTEXPROCESSING,
&d3dpp, &pDevice))){
return false;
}
First parameter specifies that our IDirect3DDevice8
object uses
default adapter. Second parameter is device type, that we set to
D3DDEVTYPE_HAL
, it makes device to be created in hardware
abstraction layer as opposed to D3DDEVTYPE_REF
that causes to
create buffers in a software layer that is slower than hardware but that
supports the full feature set this is used for debugging purposes. Next
parameter is simply a handle to our window and fourth parameter specifies that
Direct3D should process all vertex operations in software, this could also be
D3DCREATE_HARDWARE_VERTEXPROCESSING
for hardware vertex processing
that is usually faster however supported on fewer cards. The fifth parameter
specifies the device display mode that we use via d3dpp
as well as
defining other properties and at last pDevice
is the pointer used
to hold the created Direct3D device. OK, Direct3D should now be initialized
successfully, now time for Drawing with the RenderD3DScene
function
pDevice->Clear(0,NULL,D3DCLEAR_TARGET,0,1,0);
pDevice->Present(NULL,NULL,NULL,NULL);
First we fill color buffer with black using the
IDirect3DDevice8:Clear()
function, first parameter is number of
regions that we want to clear, setting that to 0 specifies that we want entire
buffer to be cleared. Second parameter is the pointer to the array of rectangles
that we want to clear, specifying 0 as first parameter make this parameter be
ignored so we use NULL
. Next parameter specifies the buffer type,
in later tutorials you will learn about other types of buffers, and the fourth
parameter specifies the color, you can use D3DCOLOR_XRGB(r,g,b)
macro to calculate your desired color in this case 0 is black, the last two
parameters used are depth and stencil buffers that you will learn later for now
just use the default values. Then we present the buffer to the screen using the
Present function for now just use NULL
for the parameters they will
be explained more in later tutorials.
Conclusion
OK, compile the code and say hello to the Direct3D programming world! If any
thing seems complicated don't worry about it at the moment, you will be familiar
with Direct3D soon, just continue to next tutorials and you could also try
changing some of the values, if you read more about the explained functions in
the DirectX documentations it will really help you to understand how the program
works. And finally feel free to contact me if you have any questions. Good
Luck!