Note: Code linked in the post will change as I change the files on my computer and Dropbox updates them. :)
Hello everyone! I am creating my very first deferred renderer (.h | .cpp). It has been exciting so far even though I just got done with Directional Lighting and am now trying to get Point Lights working. I am also using this guide as reference. Before I talk about the problems, I have a few questions:
G-Buffer Formats:
I have 4 render targets (color, normal, depth, lighting). Here's the formats I am using:
//Color
RenderTargets.push_back(NXRenderTarget(DXGI_FORMAT_R16G16B16A16_UNORM, NXGPU::Context->ViewportWidth, NXGPU::Context->ViewportHeight));
//Normal
RenderTargets.push_back(NXRenderTarget(DXGI_FORMAT_R16G16B16A16_UNORM, NXGPU::Context->ViewportWidth, NXGPU::Context->ViewportHeight));
//Depth
RenderTargets.push_back(NXRenderTarget(DXGI_FORMAT_R32_FLOAT, NXGPU::Context->ViewportWidth, NXGPU::Context->ViewportHeight));
//Lighting Buffer
RenderTargets.push_back(NXRenderTarget(DXGI_FORMAT_R16G16B16A16_UNORM, NXGPU::Context->ViewportWidth, NXGPU::Context->ViewportHeight));
Are those good formats to be using for the G-Buffer? Also, I know this question may be stupid, but I'm a little confused about R32_FLOAT. So I know the format contains one 32-bit floating-point red channel. But in examples I've seen on the internet, when using this format, and drawing it to a quad, they get a White/Black depth buffer image. When I render it to a quad, I see a Red/Black depth buffer image. Should the depth buffer always be white/black? Here are a few images of the output that I get from all three targets (from the left side of a cube).
Color:
Normal:
Depth:
Point Light Problems:
For my engine I created a light manager (.h | .cpp) to handle lighting. Implementing Directional Lighting was a breeze and worked the first time I tried it as you can see below:
Directional Light (1, 0, 0)
Directional Light (1, 0, 1)
As you can see, Directional Lights seem to work fine. But point lights are weird. First, I can't seem to get a smooth sphere rendered (created in 3DS Max with a radius of 1.0 and 32 segments):
3DS Max:
My Engine (in forward renderer):
As you can see the sphere is not smooth. When using this sphere as a point light with Deferred Rendering, I get weird results. It took me a long time to get to the point where anything is even lit. I experimented for several days and finally got to this point, but something is still wrong. It's driving me crazy.
Point Light | Position: (10, 0, -8), Radius: 50, Intensity: 100
Point Light | Position (10, 0, -8), Radius: 100, Intensity: 1000
As you can see, something is certainly wrong. If I set the intensity lower than 100 (like 4 as shown in the tutorials), nothing is visible. If the radius is too small or big, nothing is shown. I tried moving it to different positions, disabling/enabling Z-Buffer, changing blend states, and I get the same results, sometimes nothing visible after the changes. I didn't even set my color to green. It's supposed to be white. This is the Point Light shader I am using:
Pixel Shader:
cbuffer LightDataBuffer : register(b0)
{
float gLightRadius;
float gLightIntensity;
float2 gHalfPixel;
float3 gLightColor;
float3 gEyePosition;
float3 gLightPosition;
float4x4 gInverseVP;
}
Texture2D colorMap : register(t0);
Texture2D normalMap : register(t1);
Texture2D depthMap : register(t2);
SamplerState colorSampler
{
AddressU = CLAMP;
AddressV = CLAMP;
MagFilter = LINEAR;
MinFilter = LINEAR;
Mipfilter = LINEAR;
};
SamplerState normalSampler
{
AddressU = CLAMP;
AddressV = CLAMP;
MagFilter = POINT;
MinFilter = POINT;
Mipfilter = POINT;
};
struct VertexShaderOutput
{
float4 Position : POSITION0;
float4 ScreenPosition : TEXCOORD0;
};
float4 main(VertexShaderOutput input) : SV_TARGET
{
input.ScreenPosition.xy /= input.ScreenPosition.w;
float2 texCoord = 0.5f * (float2(input.ScreenPosition.x, -input.ScreenPosition.y) + 1);
texCoord -= gHalfPixel;
float4 normalData = normalMap.Sample(normalSampler, texCoord);
float3 normal = 2.0f * normalData.xyz - 1.0f;
float specularPower = normalData.a * 255;
float specularIntensity = colorMap.Sample(colorSampler, texCoord).a;
float depthVal = depthMap.Sample(normalSampler, texCoord).r;
float4 position;
position.xy = input.ScreenPosition.xy;
position.z = depthVal;
position.w = 1.0f;
position = mul(position, gInverseVP);
position /= position.w;
float3 lightVector = gLightPosition - position;
float attenuation = saturate(1.0f - length(lightVector) / gLightRadius);
lightVector = normalize(lightVector);
float NdL = max(0, dot(normal, lightVector));
float3 diffuseLight = NdL * gLightColor.rgb;
float3 reflectionVector = normalize(reflect(-lightVector, normal));
float3 directionToCamera = normalize(gEyePosition - position.rgb);
float specularLight = specularIntensity * pow(saturate(dot(reflectionVector, directionToCamera)), specularPower);
return attenuation * gLightIntensity * float4(diffuseLight.rgb, specularLight);
}
Vertex Shader:
cbuffer MatrixBuffer : register(b0)
{
float4x4 world;
float4x4 view;
float4x4 projection;
}
struct VertexShaderInput
{
float3 Position : POSITION0;
};
struct VertexShaderOutput
{
float4 Position : SV_POSITION;
float4 ScreenPosition : TEXCOORD0;
};
VertexShaderOutput main(VertexShaderInput input)
{
VertexShaderOutput output;
float4 worldPosition = mul(float4(input.Position, 1), world);
float4 viewPosition = mul(worldPosition, view);
output.Position = mul(viewPosition, projection);
output.ScreenPosition = output.Position;
return output;
}
Point Light Shader class:
~Header:
class NXPointLightShader : public NXShader
{
public:
struct LightDataBuffer
{
float Radius;
float Intensity;
XMVECTOR HalfPixel;
XMVECTOR Color;
XMVECTOR CameraPosition;
XMVECTOR LightPosition;
XMMATRIX InverseVP;
};
NXPointLightShader();
bool SetVSConstants(NXCamera3D& camera, XMMATRIX& world);
bool SetPSConstants(NXCamera3D& camera, XMVECTOR& position, XMVECTOR& color, float radius, float intensity);
private:
D3D11_MAPPED_SUBRESOURCE mappedResource;
ID3D11Buffer* matrixBuffer;
bool CreateMatrixBuffer();
void DisposeShader();
};
~Implementation:
NXPointLightShader::NXPointLightShader() : NXShader(L"PointLight", "../Debug/PointLightVS.cso", "../Debug/PointLightPS.cso") { if (!CreateMatrixBuffer()){ return; } }
bool NXPointLightShader::SetVSConstants(NXCamera3D& camera, XMMATRIX& world)
{
TransformConstantBuffer* dataPtr = nullptr;
XMMATRIX proj, view;
camera.GetViewMatrix(view);
NXGPU::Context->GetProjectionMatrix(proj);
world = XMMatrixTranspose(world);
view = XMMatrixTranspose(view);
proj = XMMatrixTranspose(proj);
if (FAILED(NXGPU::Map(matrixBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource)))
return false;
dataPtr = (TransformConstantBuffer*) mappedResource.pData;
dataPtr->World = world;
dataPtr->View = view;
dataPtr->Projection = proj;
NXGPU::Unmap(matrixBuffer, 0);
NXGPU::SetVSConstantBuffers(0, 1, &matrixBuffer);
return true;
}
bool NXPointLightShader::SetPSConstants(NXCamera3D& camera, XMVECTOR& position, XMVECTOR& color, float radius, float intensity)
{
LightDataBuffer* dataPtr = nullptr;
XMMATRIX proj, view;
camera.GetViewMatrix(view);
NXGPU::Context->GetProjectionMatrix(proj);
XMFLOAT2 halfPixel;
halfPixel.x = 0.5f / (float) NXGPU::Context->ViewportWidth;
halfPixel.y = 0.5f / (float) NXGPU::Context->ViewportHeight;
if (FAILED(NXGPU::Map(ConstantBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource)))
return false;
dataPtr = (LightDataBuffer*) mappedResource.pData;
dataPtr->Radius = radius;
dataPtr->Intensity = intensity;
dataPtr->HalfPixel = XMLoadFloat2(&halfPixel);
dataPtr->LightPosition = position;
dataPtr->Color = color;
dataPtr->CameraPosition = camera.Position;
dataPtr->InverseVP = XMMatrixInverse(nullptr, view * proj);
NXGPU::Unmap(ConstantBuffer, 0);
NXGPU::SetPSConstantBuffers(0, 1, &ConstantBuffer);
ID3D11ShaderResourceView* textures[3] = { NXRenderer::Technique->RenderTargets[0].Texture->Texture, NXRenderer::Technique->RenderTargets[1].Texture->Texture, NXRenderer::Technique->RenderTargets[2].Texture->Texture };
NXGPU::GetDeviceContext()->PSSetShaderResources(0, 3, textures);
return true;
}
bool NXPointLightShader::CreateMatrixBuffer()
{
D3D11_BUFFER_DESC lightDataDesc;
lightDataDesc.Usage = D3D11_USAGE_DYNAMIC;
lightDataDesc.ByteWidth = sizeof(LightDataBuffer);
lightDataDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
lightDataDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
lightDataDesc.MiscFlags = 0;
lightDataDesc.StructureByteStride = 0;
if (FAILED(NXGPU::GetDevice()->CreateBuffer(&lightDataDesc, NULL, &ConstantBuffer)))
return false;
D3D11_BUFFER_DESC matrixDataDesc;
matrixDataDesc.Usage = D3D11_USAGE_DYNAMIC;
matrixDataDesc.ByteWidth = sizeof(TransformConstantBuffer);
matrixDataDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
matrixDataDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
matrixDataDesc.MiscFlags = 0;
matrixDataDesc.StructureByteStride = 0;
if (FAILED(NXGPU::GetDevice()->CreateBuffer(&matrixDataDesc, NULL, &matrixBuffer)))
return false;
return true;
}
void NXPointLightShader::DisposeShader(){ if (matrixBuffer){ matrixBuffer->Release(); matrixBuffer = nullptr; } }
I've tried transposing world, view, and projection which helped get me to where I am now. Here is the full NXShader file (sets up shaders: .h | .cpp). It doesn't seem to be working correctly at all and it's making frustrated. Any help? Also, how does my deferred rendering code look so far? Any other problems? Thanks!