Sunday, May 3, 2009

DirectX10 Tutorial 1 - Direct3D 10 Basics

In this tutorial, we will learn to create the bare minimum Direct3d application. We will create a Direct3D device, set it up to render to the window, and our end result will be a window with a colored background.

Before we get started on creating the device, I'd like to highlight the variables that will be used by the code to follow. Their specific usages will become clearer as we examine the code.

ID3D10Device* g_pd3dDevice = NULL;
IDXGISwapChain* g_pSwapChain = NULL;
ID3D10RenderTargetView* g_pRenderTargetView = NULL;

Setting up the Direct3D 10 Device
We start by creating a device and a swap chain. The device provides us with rendering functionality and the ability to create and manage resources. The swap chain's function is to manage the swapping of two or more buffers. In our case, there will be two: front and back. The front buffer (read only) is what is currently being displayed on the screen. The back buffer is where the device will draw to. After the drawing is complete, the buffers are swapped and the content of the then-backbuffer now-frontbuffer is displayed.

NOTE:
As we progress through this tutorial and subsequent tutorials we'll come across many "SOME_TYPE_DESC typeDesc;" objects. There is a common pattern for how resources are outlined and created in Direct3D. A description object is created, and is either filled out manually or by means of a function. Afterwards, the description, in conjunction with related variables are used in a function call to create or bind a resource.

Before we can create and bind the swap chain, we have to fill out a 'description' for it. A detailed explanation of all of the fields can be found in the SDK. Notice the BufferUsage flag is set to DXGI_USAGE_RENDER_TARGET_OUTPUT. This indicates to the application the backbuffer will be used as a target for render output.

// Create a swap chain description and fill with the appropriate values
DXGI_SWAP_CHAIN_DESC sd;
ZeroMemory( &sd, sizeof( sd ) );

// Buffer dimensions, format, and usage
sd.BufferCount = 1;
sd.BufferDesc.Width = width;
sd.BufferDesc.Height = height;
sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
// set the buffer as a target for render output
sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;

// Refresh Rate
sd.BufferDesc.RefreshRate.Numerator = 60;
sd.BufferDesc.RefreshRate.Denominator = 1;

// Multi sampling parameters
sd.SampleDesc.Count = 1;
sd.SampleDesc.Quality = 0;

// application window
sd.OutputWindow = g_hWnd;
sd.Windowed = TRUE;

With the filled out description in hand, let's create the device and swap chain. Here is the function call in isolation. Notice the description (sd) and related variables (g_pSwapChain and g_pd3dDevice) being used in the function call.

D3D10CreateDeviceAndSwapChain(
NULL,
g_driverType,
NULL,
createDeviceFlags,
D3D10_SDK_VERSION,
&sd, // Swap Chain description
&g_pSwapChain, // pointer to the swap chain
&g_pd3dDevice );// pointer to the device

Armed with a device and swap chain, we need a way to enable the device to write to the backbuffer. In DirectX10, the device accesses resources through a 'view'.

In this case, we create a 2D texture resource pointer and point it to the backbuffer using the GetBuffer function call. The CreateRenderTargetView function is called to bind the backbuffer to the newly created RenderTargetView. OmSetRenderTargets is then called to bind the view to the device's pipeline. Now, render output from the pipeline will be sent to the backbuffer.

ID3D10Texture2D* pBackBuffer;
hr = g_pSwapChain->GetBuffer(
0,
// the type of interface used to manipulate the buffer
__uuidof( ID3D10Texture2D ),
// the pointer that will store the address of the backbuffer
( LPVOID* )&pBackBuffer );

if( FAILED( hr ) )
return hr;

hr = g_pd3dDevice->CreateRenderTargetView(
// the pointer to the resource that will serve as the backbuffer
pBackBuffer,
NULL,
// the pointer that will store the address of the RenderTargetView
&g_pRenderTargetView );

pBackBuffer->Release();

if( FAILED( hr ) )
return hr;

g_pd3dDevice->OMSetRenderTargets(
1,
// the RenderTargetView(s) to be bound to the device
&g_pRenderTargetView,
NULL );

Finally, we create and set the viewport.

D3D10_VIEWPORT vp;
vp.Width = width;
vp.Height = height;
vp.MinDepth = 0.0f;
vp.MaxDepth = 1.0f;
vp.TopLeftX = 0;
vp.TopLeftY = 0;
g_pd3dDevice->RSSetViewports( 1, &vp );


Rendering
With the device ready to go, we can now render the scene.

void Render()
{
// Create the color that will be used to fill the backbuffer
float ClearColor[4] = {
0.0f, // RED
0.125f, // GREEN
0.3f, // BLUE
1.0f }; // ALPHA

// Fill the backbuffer with the color
g_pd3dDevice->ClearRenderTargetView(
g_pRenderTargetView,
ClearColor );

// instruct the swapchain to flip the buffers
g_pSwapChain->Present( 0, 0 );
}

Cleanup
We should always cleanup after ourselves! Like we were never there...
This function will set the device to the state it was in when it was first created. In addition any resources being used are released.

void CleanupDevice()
{
if( g_pd3dDevice ) g_pd3dDevice->ClearState();
if( g_pRenderTargetView ) g_pRenderTargetView->Release();
if( g_pSwapChain ) g_pSwapChain->Release();
if( g_pd3dDevice ) g_pd3dDevice->Release();
}

Conclusion:
So there you have it a minimal DirectX 10 application!

I won't be providing an exercise for this Tutorial, but if you feel like fiddling around a little, the fill color for the backbuffer easily tweakable. Check out: http://cloford.com/resources/colours/500col.htm for some nice colors. The values are 0-255 so just divide by 255 to get the 0.0-1.0 value.

I just wanted to mention something about the diagrams and, in general, about writing tutorials based off of other tutorials. The diagrams I draw and the words I write are based off of my understanding and ability to discern what I have read and visualize what is happening. I am fallible, and as such, they may not be correct. If that is the case, please please please let me know. The last thing I want to do is spread incorrect information.

I hope this was helpful to someone! I sure feel like I learned a lot. As always, questions, comments, and criticism are always welcome! Don't be a stranger.

~Elmer

------
DirectX10 SDK Location: DirectX Graphics->Tutorials and Samples ->Tutorials->Basic Tutorials
Source Code Location: (SDK root)\Samples\C++\Direct3D10\Tutorials\Tutorial01

Attachment(s): Tutorial01.cpp
------
The commenting shown in this tutorial is formatted slightly differently than in my own code. Adjustments were made and some were removed to accommodate the blog format.

2 comments:

  1. Thanks for the tutorial. It was helpful in setting up my app.

    ReplyDelete
  2. Well written and layed out tutorial - Thanks.

    ReplyDelete