Triple Buffer
Official Development Community Other
Home Tutorials Forum Contact Us
Team Pages Articles Links Special Thanks
Team Projects Tools Gurus  


  Tutorial - Introduction to Direct3D
  Written by: Ali Akhtazadeh, Edited by: Erik Yuzwa
Published: 13/Jun/2006




Code


The code for this tutorial is going to be very simple. We’re not going to check the hardware for anything, and we’re not going to be displaying anything on the screen. All we’re going to do is create a Direct3D device and then loop until the user exits the application.

First we start off by defining the objects we are going to need to use. The IDirect3D9, IDirect3DDevice9 and D3DPRESENT_PARAMETERS have already been discussed in the previous section, so you should know what they are used for. Next we have to initialize the IDirect3D9 (D3D object) and the IDirect3DDevice9 (D3D device).


IDirect3D9* d3d = 0; IDirect3DDevice9* d3dDevice = 0; D3DPRESENT_PARAMETERS d3dpp;

First we’ll initialize the D3D object, because you need it to initialize the D3D device. Initializing the D3D object is an extremely simple step and will always be done the same. Just call the Direct3DCreate9 function and pass in D3D_SDK_VERSION as the only argument. Passing in D3D_SDK_VERSION tells D3D to compile the code against the current version of the SDK you are using.


d3d = Direct3DCreate9( D3D_SDK_VERSION );

After you call Direct3DCreate9, you should also check to make sure that the value it returns is not NULL. If it returns a NULL value that means it failed. Next up is the D3DPRESENT_PARAMETERS structure that we need to fill.


d3dpp.BackBufferCount = 1; d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD; d3dpp.MultiSampleType = D3DMULTISAMPLE_NONE; d3dpp.EnableAutoDepthStencil = TRUE; d3dpp.AutoDepthStencilFormat = D3DFMT_D16; d3dpp.BackBufferWidth = 400; d3dpp.BackBufferHeight = 300; d3dpp.BackBufferFormat = D3DFMT_UNKNOWN; d3dpp.Windowed = TRUE;

First we see that we are specifying one back-buffer. You can have more if your hardware supports it and there’s enough memory. Usually one is what you’d want however. Next we set EnableAutoDepthStencilFormat to true because we are using a depth-buffer.

After that we specify what format we want our depth-buffer to be in. You have a choice of a few different formats which are listed in the DirectX SDK documentation files. We’re specifying that we want a 16 bit depth buffer.

Then it’s the back-buffer’s width and height which we set to the width and height of our window. Then we have to specify which format we want the back buffer to be. I’ve specified D3DFMT_UNKNOWN which tells d3d to just use whatever format the desktop is in at this time. This however only works if you are using Direct3D in a windowed application and will not work with full-screen applications.

Now what is a back-buffer format? If you right-click your desktop and choose “properties” and then select the “Settings” tab, you’ll see a drop down box that says “Color quality”. From that drop down box you can choose either 32 bits, 16 bits, or maybe 24 bits depending on your system/hardware. This is the format of your desktop. Likewise, we have a format of our back-buffer. We can choose D3DFMT_A8R8G8B8, which means 8 bits for alpha, 8 for red, 8 for green and 8 for blue implying a 32 bit back-buffer. Or we can choose a format such as D3DFMT_R5G6B5 which means 5 bits for red, 6 bits for green and 5 bits for blue. D3DFMT_R5G6B6 is a 16 bit back-buffer.

A D3DFORMAT value always starts with D3DFMT_ and then describes the bits. The ‘X’ means unused, so in D3DFMT_X8R8G8B8, you have 8 bits that are not used for anything and the remaining 24 bits for red green and blue. D3DFMT_A8R8G8B8 is still a 32 bit format however. The ‘A’ means alpha, the ‘B’ means blue, the ‘G’ means green and the ‘R’ means red.

There are many different formats which are listed in the Direct3D SDK documentation files. You have to be careful in windowed mode because usually you cannot use any format you want. The format you use has to be the same as the format of you desktop.

Finally we set the ‘Windowed’ member to true which tells D3D that we are making a windowed application, not a full-screen one. After we fill in our D3DPRESENT_PARAMETERS we need to create our device with a call to IDirect3D9::CreateDevice.

When we call ‘CreateDevice’, we have to make sure that our call was successful. We do that by checking its return value. As previously mentioned, Direct3D (and DirectX as a whole) is based on COM. COM functions all return a similar value known as HRESULT. An HRESULT is simply an error code that tells you what happened in the function. Normally when the value S_OK is returned it means everything went fine. However we do not simply check for S_OK because there are other values that also mean everything went fine. So Microsoft created a macro that we are supposed to use to check COM return values. The two macros are the SUCCEEDED and the FAILED macro. If you pass in an HRESULT as an argument to either of the macros, the SUCCEDED macro will return “true” if the HRESULT means success, and the FAILED macro will return “false” if the HRESULT is an unsuccessful one.

