There are a dozen views about everything until you know the answer. Then there’s never more than one.
– C.S. Lewis, That Hideous Strength
Now that the swap chain is set up, the GPU must be told where exactly it should draw. More precisely,
a viewport,
for clipping, for example, and
a depth/stencil buffer,
must be defined.
This tutorial will focus on how to initialize the viewport and the depth/stencil buffer, but any etails about how to use
them, will be postponed to later tutorials. As an example though, the stencil buffer could be used to add shadows to 3D
objects or to create motion blur effects.
For now, we simply want to render to the backbuffer.
The Render Target View
When rendering in Direct3D, DirectX must know where exactly to render to. The render target view is a COM object that
maintains a location in video memory to render into. In most cases this will be the back buffer. Here is how the render
target view can be created:
That was rather easy, but we will quickly explain each step:
Using the IDXGISwapChain::GetBuffer function it is possible to obtain a pointer to the current backbuffer in the swap
chain.
The first parameter specifies the index of which backBuffer to use. As we created the swap chain with the swap effect
DXGI_SWAP_EFFECT_FLIP_DISCARD, GetBuffer can only access the zero-th buffer for read and write access - thus, for
our situation, we have to set this to zero.
The second parameter is the interface type of the back buffer which will be a 2D-texture in most cases.
The last parameter returns a pointer to the actual back buffer.
The first parameter specifies the resource the render target is created for.
The second parameter is a pointer to a D3D11_RENDER_TARGET_VIEW_DESC, which, among other things, describes the data
type, or format, of the elements in the specified resource (first parameter). We declared our back buffer to have a
typed format, and thus we can set this parameter to NULL, which tells DirectX to create a view to the first mipmap level
of the specified resource. Do not worry, mipmaps will be covered in a later tutorial, for now, we can safely set this to
NULL.
The third parameter returns a pointer to the created render target view.
The Depth/Stencil Buffer
The depth/stencil buffer basically is a 2D-texture that stores the depth information of the pixels to render. To create
this texture, once again a structure description, namely
an ID3D11_TEXTURE2D_DESC, must be
filled out:
UINT Width and UINT Height
The width and height of the texture in texels.
UINT MipLevels
Mipmaps will be covered in later tutorials. Just know for now that to create a depth/stencil buffer, a two dimensional
texture with a mipmap level of one is necessary.
UINT ArraySize
The number of textures in the texture array. Only one texture is needed for the depth/stencil buffer.
For our depth/stencil buffer we will set this to DXGI_FORMAT_D24_UNORM_S8_UINT. Again, the details will be covered in a
later tutorial. For now, it is enough to know that a structure format which uses 24-bits for the depth buffer and 8-bits
for the stencil buffer is requested.
We have seen this structure before, it is used to specify how multi-sampling or anti-aliasing is done. It should be
clear that those settings for the depth/stencil buffer must match the settings for the render target.
This member identifies how the texture is to be read from and written to. For our depth/stencil buffer we use
D3D11_USAGE_DEFAULT, which tells DirectX that the GPU, and only the GPU, will be reading from and writing to the
resource.
Those flags are various options for how to bind to the different pipeline stages; the pipeline will be covered in later
tutorials. For our depth/stencil buffer, we have to use the D3D11_BIND_DEPTH_STENCIL flag.
These flags specify how the CPU may access the resource. Since in this intended usage scenario, only the GPU needs to
access the texture, we can set this to zero.
To create the texture with the desired specifications, the ID3D11Device::CreateTexture2D method was used:
The first parameter is the texture description.
The second parameter is a pointer to the initial data that the resource should be filled with. Since we are using the
texture as a depth/stencil-buffer, it needs not to be filled with any initial data, and thus this parameter can safely
be set to NULL.
The last parameter returns a pointer to the depth/stencil-buffer.
To finally create the depth/stencil-view, we used the ID3D11Device::CreateDepthStencilView method:
The first parameter is a pointer to the depth/stencil-buffer resource we just created.
The second parameter is a pointer to a
D3D11_DEPTH_STENCIL_VIEW_DESC
structure. Setting this parameter to NULL creates a view that accesses mipmap level 0 of the entire resource using the
format the resource was created with. This is what we wanted.
The last parameter returns the address of a pointer to the created depth/stencil-view.
The first parameter of the function is the number of render targets that we want to set. For now, we only intend to set
one render target, and thus we set this value to one. Rendering simultaneously to several render targets is a rather
advanced technique that we won’t cover until way later.
The second parameter is a pointer to the first element in a list of render target view pointers. Again, we only have one
render target view, and thus we can simply input the address of our render target view interface here.
The third parameter is a pointer to the depth/stencil-view.
Setting the Viewport
To tell DirectX what area of the backbuffer to render into, yet another structure must be filled out, the
D3D11_VIEWPORT description:
The first four floats define the viewport rectangle (relative to the client window rectangle). For now, we will just set
this to the entire client area, since we want to be able to draw on the entire window.
The MinDepth and MaxDepth members specify the minimal and maximal depth buffer values, which will be set to zero and one
for now.
The first parameter is the number of viewports to use, and the second parameter is a pointer to an array of viewports.
For example, we could use multiple viewports to implement a split-screen view like in the good old Nintendo times. In a
later tutorial, we will also see how to use multiple viewports to create a user-interface. But for now, we will just set
the viewport to the only one we have created so far:
Clearing the Back and Depth Buffers
What is left to do now is to clear the back and depth/stencil buffers after each frame so that new scenes can be
rendered without any leftover artefacts.
To clear the back buffer, it is enough to simply fill the entire back buffer with a single colour using
the ID3D11DeviceContext::ClearRenderTargetView
method, which sets each pixel in a render target view to a specified colour:
The first parameter is the address of the render target interface.
The second parameter is an array with four elements, specifying the colour in RGBA format.
These flags identify the type of data to clear. The possible values are D3D11_CLEAR_DEPTH and D3D11_CLEAR_STENCIL.
We will obviously or them.
FLOAT Depth
This specifies the value to override the entire depth buffer with. This value will be clamped between and . We
will use 1.0f (the empty background really is in the background of the scene).
UINT8 Stencil
This specifies the value to override the stencil buffer with. We set this to 0 for now, and we will talk more about this
in a later section.
Here is the code to clear our buffers:
Putting It All Together
Here is the updated Direct3D class:
And its implementation:
The new render method in the derived DirectXGame class now looks like this: