I'm currently trying to add post processing to an existing C++ DirectX11 application. The task is to apply a dynamic FX shader loaded from a file directly to the backbuffer, all rendering is already done before. In short: I have to get a copy of the backbuffer, apply some post processing effects and overwrite the buffer with those afterwards. "IDXGISwapChain->Present" is called at last.
This was pretty easy to achieve in DirectX9, however I'm running into multiple issues with DirectX11 now. I only found one example for post processing on D3D11 on MSN and it was written for a Windows 8 app, which I cannot build or run on my Windows 7 desktop and the code did not make a lot of sense to me.
The first one, a missing effects framework, could be solved by using "Effects 11" you can download from MSN already.
Now I'm a bit confused on how to continue. The basic idea on how it could work is as following:
- Load the effect from a ".fx" HLSL file and compile it using "D3DX11CompileEffectFromFile"
- Get a copy of the backbuffer using "IDXGISwapChain->GetBuffer" and save it to a "ID3D11Texture" object
- Get the description of the texture, to have width and height of the screen
- Create a new render target and tell the device to use it
- Create a fullscreen quad and draw the screen texture on it (using the loaded effect)
- Reset the render target and update the backbuffer with the drawn quad
The following code for step one and two already works:
[spoiler]
1:
ID3DX11Effect *g_pEffect;
...
HRESULT hr;
ID3DBlob *m_pCompileBuffer;
ID3DInclude *m_pInclude = new CCustomInclude();
// Load effect file
hr = D3DX11CompileEffectFromFile(_T("shader.fx"), NULL, m_pInclude, NULL, NULL, device, &g_pEffect, &m_pCompileBuffer);
if (FAILED(hr))
{
if (m_pCompileBuffer && m_pCompileBuffer->GetBufferSize() > 0)
{
Log(1, "Error while compiling shader:\r\n\r\n%s\r\n", (char*)m_pCompileBuffer->GetBufferPointer());
}
else
{
Log(1, "Error while loading shader");
}
}
2 & 3:
ID3D11Texture2D *g_pScreenTexture;
...
hr = swapchain->GetBuffer(0, __uuidof(g_pScreenTexture), (PVOID*)&g_pScreenTexture);
if (SUCCEEDED(hr))
{
D3D11_TEXTURE2D_DESC m_pDesc;
g_pScreenTexture->GetDesc(&m_pDesc);
}
[/spoiler]
I'm having problems with applying the effect and drawing the fullscreen quad though ...
The vertex struct and layout for the quad is declared as following:
[spoiler]
// Vertex Structure
struct Vertex
{
public:
Vertex() { }
Vertex(float x, float y, float z, float rhw) : pos(x, y, z, rhw) { }
Vertex(float x, float y, float z, float rhw, float tex1, float tex2) : pos(x, y, z, rhw), tex(tex1, tex2) { }
Vertex(float x, float y, float z, float rhw, D3DXVECTOR2 tex) : pos(x, y, z, rhw), tex(tex) { }
Vertex(D3DXVECTOR4 pos, float tex1, float tex2) : pos(pos), tex(tex1, tex2) { }
Vertex(D3DXVECTOR4 pos, D3DXVECTOR2 tex) : pos(pos), tex(tex) { }
Vertex(D3DXVECTOR4 pos) : pos(pos) { }
static const D3D11_INPUT_ELEMENT_DESC layout[];
private:
D3DXVECTOR4 pos;
D3DXVECTOR2 tex;
};
// Vertex Layout
const D3D11_INPUT_ELEMENT_DESC Vertex::layout[] =
{
{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
{ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 16, D3D11_INPUT_PER_VERTEX_DATA, 0 }
};
[/spoiler]
The fullscreen quad is set up like this:
[spoiler]
ID3D11Buffer *g_pVertexBuffer;
...
// Create the vertex buffer
Vertex quad[] =
{
Vertex(D3DXVECTOR4(-1.0f, 1.0f, 0.5f, 1.0f), D3DXVECTOR2(0.0f, 0.0f)),
Vertex(D3DXVECTOR4(1.0f, 1.0f, 0.5f, 1.0f), D3DXVECTOR2(1.0f, 0.0f)),
Vertex(D3DXVECTOR4(-1.0f, -1.0f, 0.5f, 1.0f), D3DXVECTOR2(0.0f, 1.0f)),
Vertex(D3DXVECTOR4(1.0f, -1.0f, 0.5f, 1.0f), D3DXVECTOR2(1.0f, 1.0f))
};
D3D11_BUFFER_DESC m_pVertexDesc = { sizeof(Vertex) * ARRAYSIZE(quad), D3D11_USAGE_DEFAULT, D3D11_BIND_VERTEX_BUFFER, 0, 0 };
D3D11_SUBRESOURCE_DATA m_pVertexData = { quad, 0, 0 };
device->CreateBuffer(&m_pVertexDesc, &m_pVertexData, &g_pVertexBuffer);
[/spoiler]
Drawing currently looks similar to the next code:
[spoiler]
// Update vertex declaration
UINT m_iStrides = sizeof(Vertex);
UINT m_iOffsets = 0;
context->IASetVertexBuffers(0, 1, &g_pVertexBuffer, &m_iStrides, &m_iOffsets);
context->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
// Begin drawing
D3DX11_TECHNIQUE_DESC m_pTechDesc;
g_pEffect->GetTechniqueByIndex(0)->GetDesc(&m_pTechDesc);
for (UINT iPass = 0; iPass < m_pTechDesc.Passes; iPass++)
{
g_pEffect->GetTechniqueByIndex(0)->GetPassByIndex(iPass)->Apply(NULL, context);
context->Draw(4, 0);
}
[/spoiler]
I'm clearing the render target to the color red before drawing the quad, but the screen is red then only, so sadly it doesn't seem to draw the quad at all...
I don't know if there is any way to check that.
Also, the a post processing shader normally requires me to pass the screen texture to it, so it can apply the effects on every pixel of it. The Effects Framework provides the "ID3DX11Effect->GetVariableByName" and allows one to set the variable to a specific object / data, but it has no definition for a texture. The nearest thing I found is "Variable->AsShaderResource()->SetResource(&MyResource)", but is that really the most efficient way, to create a shader resource for the screen texture to pass it to the pixel shader?
I'm sorry for the tons of code in this post, but I found it the easiest way to show what I got already. In DirectX9 the steps described earlier worked without any problems, but it wasn't required to create a vertex buffer here anyway, so the whole thing was shorter and easier to achieve.
I hope somebody has done something similar / post processing before in D3D11/D3D10 and can help me out here, I would really appreciate it.
Thank you and cheers,
Crosire