So anyway, we give a simple call to the CreateDevice method of the IDirect3D9 object. We pass in a few arguments, give it our D3DPRESENT_PARAMETERS structure, and pass in the location of out IDirect3DDevice9 pointer and it will give us back an initialized IDirect3DDevice9 pointer. If our call was unsuccessful then the return value of the CreateDevice function will tell us so.


HWND hwnd; // Initialized and valid window handle HRESULT hr; // Error code return value variable hr = d3d->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hwnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dpp, &d3dDevice ); if( FAILED( hr ) ) { ::MessageBox( 0, "Could not create d3d device", "Error", MB_OK ); }

The CreateDevice function takes in 6 parameters. The first parameter tells D3D which graphics card to use. A system may have more then one graphics cards so we need to specify which adapter we want to use. The system’s adapters are number from 0 to the number of adapters minus 1. So if we explicitly wanted the first adapter, we’d pass in 0. For the second adapter we’d pass in 1, and so on. For the system’s default adapter, we pass in D3DADAPTER_DEFAULT.

The second parameter tells D3D whether to use the hardware on the system, or do everything in software. D3DDEVTYPE_HAL tells D3D to go through the hardware. HAL stands for “Hardware Abstraction Layer”. If you wanted D3D to stay in the software then you’d pass in D3DDEVTYPE_REF. The difference between REF and HAL is that you can use every single feature that D3D has on a REF device, but you can only use features that your hardware supports with a HAL device. The REF device is much…much slower however because it does all calculations in the software and takes no advantage of hardware.

The third parameters is your handle to the window you want to use, your main window that is. The fourth tells d3d where to process your vertices. This parameters is specifically for the “vertex processing” phase of the pipeline. You can choose to emulate this “block” in software by passing in D3DCREATE_SOFTWARE_VERTEXPROCESSING, or you can tell D3D to use the hardware (if available) by passing in D3DCREATE_HARDWARE_VERTEXPROCESSING. You can even use both by passing in D3DCREATE_MIXED_VERTEXPROCESSING and switch between hardware and software at run-time.

Software vertex processing is not that much slower then hardware vertex processing. The good thing about this is that even if you have hardware that doesn’t support the programmable pipeline in the vertex processing block, you can still get very decent speeds by emulating that part in software. But you can’t emulate the “pixel processing” block in the software because it’s just too darn slow to do so, that’s why there’s no option to do it.

After this call, if everything was OK and all the settings were acceptable, you’ll have a ready to use IDirect3DDevice9 object.


d3dDevice->Clear( 0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0xff0000ff, 1.0f, 0 ); d3dDevice->BeginScene(); d3dDevice->EndScene(); d3dDevice->Present( 0, 0, 0, 0 );

The above code has 4 statements are probably the most common function calls in D3D. The first function, Clear, tells D3D to clear whatever surface is being written on by the rendering pipeline. Usually this surface is the back-buffer (which is the case is in sample program), but it can also be a texture.

Clear has a few parameters: The first parameter is the number of rectangles in the array of RECTs that you pass to second parameter. We want to clear the whole screen so we haven’t specified any rectangles to clear. The second parameter is an array of RECTs that you want to clear. Again we don’t have any as we are clearing the entire surface, and not portions of it.

The third parameter tells D3D what to clear. We want to clear the color values, and the depth values. Remember that we have a depth-buffer as well. Clear can also clear depth values.

The fourth and fifth parameters tell D3D what value to clear the back-buffer and depth-buffer to respectively. The back-buffer which is composed of RGBA values can be cleared to some color value, and the depth-buffer which has floating point values can be cleared to some float value in the range of 0.0 (closest) to 1.0 (farthest). The last parameter is not of our concern right now as we are not using a stencil-buffer (yes another buffer). The stencil-buffer will be discussed in a later tutorial.

Anyway, the next two functions are always in pairs. BeginScene is always called before EndScene, and EndScene is always called after BeginScene. These two functions simply tell D3D that you are going to start rendering stuff and end rendering stuff respectively.

The fourth function is the Present function. Present tells D3D to put the contents of the back-buffer onto the front-buffer which we already discussed previously. The parameters for Present are mostly NULL/0 because we are putting everything on the back-buffer onto the front-buffer. For more info on the parameters of Present check the DirectX SDK documentation file.


if( d3dDevice ) d3dDevice->Release(); if( d3d ) d3d->Release();

All the above code is doing is telling the D3D device and the D3D object that we are done using them. All COM objects have a function called “Release”. This function informs the object in question that you are done using it. You can read more about this by reading up on the COM architecture that has been very briefly discussed already. Just know that any object you create with D3D has to be “freed”. And you free COM objects by calling their Release function.




Page: 1 2 3 4 5 6 7 8 9

Copyright © Triple Buffer Software 2002 - 2006.
Goto Top