This tutorial will be the first real introduction to working with DirectX 10.
We will just address two things which is initializing Direct3D and shutting down Direct3D.
We are going to add another class to the framework which will handle all the Direct3D system functions.
We will call the class D3DClass. I have updated the framework diagram below:
As you can see the D3DClass will be located inside the GraphicsClass.
The previous tutorial mentioned that all new graphics related classes will be encapsulated in the GraphicsClass and that is why it is the best location for the new D3DClass.
Now lets take a look at the changes made to the GraphicsClass:
// Filename: graphicsclass.h
Here is the first change. We have taken out the include for windows.h and instead included the new d3dclass.h.
// MY CLASS INCLUDES //
// GLOBALS //
const bool FULL_SCREEN = false;
const bool VSYNC_ENABLED = true;
const float SCREEN_DEPTH = 1000.0f;
const float SCREEN_NEAR = 0.1f;
// Class name: GraphicsClass
bool Initialize(int, int, HWND);
And the second change is the new private pointer to the D3DClass which we have called m_Direct3D.
In case you were wondering I use the prefix m_ on all class variables.
That way when I'm coding I can remember quickly which variables are members of the class and which are not.
If you'll remember from the previous tutorial this class was entirely empty with no code in it at all.
Now that we have a D3DClass member we will start to fill out some code inside the GraphicsClass to initialize and shutdown the D3DClass object.
We will also add calls to BeginScene and EndScene in the Render function so that we are now drawing to the window using Direct3D.
// Filename: graphicsclass.cpp
So the very first change is in the class constructor.
Here we initialize the pointer to null for safety reasons as we do with all class pointers.
m_Direct3D = 0;
GraphicsClass::GraphicsClass(const GraphicsClass& other)
The second change is in the Initialize function inside the GraphicsClass.
Here we create the D3DClass object and then call the D3DClass Initialize function.
We send this function the screen width, screen height, handle to the window, and the four global variables from the Graphicsclass.h file.
The D3DClass will use all these variables to setup the Direct3D system.
We'll go into more detail about that once we look at the d3dclass.cpp file.
bool GraphicsClass::Initialize(int screenWidth, int screenHeight, HWND hwnd)
// Create the Direct3D object.
m_Direct3D = new D3DClass;
// Initialize the Direct3D object.
result = m_Direct3D->Initialize(screenWidth, screenHeight, VSYNC_ENABLED, hwnd, FULL_SCREEN, SCREEN_DEPTH, SCREEN_NEAR);
MessageBox(hwnd, L"Could not initialize Direct3D", L"Error", MB_OK);
The next change is in the Shutdown function in the GraphicsClass.
Shut down of all graphics objects occur here so we have placed the D3DClass shutdown in this function.
Note that I check to see if the pointer was initialized or not. If it wasn't we can assume it was never set up and not try to shut it down.
That is why it is important to set all the pointers to null in the class constructor.
If it does find the pointer has been initialized then it will attempt to shut down the D3DClass and then clean up the pointer afterwards.
// Release the Direct3D object.
m_Direct3D = 0;
The Frame function has been updated so that it now calls the Render function each frame.
// Render the graphics scene.
result = Render();
The final change to this class is in the Render function. We call the m_Direct3D object to clear the screen to a grey color.
After that we call EndScene so that the grey color is presented to the window.
// Clear the buffers to begin the scene.
m_Direct3D->BeginScene(0.5f, 0.5f, 0.5f, 1.0f);
// Present the rendered scene to the screen.
// Filename: d3dclass.h
First thing in the header is to specify the libraries to link when using this object module.
The first library contains all the Direct3D functionality for setting up and drawing 3D graphics in DirectX.
The second library contains tools to interface with the hardware on the computer to obtain information about the refresh rate of the monitor, the video card being used, and so forth.
The third library will be used to compile shaders in later tutorials.
// LINKING //
#pragma comment(lib, "d3d10.lib")
#pragma comment(lib, "dxgi.lib")
#pragma comment(lib, "d3dcompiler.lib")
The next thing we do is include the headers for the libraries that we are directly using in this object module.
Both d3d10.h and directxmath.h should be the only two we ever need here.
// INCLUDES //
using namespace DirectX;
The class definition for the D3DClass is kept as simple as possible here.
It has the regular constructor, copy constructor, and destructor.
Then more importantly it has the Initialize and Shutdown function.
This will be what we are mainly focused on in this tutorial.
Other than that I have a couple helper functions which aren't important to this tutorial and a number of private member variables that will be looked at when we examine the d3dclass.cpp file.
For now just realize the Initialize and Shutdown functions are what concerns us.
// Class name: D3DClass
bool Initialize(int, int, bool, HWND, bool, float, float);
void BeginScene(float, float, float, float);
void GetVideoCardInfo(char*, int&);
For those familiar with Direct3D already you may notice I don't have a view matrix variable in this class.
The reason being is that I will be putting it in a camera class that we will be looking at in future tutorials.
// Filename: d3dclass.cpp
So like most classes we begin with initializing all the member pointers to null in the class constructor.
All seven pointers from the header file have all been accounted for here.
m_device = 0;
m_swapChain = 0;
m_renderTargetView = 0;
m_depthStencilBuffer = 0;
m_depthStencilState = 0;
m_depthStencilView = 0;
m_rasterState = 0;
D3DClass::D3DClass(const D3DClass& other)
The Initialize function is what does the entire setup of Direct3D.
I have placed all the code necessary in here as well as some extra stuff that will facilitate future tutorials.
I could have simplified it and taken out some items but it is probably better to get all of this covered in a single tutorial dedicated to it.
The screenWidth and screenHeight variables that are given to this function are the width and height of the window we created in the SystemClass.
Direct3D will use these to initialize and use the same window dimensions. The hwnd variable is a handle to the window. Direct3D will need this handle to access the window previously created.
The fullscreen variable is whether we are running in windowed mode or fullscreen. Direct3D needs this as well for creating the window with the correct settings.
The screenDepth and screenNear variables are the depth settings for our 3D environment that will be rendered in the window.
The vsync variable indicates if we want Direct3D to render according to the users monitor refresh rate or to just go as fast as possible.
bool D3DClass::Initialize(int screenWidth, int screenHeight, bool vsync, HWND hwnd, bool fullscreen, float screenDepth, float screenNear)
unsigned int numModes, i, numerator, denominator;
unsigned long long stringLength;
float fieldOfView, screenAspect;
// Store the vsync setting.
m_vsync_enabled = vsync;
Before we can initialize Direct3D we have to get the refresh rate from the video card/monitor.
Each computer may be slightly different so we will need to query for that information.
We query for the numerator and denominator values and then pass them to DirectX during the setup and it will calculate the proper refresh rate.
If we don't do this and just set the refresh rate to a default value which may not exist on all computers then DirectX will
respond by performing a blit instead of a buffer flip which will degrade performance and give us annoying errors in the debug output.
// Create a DirectX graphics interface factory.
result = CreateDXGIFactory(__uuidof(IDXGIFactory), (void**)&factory);
// Use the factory to create an adapter for the primary graphics interface (video card).
result = factory->EnumAdapters(0, &adapter);
// Enumerate the primary adapter output (monitor).
result = adapter->EnumOutputs(0, &adapterOutput);
// Get the number of modes that fit the DXGI_FORMAT_R8G8B8A8_UNORM display format for the adapter output (monitor).
result = adapterOutput->GetDisplayModeList(DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_ENUM_MODES_INTERLACED, &numModes, NULL);
// Create a list to hold all the possible display modes for this monitor/video card combination.
displayModeList = new DXGI_MODE_DESC[numModes];
// Now fill the display mode list structures.
result = adapterOutput->GetDisplayModeList(DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_ENUM_MODES_INTERLACED, &numModes, displayModeList);
// Now go through all the display modes and find the one that matches the screen width and height.
// When a match is found store the numerator and denominator of the refresh rate for that monitor.
for(i=0; i<numModes; i++)
if(displayModeList[i].Width == (unsigned int)screenWidth)
if(displayModeList[i].Height == (unsigned int)screenHeight)
numerator = displayModeList[i].RefreshRate.Numerator;
denominator = displayModeList[i].RefreshRate.Denominator;
We now have the numerator and denominator for the refresh rate.
The last thing we will retrieve using the adapter is the name of the video card and the amount of video memory.
// Get the adapter (video card) description.
result = adapter->GetDesc(&adapterDesc);
// Store the dedicated video card memory in megabytes.
m_videoCardMemory = (int)(adapterDesc.DedicatedVideoMemory / 1024 / 1024);
// Convert the name of the video card to a character array and store it.
error = wcstombs_s(&stringLength, m_videoCardDescription, 128, adapterDesc.Description, 128);
if(error != 0)
Now that we have stored the numerator and denominator for the refresh rate and the video card information we can release the structures and interfaces used to get that information.
// Release the display mode list.
delete  displayModeList;
displayModeList = 0;
// Release the adapter output.
adapterOutput = 0;
// Release the adapter.
adapter = 0;
// Release the factory.
factory = 0;
Now that we have the refresh rate from the system we can start the DirectX initialization.
The first thing we'll do is fill out the description of the swap chain.
The swap chain is the front and back buffer to which the graphics will be drawn.
Generally you use a single back buffer, do all your drawing to it, and then swap it to the front buffer which then displays on the user's screen.
That is why it is called a swap chain.
// Initialize the swap chain description.
// Set to a single back buffer.
swapChainDesc.BufferCount = 1;
// Set the width and height of the back buffer.
swapChainDesc.BufferDesc.Width = screenWidth;
swapChainDesc.BufferDesc.Height = screenHeight;
// Set regular 32-bit surface for the back buffer.
swapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
The next part of the description of the swap chain is the refresh rate.
The refresh rate is how many times a second it draws the back buffer to the front buffer.
If vsync is set to true in our graphicsclass.h header then this will lock the refresh rate to the system settings (for example 60hz).
That means it will only draw the screen 60 times a second (or higher if the system refresh rate is more than 60).
However if we set vsync to false then it will draw the screen as many times a second as it can, however this can cause some visual artifacts.
// Set the refresh rate of the back buffer.
swapChainDesc.BufferDesc.RefreshRate.Numerator = numerator;
swapChainDesc.BufferDesc.RefreshRate.Denominator = denominator;
swapChainDesc.BufferDesc.RefreshRate.Numerator = 0;
swapChainDesc.BufferDesc.RefreshRate.Denominator = 1;
// Set the usage of the back buffer.
swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
// Set the handle for the window to render to.
swapChainDesc.OutputWindow = hwnd;
// Turn multisampling off.
swapChainDesc.SampleDesc.Count = 1;
swapChainDesc.SampleDesc.Quality = 0;
// Set to full screen or windowed mode.
swapChainDesc.Windowed = false;
swapChainDesc.Windowed = true;
// Set the scan line ordering and scaling to unspecified.
swapChainDesc.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
swapChainDesc.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
// Discard the back buffer contents after presenting.
swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
// Don't set the advanced flags.
swapChainDesc.Flags = 0;
Now that the swap chain description has been filled out we can create it as well as the Direct3D device.
The Direct3D device is very important, it is our interface to calling all of the Direct3D functions.
We will use the device for almost everything from this point forward.
// Create the swap chain and the Direct3D device.
result = D3D10CreateDeviceAndSwapChain(NULL, D3D10_DRIVER_TYPE_HARDWARE, NULL, 0, D3D10_SDK_VERSION,
&swapChainDesc, &m_swapChain, &m_device);
Now that we have a device and swap chain we need to get a pointer to the back buffer and then attach it to the swap chain.
We'll use the CreateRenderTargetView function to attach the back buffer to our swap chain.
// Get the pointer to the back buffer.
result = m_swapChain->GetBuffer(0, __uuidof(ID3D10Texture2D), (LPVOID*)&backBufferPtr);
// Create the render target view with the back buffer pointer.
result = m_device->CreateRenderTargetView(backBufferPtr, NULL, &m_renderTargetView);
// Release pointer to the back buffer as we no longer need it.
backBufferPtr = 0;
We will also need to set up a depth buffer description.
We'll use this to create a depth buffer so that our polygons can be rendered properly in 3D space.
At the same time we will attach a stencil buffer to our depth buffer.
The stencil buffer can be used to achieve effects such as motion blur, volumetric shadows, and other things.
// Initialize the description of the depth buffer.
// Set up the description of the depth buffer.
depthBufferDesc.Width = screenWidth;
depthBufferDesc.Height = screenHeight;
depthBufferDesc.MipLevels = 1;
depthBufferDesc.ArraySize = 1;
depthBufferDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
depthBufferDesc.SampleDesc.Count = 1;
depthBufferDesc.SampleDesc.Quality = 0;
depthBufferDesc.Usage = D3D10_USAGE_DEFAULT;
depthBufferDesc.BindFlags = D3D10_BIND_DEPTH_STENCIL;
depthBufferDesc.CPUAccessFlags = 0;
depthBufferDesc.MiscFlags = 0;
Now we create the depth/stencil buffer using that description.
You will notice we use the CreateTexture2D function to make the buffers, hence the buffer is just a 2D texture.
The reason for this is that once your polygons are sorted and then rasterized they just end up being colored pixels in this 2D buffer.
Then this 2D buffer is drawn to the screen.
// Create the texture for the depth buffer using the filled out description.
result = m_device->CreateTexture2D(&depthBufferDesc, NULL, &m_depthStencilBuffer);
Now we need to setup the depth stencil description.
This allows us to control what type of depth test Direct3D will do for each pixel.
// Initialize the description of the stencil state.
// Set up the description of the stencil state.
depthStencilDesc.DepthEnable = true;
depthStencilDesc.DepthWriteMask = D3D10_DEPTH_WRITE_MASK_ALL;
depthStencilDesc.DepthFunc = D3D10_COMPARISON_LESS;
depthStencilDesc.StencilEnable = true;
depthStencilDesc.StencilReadMask = 0xFF;
depthStencilDesc.StencilWriteMask = 0xFF;
// Stencil operations if pixel is front-facing.
depthStencilDesc.FrontFace.StencilFailOp = D3D10_STENCIL_OP_KEEP;
depthStencilDesc.FrontFace.StencilDepthFailOp = D3D10_STENCIL_OP_INCR;
depthStencilDesc.FrontFace.StencilPassOp = D3D10_STENCIL_OP_KEEP;
depthStencilDesc.FrontFace.StencilFunc = D3D10_COMPARISON_ALWAYS;
// Stencil operations if pixel is back-facing.
depthStencilDesc.BackFace.StencilFailOp = D3D10_STENCIL_OP_KEEP;
depthStencilDesc.BackFace.StencilDepthFailOp = D3D10_STENCIL_OP_DECR;
depthStencilDesc.BackFace.StencilPassOp = D3D10_STENCIL_OP_KEEP;
depthStencilDesc.BackFace.StencilFunc = D3D10_COMPARISON_ALWAYS;
With the description filled out we can now create a depth stencil state.
// Create the depth stencil state.
result = m_device->CreateDepthStencilState(&depthStencilDesc, &m_depthStencilState);
With the created depth stencil state we can now set it on the Direct3D device so that it takes effect.
// Set the depth stencil state on the D3D device.
The last thing we need to create is the description of the view of the depth stencil buffer.
We do this so that Direct3D knows to use the depth buffer as a depth stencil texture.
After filling out the description we then call the function CreateDepthStencilView via the Direct3D device to create it.
// Initailze the depth stencil view.
// Set up the depth stencil view description.
depthStencilViewDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
depthStencilViewDesc.ViewDimension = D3D10_DSV_DIMENSION_TEXTURE2D;
depthStencilViewDesc.Texture2D.MipSlice = 0;
// Create the depth stencil view.
result = m_device->CreateDepthStencilView(m_depthStencilBuffer, &depthStencilViewDesc, &m_depthStencilView);
With everything finally created we can now call OMSetRenderTargets.
This will bind the render target view and the depth stencil buffer to the output render pipeline.
This way the graphics that the pipeline renders will get drawn to our back buffer that we previously created.
With the graphics written to the back buffer we can then swap it to the front and display our graphics on the user's screen.
// Bind the render target view and depth stencil buffer to the output render pipeline.
m_device->OMSetRenderTargets(1, &m_renderTargetView, m_depthStencilView);
Now that the render targets are setup we can continue on to some extra functions that will give us more control over our scenes for future tutorials.
First thing is we'll create is a rasterizer state. This will give us control over how polygons are rendered.
We can do things like make our scenes render in wireframe mode or have DirectX draw both the front and back faces of polygons.
By default DirectX already has a rasterizer state set up and working the exact same as the one below but you have no control to change it unless you set up one yourself.
// Setup the raster description which will determine how and what polygons will be drawn.
rasterDesc.AntialiasedLineEnable = false;
rasterDesc.CullMode = D3D10_CULL_BACK;
rasterDesc.DepthBias = 0;
rasterDesc.DepthBiasClamp = 0.0f;
rasterDesc.DepthClipEnable = true;
rasterDesc.FillMode = D3D10_FILL_SOLID;
rasterDesc.FrontCounterClockwise = false;
rasterDesc.MultisampleEnable = false;
rasterDesc.ScissorEnable = false;
rasterDesc.SlopeScaledDepthBias = 0.0f;
// Create the rasterizer state from the description we just filled out.
result = m_device->CreateRasterizerState(&rasterDesc, &m_rasterState);
// Now set the rasterizer state.
The viewport also needs to be setup so that Direct3D can map clip space coordinates to the render target space. Set this to be the entire size of the window.
// Setup the viewport for rendering.
viewport.Width = screenWidth;
viewport.Height = screenHeight;
viewport.MinDepth = 0.0f;
viewport.MaxDepth = 1.0f;
viewport.TopLeftX = 0;
viewport.TopLeftY = 0;
// Create the viewport.
Now we will create the projection matrix. The projection matrix is used to translate the 3D scene into the 2D viewport space that we previously created.
We will need to keep a copy of this matrix so that we can pass it to our shaders that will be used to render our scenes.
// Setup the projection matrix.
fieldOfView = 3.141592654f / 4.0f;
screenAspect = (float)screenWidth / (float)screenHeight;
// Create the projection matrix for 3D rendering.
m_projectionMatrix = XMMatrixPerspectiveFovLH(fieldOfView, screenAspect, screenNear, screenDepth);
We will also create another matrix called the world matrix. This matrix is used to convert the vertices of our objects into vertices in the 3D scene.
This matrix will also be used to rotate, translate, and scale our objects in 3D space.
From the start we will just initialize the matrix to the identity matrix and keep a copy of it in this object. The copy will be needed to be passed to the shaders for rendering also.
// Initialize the world matrix to the identity matrix.
m_worldMatrix = XMMatrixIdentity();
This is where you would generally create a view matrix. The view matrix is used to calculate the position of where we are looking at the scene from.
You can think of it as a camera and you only view the scene through this camera.
Because of its purpose I am going to create it in a camera class in later tutorials since logically it fits better there and just skip it for now.
And the final thing we will setup in the Initialize function is an orthographic projection matrix.
This matrix is used for rendering 2D elements like user interfaces on the screen allowing us to skip the 3D rendering.
You will see this used in later tutorials when we look at rendering 2D graphics and fonts to the screen.
// Create an orthographic projection matrix for 2D rendering.
m_orthoMatrix = XMMatrixOrthographicLH((float)screenWidth, (float)screenHeight, screenNear, screenDepth);
The Shutdown function will release and clean up all the pointers used in the Initialize function, its pretty straight forward.
However before doing that I put in a call to force the swap chain to go into windowed mode first before releasing any pointers.
If this is not done and you try to release the swap chain in full screen mode it will throw some exceptions.
So to avoid that happening we just always force windowed mode before shutting down Direct3D.
// Before shutting down set to windowed mode or when you release the swap chain it will throw an exception.
m_rasterState = 0;
m_depthStencilView = 0;
m_depthStencilState = 0;
m_depthStencilBuffer = 0;
m_renderTargetView = 0;
m_swapChain = 0;
m_device = 0;
In the D3DClass I have a couple helper functions.
The first two are BeginScene and EndScene.
BeginScene will be called whenever we are going to draw a new 3D scene at the beginning of each frame.
All it does is initializes the buffers so they are blank and ready to be drawn to.
The other function is Endscene, it tells the swap chain to display our 3D scene once all the drawing has completed at the end of each frame.
void D3DClass::BeginScene(float red, float green, float blue, float alpha)
// Setup the color to clear the buffer to.
color = red;
color = green;
color = blue;
color = alpha;
// Clear the back buffer.
// Clear the depth buffer.
m_device->ClearDepthStencilView(m_depthStencilView, D3D10_CLEAR_DEPTH, 1.0f, 0);
// Present the back buffer to the screen since rendering is complete.
// Lock to screen refresh rate.
// Present as fast as possible.
This next function simply gets a pointer to the Direct3D device.
This is another helper function and it will be called by the framework often.
The next three helper functions give copies of the projection, world, and orthographic matrices to calling functions.
Most shaders will need these matrices for rendering so there needed to be an easy way for outside objects to get a copy of them.
Once again we won't call these functions in this tutorial but I'm just explaining why they are in the code.
void D3DClass::GetProjectionMatrix(XMMATRIX& projectionMatrix)
projectionMatrix = m_projectionMatrix;
void D3DClass::GetWorldMatrix(XMMATRIX& worldMatrix)
worldMatrix = m_worldMatrix;
void D3DClass::GetOrthoMatrix(XMMATRIX& orthoMatrix)
orthoMatrix = m_orthoMatrix;
The last helper function returns by reference the name of the video card.
Knowing the video card name can help in debugging on different configurations.
void D3DClass::GetVideoCardInfo(char* cardName, int& memory)
strcpy_s(cardName, 128, m_videoCardDescription);
memory = m_videoCardMemory;
So now we are finally able to initialize and shut down Direct3D. Compiling and running the code will produce the same window as the last tutorial but Direct3D is initialized now
and it clears the window to a grey color.
Compiling and running the code will also show if your compiler is set up properly and if it can see the headers and libraries files from the Windows SDK.
To Do Exercises
1. Re-compile the code and run the program to ensure DirectX works, if not look at the steps from the first tutorial. Press the escape key to quit after the window displays.
2. Change the global in graphicsclass.h to full screen and re-compile/run.
3. Change the clear color in GraphicsClass::Render to yellow.
4. Write the video card name out to a text file.
Source Code and Data Files: dx10s2tut03_src.zip