Viewports and Render Targets

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:

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 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, 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 ArraySize

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.

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 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 want to set one render target, thus we set this value to one. Rendering simultaneously to several different 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, 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 an user-interface. But for now, we will just set the viewport to the only one we have created so far:

devCon->RSSetViewports(1, &vp);

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 artifacts.

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: