Rendering a full-screen triangle for post-processing

In this example we change the colours of the rendered scene to grayscale. First we need a shader that samples a texture and convert the pixel's colour to a grayscale value:

const char* const PostFxGpuProgram =
"Texture2D texture0 : register(t0);\n"
"SamplerState samplerLinear : register(s0);\n"
"struct VertexIn\n"
"{\n"
"    float2 uv : TEXCOORD0;\n"
"    float3 normal : NORMAL0;\n"
"    float3 position : POSITION0;\n"
"    float4 colour : COLOR0;\n"
"};\n"
"struct VertexOut\n"
"{\n"
"    float2 uv : TEXCOORD;\n"
"    float4 position : SV_Position;\n"
"    float4 colour : COLOR;\n"
"};\n"
"void DefaultVertexShader(VertexIn input, out VertexOut output)\n"
"{\n"
"    output.uv = input.uv;\n"
"    output.position = float4(input.position, 1.0f);\n"
"    output.colour = input.colour;\n"
"}\n"
"void DefaultFragmentShader(VertexOut input, out float4 output : SV_Target)\n"
"{\n"
"    float4 colour = texture0.Sample(samplerLinear, float2(input.uv.x, 1.0f - input.uv.y));\n"
"    float grey = 0.299f * colour.r + 0.587f * colour.g + 0.114f * colour.b;\n"
"    output = float4(grey, grey, grey, 1.0f);\n"
"};\n";

Next we setup the texture we going to render our scene to:

RenderTexture* scene_rt = new RenderTexture();
scene_rt->Create(window->GetWidth(), window->GetHeight(), PF_FLOAT32_RGBA, true);
Viewport* vp = scene_rt->AddViewport(camera, 0, 0, window->GetWidth(), window->GetHeight());
vp->SetBackgroundColour(0xff242424);

Then we'll have to create the pass (material) the full-screen triangle is rendered with:

Pass* postfx_pass = new Pass(0U);
TextureUnitState* tus = postfx_pass->CreateTextureUnitState();
Texture* rt_texture = new Texture(scene_rt);
tus->SetTexture(rt_texture);
GpuProgram* postfx_gpu_program = GpuProgramManager::Instance()->LoadFromSource("postfx.fx", PostFxGpuProgram, "DefaultVertexShader", "DefaultFragmentShader");
postfx_pass->SetGpuProgram(postfx_gpu_program);

The last step of the initialisation is to create the triangle mesh:

chrissly::graphics::Vertex vtx_data[3U] =
{
    {0.0f, 0.0f, 0xffffffff, 0.0f, 0.0f, 1.0f, -1.0f , -1.0f, 0.0f},
    {2.0f, 0.0f, 0xffffffff, 0.0f, 0.0f, 1.0f,  3.0f , -1.0f, 0.0f},
    {0.0f, 2.0f, 0xffffffff, 0.0f, 0.0f, 1.0f,  -1.0f,  3.0f, 0.0f}
};
HardwareVertexBuffer* vertex_buffer = CE_NEW HardwareVertexBuffer(3U, 36U, HBU_STATIC, false);
void* buffer = vertex_buffer->Map();
memcpy(buffer, vtx_data, sizeof(vtx_data));
vertex_buffer->Unmap();
Mesh* mesh = MeshManager::Instance()->CreateManual("fs_triangle_mesh");
SubMesh* sub_mesh = mesh->CreateSubMesh();
sub_mesh->vertexData = CE_NEW VertexData(vertex_buffer);
sub_mesh->topology = PT_TRIANGLELIST;
Entity* fs_triangle = SceneManager::Instance()->CreateEntity("fs_triangle_mesh");

In our renderloop GraphicsSystem::RenderOneFrame() will be replaced with the following code:

// render scene to texture
scene_rt->Update();
// switch to main render window
RenderSystem::Instance()->SetRenderTarget(window);
RenderSystem::Instance()->SetViewport(window->GetViewport(0U));
// set pass that applies posteffect and render full-screen triangle
RenderSystem::Instance()->SetPass(postfx_pass);
RenderSystem::Instance()->Render(fs_triangle->GetSubEntity(0U));
window->UpdateFrameTime();
window->SwapBuffers();
// manually raise frame number to update animations
GraphicsSystem::Instance()->RaiseFrameNumber();