Project Information

  • Language: C++
  • Graphics API: DirectX 11
  • Creation: Y3 S1
  • Source URL: GitHub Page

Advanced Graphics 1

Created as part of my Advanced Graphics and Real-Time Rendering module in semester 1 of my third year. The task was to create a framework using the DirectX graphics API, which would demonstrate a range of advanced graphical techniques. The techniques implemented were deferred rendering, screen space ambient occlusion (SSAO), fast approximate anti-aliasing (FXAA), and motion blur along with a range of advanced texture mapping techniques. The framework was created using the entity-component system and abstraction to allow for the simple creation of new objects and features. The framework also includes a custom editor, ImGui, which allows for the modification of parameters at runtime.

What Did I Learn?

This project was a great opportunity to learn about how the DirectX graphics API can be utilized to implement a range of complex texture mapping and post-processing techniques. I learned a lot about shaders and how they can be used to implement these techniques. I learned how to displace texture coordinates using a height map and the methods of modifying them to achieve the desired normal and parallax effects. I learned how to implement SSAO and how to use the depth buffer to create a shadow map. I learned how to implement motion blur and how to use store pixel values from previous frames and modify them given a velocity and direction based on camera movement to achieve this effect. I learned how to implement FXAA and how to use the luminance of a pixel to determine if it should be blurred and added to the final image.

For deferred rendering, I learned how to use multiple render targets to store the position, normal, and albedo of each pixel in the scene. I learned how to use the depth buffer to determine the depth of each pixel and how to use this to determine the distance of each pixel from the camera. I learned how to use the position and normal of each pixel to determine light values and how to use the albedo to determine the colour of the pixel.

The first step in deferred rendering is to render the scene to the render targets, which is done in the BeginFrameDeferred() function.

void Graphics::BeginFrameDeferred()
{
    // Clear render target/depth stencil
    ID3D11RenderTargetView* renderTargets[] = {
        m_pRenderTargetsDeferred[Bind::RenderTarget::Type::POSITION]->GetRenderTarget(),
        m_pRenderTargetsDeferred[Bind::RenderTarget::Type::ALBEDO]->GetRenderTarget(),
        m_pRenderTargetsDeferred[Bind::RenderTarget::Type::NORMAL]->GetRenderTarget()
    };
    m_pContext->OMSetRenderTargets( BUFFER_COUNT, renderTargets, m_pDepthStencil->GetDepthStencilView() );
    m_pContext->ClearRenderTargetView( m_pRenderTargetsDeferred[Bind::RenderTarget::Type::POSITION]->GetRenderTarget(), m_clearColor );
    m_pContext->ClearRenderTargetView( m_pRenderTargetsDeferred[Bind::RenderTarget::Type::ALBEDO]->GetRenderTarget(), m_clearColor );
    m_pContext->ClearRenderTargetView( m_pRenderTargetsDeferred[Bind::RenderTarget::Type::NORMAL]->GetRenderTarget(), m_clearColor );
    m_pDepthStencil->ClearDepthStencil( m_pContext.Get() );
}
The second step is to render the scene to the back buffer using the render targets as inputs to the shaders.

void Graphics::UpdateRenderStateCube( bool useShadows, bool useDeferred, bool useGBuffer )
{
    // Set default render state for cubes
    m_pRasterizerStates[Bind::Rasterizer::Type::SOLID]->Bind( m_pContext.Get() );
    if ( useShadows )
    {
        Shaders::BindShaders( m_pContext.Get(), m_vertexShaderSDW, m_pixelShaderSDW );
    }
    else if ( useDeferred )
    {
        useGBuffer ?
            Shaders::BindShaders( m_pContext.Get(), m_vertexShaderDR, m_pixelShaderDR ) :
            Shaders::BindShaders( m_pContext.Get(), m_vertexShaderGB, m_pixelShaderGB );
    }
    else
    {
        Shaders::BindShaders( m_pContext.Get(), m_vertexShader, m_pixelShader );
    }
}
When rendering the scene to the render targets, the Bind::RenderTarget::Type enum is used to determine which render target is being used. This is done by passing the render target type to the GetRenderTarget() function of the render target. The render target type is then used to determine which shader is used to render the scene. The Bind::RenderTarget::Type enum is also used to determine which render target is being used in the EndFrame() function. This is done by passing the render target type to the GetShaderResourceView() function of the render target.

Future Additions

In the future, I would like to add a few more features to the framework. I would like to add a water shader to the scene and implement a water shader. I would also like to add a shadow mapping shader, a depth of field shader, a volumetric lighting shader a lens flare shader, screen space reflection and refraction shaders, a screen space subsurface scattering shader, among various other global illumination shaders to the scene. I would also like to spend more time working on implementing a full shadow mapping system which would include a shadow mapping shader, a shadow mapping buffer, and a shadow mapping camera, which would allow for the rendering of shadows in the scene.


Feel free to drop a comment below.