Quad rendering

In order for our engine to work, we need to render the RenderTargets to a full-screen quad. I have seen various implementations of this, but I liked the Cansin’s implementation the best. I’ve slightly altered it, but it is based on his implementation. Add a new class to the “Renderers” folder called “QuadRenderer”. Once the class is created, add the using directives as follows:

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

Now we can use these namespaces in our class. Let us begin by adding some variables:

GraphicsDevice device;
VertexBuffer vertexBuffer;
IndexBuffer indexBuffer;

Now we need to create a constructor. In the constructor, we’ll create the vertices and indices that will be used by our VertexBuffer and IndexBuffer.

public QuadRenderer(GraphicsDevice device)
{
    this.device = device;

    // Create the Vertices for our Quad
    VertexPositionTexture[] vertices =
    {
        new VertexPositionTexture(new Vector3(1, -1, 0), new Vector2(1, 1)),
        new VertexPositionTexture(new Vector3(-1, -1, 0), new Vector2(0, 1)),
        new VertexPositionTexture(new Vector3(-1, 1, 0), new Vector2(0, 0)),
        new VertexPositionTexture(new Vector3(1, 1, 0), new Vector2(1, 0))
    };

    // Create the VertexBuffer
    vertexBuffer = new VertexBuffer(device, VertexPositionTexture.VertexDeclaration, vertices.Length, BufferUsage.None);
    vertexBuffer.SetData<VertexPositionTexture>(vertices);

    // Create the Indices
    ushort[] indices = { 0, 1, 2, 2, 3, 0 };

    // Create the IndexBuffer
    indexBuffer = new IndexBuffer(device, IndexElementSize.SixteenBits, indices.Length, BufferUsage.None);
    indexBuffer.SetData<ushort>(indices);
}

As you can see the code is creating the vertices and indices and assigning them to the Vertex and Index Buffers. Pretty straight forward. Let’s add a Draw method so we can use the class from our “DeferredRenderer” class:

public void Draw()
{
    // Set VertexBuffer
    device.SetVertexBuffer(vertexBuffer);

    // Set IndexBuffer
    device.Indices = indexBuffer;

    // Draw Quad
    device.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, 4, 0, 2);
}

That is all we need to do for the time being. If you have seen the Cansin’s version of this code, you’ll know that he has 2 more methods to do with assigning the buffers and another to just draw the quad. We’ll come back to these.

Back in our “DeferredRenderer” class, create a new variable:

private QuadRenderer fullscreenQuad;

We need to instantiate this new object, so add the following to the constructor after everything else:

fullscreenQuad = new QuadRenderer(device);

Excellent. Building the solution will throw no errors and also running the application will still produce the same results as the previous post, so let’s do something about it. Locate your ClearGBuffer method and add the following code after the Effect apply:

fullscreenQuad.Draw();

If you were too eager and have tried to build and run the project, you’ll still be seeing the same results as before. We need to add in one more Draw call, and this one goes in the CombineGBuffer method. Find your CombineGBuffer method and add in the same code as above after the Effect apply call. The code should look like:

void CombineGBuffer()
{
    finalEffect.Parameters["colorMap"].SetValue(colorRT);
    finalEffect.Parameters["lightMap"].SetValue(lightRT);
    finalEffect.Parameters["halfPixel"].SetValue(halfPixel);
    finalEffect.CurrentTechnique.Passes[0].Apply();
    fullscreenQuad.Draw();
}

Now, build and run the application. You should now be seeing a Black window.

This is because we are now combining the RenderTargets and applying them to the Quad. In the next episode, we’ll add in a new Debug step so we can see the output of the different RenderTargets as they are being rendered at the various steps within the engine.