Tutorial 1 – The DirectX Window

In this tutorial we’ll learn how to set up DirectX to use a window just like those we created in the Win32 tutorials. Most of the code for the main file remains the same. We’ll come to the required changes in a bit but first we’ll create a class to manage all of the DirectX rendering.

Now bear in mind that splitting off the DirectX functionality into it’s own class is just a preference of mine. You can lump it all in with the Win32 code or arrange it any other way you like. I like to implement the code in this way because I feel it is much easier to read and manage applications when they’re divided into self contained modules.

The DirectX Class

Step 1; Add a new class to your project. I’ve called mine DXRenderer as it handles all of the DirectX rendering. The header file will define a couple of simple functions that are needed and a few private member variables:

#include <Windows.h>
#include <d3d10.h>

class DXRenderer
{
  public:
    DXRenderer(void);
    ~DXRenderer(void);
    int Initialise(HWND hwnd, unsigned int width, unsigned int height);
    int Resize(unsigned int width, unsigned int height);
    void Render(void);
  private:
    ID3D10Device *m_pD3DDevice;
    IDXGISwapChain *m_pSwapChain;
    ID3D10RenderTargetView *m_pRenderTargetView;
    unsigned int m_width, m_height;
};

As you can see we include the Windows header file to allow us to use some Windows specific types in this class. We also include the Direct3D10 header which gives us access some of DirectX10’s graphics types and functions. To be able to use this you’ll need to download the latest DirectX SDK.

Those of you who have read through the OpenGL tutorials will notice a striking similarity in the above text. From here the material starts to change a little though as the differences between OpenGL and DirectX appear.

Following the header includes is the definition of the DXRenderer class. It has a public constructor and destructor with no parameters, an initialise function which takes an HWND (more on that in a second!) and some window dimensions, a resize function which also takes window dimensions and a render function.

HWND is short for Handle of a Window, we need this reference to the main window in order to tell DirectX how and where to render it’s graphics.

The private variables are as follows; An ID3D10Device pointer. This is used to perform rendering and create Direct3D resources.

An IDXGISwapChain pointer. This stores rendered data before it is flushed to the screen (or other output device).

An ID3D10RenderTargetView pointer. This is a type of resource used by the Direct3D pipeline. In this case he render target view contains our colour buffer (i.e. the colour values of all the pixels that we will send to the screen).

The only thing that is left is the width and height of the window.

Well now you know what the DXRenderer class looks like, lets see what it does.

DXRenderer::DXRenderer(void) : m_pD3DDevice(NULL),
                               m_pSwapChain(NULL),
                               m_pRenderTargetView(NULL),
                               m_width(1),
                               m_height(1)
{
}

The constructor is fairly dull. For this one I’ve used a slightly different syntax than you’re probably used to called an initialisation list. It’s nothing scary, it basically initialises some of the values used by the class. In the DXRenderer class it sets our Direct3D variables to NULL and initialises the window’s dimensions to a “sensible” value.

Initialisation

In this section we’ll take a look at the initialise function’s implementation:

int DXRenderer::Initialise(HWND hwnd, unsigned int width, unsigned int height)

This function is quite in depth so I’ll be mainly going over what the function is trying to achieve (for full implementation details download the project below).

First off we need to create the core Direct3D objects; the D3D10Device and the swapchain. DirectX provides us with a nice simple function to do both of these at once. All we need to do is set up some structures to describe our output window and then call D3D10CreateDeviceAndSwapChain passing it those structures.

We also need to set up our colour buffer (render target view). For this project I have split this initialisation stage out into the resize function which we’ll take a look at in a little bit.

Once we have successfully created the device, swapchain and render target view we need to choose how the polygons will be rasterized, i.e. how the shapes will be coloured in and rendered. This again involves filling in a structure describing our rasterizer and then creating a rasterstate by calling CreateRasterizerState. We then need to inform our Direct3D device what rasterizer state to use by calling the device’s RSSetState function.

Once this is all done our Direct3D window is ready to render things!

Resize

Now it’s time to look at our class’s resize function which has been called from our initialise function:

int DXRenderer::Resize(unsigned int width, unsigned int height)

This function will resize our colour buffer to match the given dimensions each time it is called. Firstly we have to let go of our old colour buffer by calling release on our render target view variable. Now we can resize our swap chain’s buffers and recreate our render target view to match. Once this is done we need to tell our Direct3D device to use the new render target and resize it’s viewports.

Render

Out render function is much more simple:

void DXRenderer::Render(void)
{
  float pBackgroundColour[4] = { 0.0f, 0.0f, 0.0f, 1.0f };

  m_pD3DDevice->ClearRenderTargetView(m_pRenderTargetView, pBackgroundColour);

  m_pSwapChain->Present(0, 0);
}

This creates an RGBA value and tells our device to fill it’s back buffer with this colour. We then swap the back buffer to the front and our rendering is complete.

Using The DirectX Class

To use the DirectX class we need to include it’s header in the main file and then do some additional coding.

#include "DXRenderer.h"

DXRenderer *g_pDXRenderer(NULL);

Of course we also have to construct our DXRenderer object with the following call:

g_pDXRenderer = new DXRenderer();

Although our DirectX rendering class has been constructed, it still needs to be initialised.

RECT windowRect;
GetClientRect(hwnd, &windowRect);

g_pDXRenderer->Initialise(hwnd, windowRect.right, windowRect.bottom);

In this bit of code we get the dimensions of the inside of our Window and pass it to the DXRenderer object’s initialise function. If that function succeeds then we can carry on with our standard message loop. Of course we still need to add a call to our render function to the code. We’ll put it in the message loop so that the window is continually redrawn:

g_pDXRenderer->Render();

If you follow these instructions then you should get something that looks like this:

It does look rather like the window from the Win32 tutorials but trust me, these are black pixels created by DirectX rather than Win32! Honestly! Try changing the colour value in the render function and see what happens.

Cleanliness Is A Virtue

We’ve created a few things in our code now and it’s important to always clear up after yourself. That’s why we’ll be adding this line to the end of our main code:

delete g_pDXRenderer;

This will call the DXRenderer’s destructor which in turn cleans up the Direct3D objects that we created. For a more in depth look, comments, additional error checking etc. can be found here:

DirectX Tutorial 1

There’s quite a lot of new topics covered in this tutorial (Graphics APIs usually take a bit of setting up!) so take your time to digest it all. The following tutorials should be in slightly more bitesize chunks!

Advertisements
  1. Blake
    August 30, 2013 at 4:38 pm

    Really appreciated this article. It really helped me! Keep up the good work!

  1. No trackbacks yet.

Tell me what you think!

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: