Saturday, September 15, 2018

rendering - Why can't I write to my render targets?


Alright, been working on setting up my first deferred rendering attempt using a light prepass technique in Direct3D 11.


Anywho, I've been having problems understanding and using render targets. Never really had to do much of anything with them in my previous forward rendering approaches. I'm pretty sure I'm correctly initializing them and binding them to the output merger stage however I'm not getting any results from my first geometry pass.



Now, what I'm doing to test my Geometry buffer pass could be the problem itself and the source of my confusion. I've been unclear as to how to choose which render target is presented with the swap chain. I've been working under the assumption that I just need to make sure I have a render target view that was initialized using the swap chain's backbuffer address and that this render target is what gets presented each frame. So based on that I've been binding my "Presentation" render target to the pipeline instead of what I would be using for my Position and/or Normal render buffers. However, I get nothing but an empty black frame so I assume the buffer isn't getting written to properly. (I'm actually overriding the values written to the buffer with a hard coded color in the pixel shader so I don't think it's related to the shader logic doing something funky, however sending the same "presentation" render target through my forward pass shader works fine...)


So my questions essentially are: am I missing a critical concept or failing to properly set up the Texture2D and Render Target View resources? And is my approach in choosing which render target to present correct, or can I directly bind a specific render target to the swap chain? Also my swap chain has a DXGI_FORMAT_R8G8B8A8_UNORM format for the backbuffer, do the other render target views and textures have to match this?


Edit: With Nathan's clarification and debugging tips I was able to solve my buffer issue. It was actually getting written to however, I had a transposed matrix that was mucking up the vertex transformations. The code below works fine if someone reads this that is interested in it.




Providing the relevant code from my project.


Prepare pipeline for the geometry pass. (Pretty sure I've got this correct)


void GeometryBufferPass::StartRendering(RenderTargetView* _positionRT, RenderTargetView* _normalRT,
DepthBuffer* _depthBuffer, RasterizerState* _rasterizerState) {
static ID3D11RenderTargetView* _renderTargets[2];


static float _ClearColor[4] = { 0.0f, 0.0f, 0.0f, 1.0f }; // RGBA
pDeviceManager->GetDeviceContext()->ClearRenderTargetView(_positionRT->GetRenderTargetView(), _ClearColor);
pDeviceManager->GetDeviceContext()->ClearRenderTargetView(_normalRT->GetRenderTargetView(), _ClearColor);
pDeviceManager->GetDeviceContext()->ClearDepthStencilView(_depthBuffer->GetDepthBufferView(), D3D11_CLEAR_DEPTH, 1.0f, 1);

pDeviceManager->GetDeviceContext()->VSSetShader(vShader.GetShaderPtr(), 0, 0);
pDeviceManager->GetDeviceContext()->PSSetShader(pShader.GetShaderPtr(), 0, 0);

_renderTargets[0] = _positionRT->GetRenderTargetView();
_renderTargets[1] = _normalRT->GetRenderTargetView();

pDeviceManager->GetDeviceContext()->OMSetRenderTargets(2, _renderTargets, _depthBuffer->GetDepthBufferView());

pDeviceManager->GetDeviceContext()->IASetInputLayout(pVertexLayout);
pDeviceManager->GetDeviceContext()->IASetPrimitiveTopology(topologyType);

pDeviceManager->GetDeviceContext()->RSSetState(_rasterizerState->GetRasterizerState());

pDeviceManager->GetDeviceContext()->VSSetConstantBuffers(0, 1, transformBuffer.GetConstantBuffer());
pDeviceManager->GetDeviceContext()->PSSetConstantBuffers(1, 1, materialBuffer.GetConstantBuffer());
}


Render each object through the geometry pass.


void GeometryBufferPass::Render(Entity &_entity) {
static XMMATRIX _matrix;
static GeometryBufferPass_TransformBuffer _transformBuffer;
static GeometryBufferPass_MaterialBuffer _materialBuffer;
static unsigned int _offset = 0;

_matrix = XMMatrixMultiply(*_entity.GetInstanceObject()->GetScaleMatrix(), *_entity.GetInstanceObject()->GetRotationMatrix());
_matrix = XMMatrixMultiply(_matrix, *_entity.GetInstanceObject()->GetTranslationMatrix());

_transformBuffer.world = XMMatrixTranspose(_matrix);

_matrix = XMMatrixMultiply(_matrix, viewProjectionMatrix);
_transformBuffer.worldViewProjection = XMMatrixTranspose(_matrix);

transformBuffer.Update(&_transformBuffer);

_materialBuffer.specularExponent = _entity.GetRenderObject()->GetMaterial()->GetSpecularExponent();
materialBuffer.Update(&_materialBuffer);


pDeviceManager->GetDeviceContext()->IASetVertexBuffers(0, 1, _entity.GetRenderObject()->GetVertexBuffer()->GetVertexBuffer(), _entity.GetRenderObject()->GetVertexBuffer()->GetVertexStridePtr(), &_offset);
if (_entity.GetRenderObject()->GetVertexBuffer()->GetIndexBuffer() != 0) {
pDeviceManager->GetDeviceContext()->IASetIndexBuffer(_entity.GetRenderObject()->GetVertexBuffer()->GetIndexBuffer(), DXGI_FORMAT_R32_UINT, 0);
pDeviceManager->GetDeviceContext()->DrawIndexed(_entity.GetRenderObject()->GetVertexBuffer()->GetIndexBufferSize(),0,0);
} else pDeviceManager->GetDeviceContext()->Draw(_entity.GetRenderObject()->GetVertexBuffer()->GetVertexBufferSize(), 0);
}



Here is how I create Texture2D and Render Target View objects.


bool Texture2D::InitializeTexture(const unsigned int _width, const unsigned int _height, const bool _isShaderResourceView, const bool _isRenderTargetView) {

if (!StartUp())
return false;


D3D11_TEXTURE2D_DESC _desc;
_desc.Width = _width;
_desc.Height = _height;
_desc.MipLevels = 0;
_desc.ArraySize = 1;
_desc.Format = DXGI_FORMAT_R32G32B32A32_FLOAT; // or DXGI_FORMAT_R8G8B8A8_UNORM? Even though I am writing 4 floats to it...

_desc.Usage = D3D11_USAGE_DEFAULT;
_desc.CPUAccessFlags = 0;
_desc.MiscFlags = D3D11_RESOURCE_MISC_GENERATE_MIPS; // Not sure if I need to generate a mip map chain for my purposes.

unsigned int _bindFlag = 0;
if (_isShaderResourceView)
_bindFlag |= D3D11_BIND_SHADER_RESOURCE;

if (_isRenderTargetView)
_bindFlag |= D3D11_BIND_RENDER_TARGET;


_desc.BindFlags = _bindFlag;

DXGI_SAMPLE_DESC _sampleDesc;
_sampleDesc.Count = 1;
_sampleDesc.Quality = 0;
_desc.SampleDesc = _sampleDesc;

if (FAILED(pDeviceManager->GetDevice()->CreateTexture2D(&_desc, 0, &pTexture)))
return false;


if (_isRenderTargetView)
if (!renderTarget.StartUp(pTexture, _desc.Format))
return false;

return true;
}

Render Target View initialization


bool RenderTargetView::StartUp(ID3D11Resource* _resource, const DXGI_FORMAT _format, const D3D11_RTV_DIMENSION _viewType) {

pDeviceManager = D11DeviceManager::GetSingleton();

D3D11_RENDER_TARGET_VIEW_DESC _desc;
_desc.Format = _format;
_desc.ViewDimension = _viewType;

// Class only handles Texture2D at the moment.
D3D11_TEX2D_RTV _rtv;
_rtv.MipSlice = 1;
_desc.Texture2D = _rtv;


return !FAILED(pDeviceManager->GetDevice()->CreateRenderTargetView(_resource, &_desc, &pRenderTargetView));
}



And figured I might as well post the shader code just in case.


cbuffer TransformBuffer : register(b0) {
float4x4 worldMatrix,
worldViewProjectionMatrix;
};


// Pretty sure constant buffers have to be atleast 16 bytes
// but shader compiler has yet to throw a warning or error.
cbuffer MaterialBuffer : register(b1) {
float materialSpecularExponent;
};

struct vsInput {
float3 positionLS : POSITION;
float3 textureLS : TEXTURE;

float3 normalLS : NORMAL;
};

struct vsOutput {
float4 positionCS : SV_POSITION;
float2 textureLS : TEXTURE;
float3 normalWS : NORMAL;
float3 positionWS : POSITION;
};


struct psOutput {
float4 normalWS : SV_TARGET0;
float4 positionWS : SV_TARGET1;
};

vsOutput VS( in const vsInput _in ) {
vsOutput _out;

_out.positionCS = float4(mul(_in.positionLS, (float3x3) worldViewProjectionMatrix), 1.0f);
_out.positionWS = mul(_in.positionLS, (float3x3) worldMatrix);

_out.normalWS = mul(_in.normalLS, (float3x3) worldMatrix);
_out.textureLS = (float2)_in.textureLS;

return _out;
}

psOutput PS( in const vsOutput _in ) {
psOutput _out;

// Overwriting the pixel shader return values just so I can see if the render

// target is getting written to.
_out.normalWS = float4(0.5f, 1.0f, 0.0f, 1.0f);//float4(normalize(_in.normalWS), materialSpecularExponent);
_out.positionWS = float4(0.5f, 1.0f, 0.0f, 1.0f);//float4(_in.positionWS, 1.0f);

return _out;
}

Answer



You don't choose which render target to present. The IDXGISwapChain object owns the back buffer (the render target that gets presented); it's automatically created when the swap chain is set up. To draw to the back buffer, you first get a reference to it as a texture by calling IDXGISwapChain::GetBuffer; then you set up a RenderTargetView from that.


In order to display another render target on the screen, you can use ID3D11DeviceContext::CopyResource to copy from the other render target to the back buffer. That only works if they're exactly the same size and format; in other cases, I believe you have to copy the image by drawing a full-screen quad with a simple pixel shader that samples your image and returns it.


Other render targets and textures do not need to have the same format as the back buffer; they can have any supported format you want.



No comments:

Post a Comment

Simple past, Present perfect Past perfect

Can you tell me which form of the following sentences is the correct one please? Imagine two friends discussing the gym... I was in a good s...