I'm having some issues getting shadowmapping to work for point lights. I've done this in OpenGL4, and the shader code seems to translate pretty much 1-to-1. From debugging the depth stencil views after the shadowpass in Visual Studio, it seems the depth textures are simply empty.
Here's the overall point light pass:
void DX11PointLightPass::Render(ID3D11DeviceContext* context, const RenderQueue& renderQueue, std::vector<DX11MeshPtr>& meshes, ID3D11DepthStencilView* gbufferDSV, const RenderableLighting::PointLight& pointLight)
{
// preserve current state
ID3D11RasterizerStatePtr prevRasterizerState = nullptr;
ID3D11DepthStencilStatePtr prevDepthStencilState = nullptr;
D3D11_VIEWPORT prevViewport;
uint32_t numViewports = 1;
context->RSGetState(&prevRasterizerState);
context->OMGetDepthStencilState(&prevDepthStencilState, 0);
context->RSGetViewports(&numViewports, &prevViewport);
//
// shadow pass
//
context->VSSetShader(mNullVertexShader, NULL, NULL);
context->PSSetShader(NULL, NULL, NULL);
context->OMSetDepthStencilState(NULL, 0); // defaults to depth rendering/testing
context->RSSetState(mRSCullFront);
context->RSSetViewports(1, &mShadowPassViewport);
for (uint32_t face = 0; face < TEXTURE_CUBE_NUM_FACES; face++)
{
context->OMSetRenderTargets(0, NULL, mShadowmapView[face]);
context->ClearDepthStencilView(mShadowmapView[face], D3D11_CLEAR_DEPTH, 1.0f, 0);
Mat4 lightViewMatrix = glm::lookAt(pointLight.mLightPosition, pointLight.mLightPosition + CUBEMAP_DIRECTION_VECTORS[face], CUBEMAP_UP_VECTORS[face]);
Mat4 lightProjMatrix = PerspectiveMatrixFov(90.0f, 1.0f, Z_NEAR, Z_FAR);
DepthPass(context, renderQueue, meshes, lightProjMatrix * lightViewMatrix); // draws all geometry using light view-projection matrix
}
//
// stencil pass
//
// restore rendering to the backbuffer
mBackbuffer.BindForShadingStage(context, gbufferDSV); // refactor the call?
context->OMSetDepthStencilState(mDSSStencilPass, 0);
context->RSSetState(mRSNoCulling);
// restore screen viewport
context->RSSetViewports(1, &prevViewport);
mNullCBuffer.SetData(NullCBuffer(pointLight.mWVPMatrix), context, 0);
mSphereMesh.Draw(context);
//
// shading pass
//
context->OMSetDepthStencilState(mDSSShadingPass, 0);
context->RSSetState(mRSCullFront);
context->PSSetShaderResources(DX11Texture::SHADER_TEXTURE_SLOT_DEPTH, 1, &mShadowmapSRV.p);
context->VSSetShader(mShadingVertexShader, NULL, NULL);
context->PSSetShader(mPixelShader, NULL, NULL);
mPointLightCBuffer.SetData(PointLightCBuffer(pointLight.mWVPMatrix, pointLight.mLightColor, Vec4(pointLight.mLightPosition, 1.0), pointLight.mLightIntensity, pointLight.mMaxDistance), context, 0);
mSphereMesh.Draw(context);
// restore state
context->PSSetShaderResources(DX11Texture::SHADER_TEXTURE_SLOT_DEPTH, 1, &gNullSrv);
context->OMSetDepthStencilState(prevDepthStencilState, 0);
context->RSSetState(prevRasterizerState);
}
Some of the constants used:
const uint32_t TEXTURE_CUBE_NUM_FACES = 6;
const Vec3 CUBEMAP_DIRECTION_VECTORS[TEXTURE_CUBE_NUM_FACES] = { Vec3(1.0f, 0.0f, 0.0f), Vec3(-1.0f, 0.0f, 0.0f), Vec3(0.0f, 1.0f, 0.0f),
Vec3(0.0f, -1.0f, 0.0f), Vec3(0.0f, 0.0f, 1.0f), Vec3(0.0f, 0.0f, -1.0f) };
const Vec3 CUBEMAP_UP_VECTORS[TEXTURE_CUBE_NUM_FACES] = { Vec3(0.0f, -1.0f, 0.0f), Vec3(0.0f, -1.0f, 0.0f), Vec3(0.0f, 0.0f, -1.0f),
Vec3(0.0f, 0.0f, -1.0f), Vec3(0.0f, -1.0f, 0.0f), Vec3(0.0f, -1.0f, 0.0f) };
Here's the overall setup for the d3d resources:
// rasterize for front-face culling due to light volumes
D3D11_RASTERIZER_DESC rasterizerDesc;
ZeroMemory(&rasterizerDesc, sizeof(D3D11_RASTERIZER_DESC));
rasterizerDesc.FillMode = D3D11_FILL_SOLID;
rasterizerDesc.CullMode = D3D11_CULL_FRONT;
rasterizerDesc.FrontCounterClockwise = true;
rasterizerDesc.DepthClipEnable = true;
rasterizerDesc.ScissorEnable = false;
rasterizerDesc.MultisampleEnable = false;
rasterizerDesc.AntialiasedLineEnable = false;
DXCALL(device->CreateRasterizerState(&rasterizerDesc, &mRSCullFront));
// rasterize with no culling for stencil pass
ZeroMemory(&rasterizerDesc, sizeof(D3D11_RASTERIZER_DESC));
rasterizerDesc.FillMode = D3D11_FILL_SOLID;
rasterizerDesc.CullMode = D3D11_CULL_NONE;
rasterizerDesc.FrontCounterClockwise = true;
rasterizerDesc.DepthClipEnable = true;
rasterizerDesc.ScissorEnable = false;
rasterizerDesc.MultisampleEnable = false;
rasterizerDesc.AntialiasedLineEnable = false;
DXCALL(device->CreateRasterizerState(&rasterizerDesc, &mRSNoCulling));
// DSS stencil pass
D3D11_DEPTH_STENCIL_DESC depthStencilDesc;
ZeroMemory(&depthStencilDesc, sizeof(D3D11_DEPTH_STENCIL_DESC));
depthStencilDesc.DepthEnable = true;
depthStencilDesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ZERO;
depthStencilDesc.DepthFunc = D3D11_COMPARISON_LESS;
depthStencilDesc.StencilEnable = true;
depthStencilDesc.StencilReadMask = 0;
depthStencilDesc.StencilWriteMask = 0;
depthStencilDesc.FrontFace.StencilFailOp = D3D11_STENCIL_OP_KEEP;
depthStencilDesc.FrontFace.StencilDepthFailOp = D3D11_STENCIL_OP_INVERT;
depthStencilDesc.FrontFace.StencilPassOp = D3D11_STENCIL_OP_KEEP;
depthStencilDesc.FrontFace.StencilFunc = D3D11_COMPARISON_ALWAYS;
depthStencilDesc.BackFace.StencilFailOp = D3D11_STENCIL_OP_KEEP;
depthStencilDesc.BackFace.StencilDepthFailOp = D3D11_STENCIL_OP_INVERT;
depthStencilDesc.BackFace.StencilPassOp = D3D11_STENCIL_OP_KEEP;
depthStencilDesc.BackFace.StencilFunc = D3D11_COMPARISON_ALWAYS;
DXCALL(device->CreateDepthStencilState(&depthStencilDesc, &mDSSStencilPass));
// DSS shading pass
ZeroMemory(&depthStencilDesc, sizeof(D3D11_DEPTH_STENCIL_DESC));
depthStencilDesc.DepthEnable = false;
depthStencilDesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ZERO;
depthStencilDesc.DepthFunc = D3D11_COMPARISON_ALWAYS;
depthStencilDesc.StencilEnable = true;
depthStencilDesc.StencilReadMask = 0xFF;
depthStencilDesc.StencilWriteMask = 0xFF;
depthStencilDesc.FrontFace.StencilFailOp = D3D11_STENCIL_OP_KEEP;
depthStencilDesc.FrontFace.StencilDepthFailOp = D3D11_STENCIL_OP_KEEP;
depthStencilDesc.FrontFace.StencilPassOp = D3D11_STENCIL_OP_KEEP;
depthStencilDesc.FrontFace.StencilFunc = D3D11_COMPARISON_EQUAL;
depthStencilDesc.BackFace.StencilFailOp = D3D11_STENCIL_OP_KEEP;
depthStencilDesc.BackFace.StencilDepthFailOp = D3D11_STENCIL_OP_KEEP;
depthStencilDesc.BackFace.StencilPassOp = D3D11_STENCIL_OP_KEEP;
depthStencilDesc.BackFace.StencilFunc = D3D11_COMPARISON_EQUAL;
DXCALL(device->CreateDepthStencilState(&depthStencilDesc, &mDSSShadingPass));
// create shadowmap texture/view/srv
D3D11_TEXTURE2D_DESC depthBufferDesc;
ZeroMemory(&depthBufferDesc, sizeof(D3D11_TEXTURE2D_DESC));
depthBufferDesc.ArraySize = TEXTURE_CUBE_NUM_FACES;
depthBufferDesc.Format = DXGI_FORMAT_R32_TYPELESS;
depthBufferDesc.Width = shadowmapSize;
depthBufferDesc.Height = shadowmapSize;
depthBufferDesc.MipLevels = 1;
depthBufferDesc.SampleDesc.Count = 1;
depthBufferDesc.Usage = D3D11_USAGE_DEFAULT;
depthBufferDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL | D3D11_BIND_SHADER_RESOURCE;
depthBufferDesc.MiscFlags = D3D11_RESOURCE_MISC_TEXTURECUBE;
DXCALL(device->CreateTexture2D(&depthBufferDesc, NULL, &mShadowmapTexture));
D3D11_DEPTH_STENCIL_VIEW_DESC dsvDesc;
ZeroMemory(&dsvDesc, sizeof(D3D11_DEPTH_STENCIL_VIEW_DESC));
dsvDesc.Format = DXGI_FORMAT_D32_FLOAT;
dsvDesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D;
dsvDesc.Texture2D.MipSlice = 0;
//dsvDesc.Texture2DArray.ArraySize = 1;
//dsvDesc.Texture2DArray.MipSlice = 0;
for (uint32_t face = 0; face < TEXTURE_CUBE_NUM_FACES; face++)
{
//dsvDesc.Texture2DArray.FirstArraySlice = face;
DXCALL(device->CreateDepthStencilView(mShadowmapTexture, &dsvDesc, &mShadowmapView[face]));
}
D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc;
ZeroMemory(&srvDesc, sizeof(D3D11_SHADER_RESOURCE_VIEW_DESC));
srvDesc.Format = DXGI_FORMAT_R32_FLOAT;
srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBE;
srvDesc.TextureCube.MipLevels = 1;
srvDesc.TextureCube.MostDetailedMip = 0;
DXCALL(device->CreateShaderResourceView(mShadowmapTexture, &srvDesc, &mShadowmapSRV));
// viewport used during shadow pass
ZeroMemory(&mShadowPassViewport, sizeof(D3D11_VIEWPORT));
mShadowPassViewport.TopLeftX = 0;
mShadowPassViewport.TopLeftY = 0;
mShadowPassViewport.Width = static_cast<float>(shadowmapSize);
mShadowPassViewport.Height = static_cast<float>(shadowmapSize);
mShadowPassViewport.MinDepth = 0.0f;
mShadowPassViewport.MaxDepth = 1.0f;
// shaders used
DXCALL(device->CreateVertexShader(gPointLightVertexShader, sizeof(gPointLightVertexShader), NULL, &mShadingVertexShader));
DXCALL(device->CreateVertexShader(gNullVertexShader, sizeof(gNullVertexShader), NULL, &mNullVertexShader));
DXCALL(device->CreatePixelShader(gPointLightPixelShader, sizeof(gPointLightPixelShader), NULL, &mPixelShader));
The shadow and stencil pass has no pixel shader, only a vertex shader which renders geometry from lights point of view. The point light pixel shader looks like this:
cbuffer PointLightConstants : register(b0)
{
float4x4 gLightWVPMatrix;
float4 gLightColor;
float4 gLightPosition;
float gLightIntensity;
float gMaxDistance;
};
Texture2D gPositionTexture : register(t0);
Texture2D gDiffuseTexture : register(t1);
Texture2D gNormalTexture : register(t2);
TextureCube gShadowmap : register(t3);
SamplerState gSampler : register(s0);
float VectorToDepthValue(float3 Vec)
{
float3 AbsVec = abs(Vec);
float LocalZcomp = max(AbsVec.x, max(AbsVec.y, AbsVec.z));
const float f = 100.0; // TODO: TEMP - use constant buffer values!
const float n = 0.1;
float NormZComp = (f + n) / (f - n) - (2 * f * n) / (f - n) / LocalZcomp;
return (NormZComp + 1.0) * 0.5;
}
float4 ps_main(float4 position : SV_Position) : SV_Target0
{
float4 worldPosition = gPositionTexture[uint2(position.xy)];
float4 diffuse = gDiffuseTexture[uint2(position.xy)];
float3 normal = (float3)gNormalTexture[uint2(position.xy)];
float3 positionDiff = (float3)(gLightPosition - worldPosition);
// shadowmapping
float3 cubemapDir = (float3)(worldPosition - gLightPosition);
float storedDepth = gShadowmap.Sample(gSampler, cubemapDir).r;
float visibility = 0.0;
if (storedDepth + 0.0001 > VectorToDepthValue(cubemapDir))
visibility = 1.0;
// light attenuation
float distance = length(positionDiff);
float attenuation = clamp(1.0 - distance * distance * (1 / (gMaxDistance * gMaxDistance)), 0.0, 1.0);
attenuation *= attenuation;
float3 lightDir = normalize(positionDiff);
float angleNormal = clamp(dot(normalize(normal), lightDir), 0, 1);
return visibility * diffuse * angleNormal * attenuation * gLightIntensity * gLightColor;
}
Again, I dont believe the issue is in the pixel shader, but rather in the shadow pass... I might be concerned the somehow the texture cube/depth stencil views or alike are setup improperly as, atleast when I debug in visual studio, the texture cube seems to be completely black on all 6 sides. Any ideas?
EDIT: worth mentioning, Im using frontface = CCW and glm math library (column major, right handed)
EDIT2: actually, the first side is filled white; the rest 5 are black