Exponential shadow mapping filtering

Started by
3 comments, last by Mona2000 9 years, 7 months ago

Got some issues making ESMs work. Here's my workflow:

  1. Render exp(depth) into shadow maps
  2. Horizontal+Vertical blur said shadow maps
  3. Test in lighting pass

The problem is the shadow maps are just pure white, when looking at them in the debugger, making the comparison garbage. If I render only depth (no exp(depth)) it works just fine (of course I have to change step 3 though)

The shaders used:

Step 1:


#ifndef EXP_DEPTH_PIXEL_HLSL
#define EXP_DEPTH_PIXEL_HLSL

float ps_main(float4 position : SV_Position) : SV_Depth
{
    return exp(position.z / position.w);
}

#endif

Step 2:


#ifndef BOX_BLUR_PIXEL_HLSL
#define BOX_BLUR_PIXEL_HLSL

#include "Constants.hlsl"

cbuffer BoxBlurConstants : register(CBUFFER_REGISTER_PIXEL)
{
    // (1 / TEXTURE_WIDTH, 0) for horizontal pass, (0, 1 / TEXTURE_HEIGHT) for vertical pass
    float2 gTexelSize;
    float gTextureIndex;
};

SamplerState gSampler : register(SAMPLER_REGISTER_DEPTH_NO_COMPARE);
Texture2DArray gDepthTexture : register(TEXTURE_REGISTER_DEPTH);

static const uint NUM_SAMPLES = 7;


float ps_main(float4 position : SV_Position) : SV_Depth
{
    float ret = 0.0;

    for (int i = -3; i <= 3; i++)
    {
        const float2 texCoord = (uint2)position.xy + i;
        ret += gDepthTexture[float3(texCoord, gTextureIndex)].r;
    }

    ret /= NUM_SAMPLES;

    return ret;
}

#endif

Step 3:


#ifndef DIRECTIONAL_LIGHT_PIXEL_HLSL
#define DIRECTIONAL_LIGHT_PIXEL_HLSL

#include "FullscreenTriangleVertex.hlsl"
#include "Constants.hlsl"

#define DEPTH_BIAS 0.005
#define NUM_CASCADES 4
#define ESM_CONSTANT 10

cbuffer DirectionalLightConstants : register(CBUFFER_REGISTER_PIXEL)
{
    float4x4 gSplitVPMatrices[NUM_CASCADES];
    float4x4 gCameraViewMatrix;
    float4 gSplitDistances;
    float4 gLightColor;
    float4 gLightDirection;
};

Texture2D gPositionTexture : register(TEXTURE_REGISTER_POSITION);
Texture2D gDiffuseTexture : register(TEXTURE_REGISTER_DIFFUSE);
Texture2D gNormalTexture : register(TEXTURE_REGISTER_NORMAL);
Texture2DArray gShadowmap : register(TEXTURE_REGISTER_DEPTH);
SamplerState gShadowmapSampler : register(SAMPLER_REGISTER_DEPTH_NO_COMPARE);


float4 ps_main(float4 position : SV_Position) : SV_Target0
{
    float4 worldPos = gPositionTexture[uint2(position.xy)];
    float4 diffuse = gDiffuseTexture[uint2(position.xy)];
    float4 normal = gNormalTexture[uint2(position.xy)];

    float4 camPos = mul(gCameraViewMatrix, worldPos);

    uint index = 3;
    if (camPos.z > gSplitDistances.x)
        index = 0;
    else if (camPos.z > gSplitDistances.y)
        index = 1;
    else if (camPos.z > gSplitDistances.z)
        index = 2;

    float3 projCoords = (float3)mul(gSplitVPMatrices[index], worldPos);
    const float2 texelSize = 1.0 / float2(1024.0, 1024.0);
    projCoords.xy = (floor(projCoords.xy / texelSize)) * texelSize;

    float viewDepth = projCoords.z - DEPTH_BIAS;
    projCoords.z = float(index);
    //float visibilty = gShadowmap.SampleCmpLevelZero(gShadowmapSampler, projCoords, viewDepth);
    float occluder = gShadowmap.Sample(gShadowmapSampler, projCoords).r;
    float visibility = occluder / exp(ESM_CONSTANT * viewDepth);

    float angleNormal = clamp(dot(normal, gLightDirection), 0, 1);

    return visibility * diffuse * angleNormal * gLightColor;
}

#endif

It strikes me though, maybe the shadow map needs to cleared with a different default depth value... but what?

Advertisement

What format is the shadowmap render target?

What format is the shadowmap render target?

DXGI_FORMAT_R32_FLOAT


DX11Shadowmap::DX11Shadowmap(ID3D11DevicePtr device, const uint32_t shadowmapSize, const uint32_t numTextures, const bool isCubeTexture) : mShadowmapTexture(nullptr), mInputLayout(nullptr), mShadowmapSRV(nullptr)
    {
        mShadowmapViews.resize(numTextures);

        D3D11_INPUT_ELEMENT_DESC inputDescription;
        ZeroMemory(&inputDescription, sizeof(D3D11_INPUT_ELEMENT_DESC));
        inputDescription.SemanticName = "POSITION";
        inputDescription.SemanticIndex = 0;
        inputDescription.Format = DXGI_FORMAT_R32G32B32_FLOAT;
        inputDescription.InputSlot = 0;
        inputDescription.AlignedByteOffset = 0;
        inputDescription.InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
        inputDescription.InstanceDataStepRate = 0;
        DXCALL(device->CreateInputLayout(&inputDescription, 1, gTransformVertexShader, sizeof(gTransformVertexShader), &mInputLayout));

        // create shadowmap texture/view/srv
        D3D11_TEXTURE2D_DESC depthBufferDesc;
        ZeroMemory(&depthBufferDesc, sizeof(D3D11_TEXTURE2D_DESC));
        depthBufferDesc.ArraySize = numTextures;
        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 = isCubeTexture ? D3D11_RESOURCE_MISC_TEXTURECUBE : 0;
        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_TEXTURE2DARRAY;
        dsvDesc.Texture2DArray.ArraySize = 1;
        for (uint32_t face = 0; face < numTextures; face++)
        {
            dsvDesc.Texture2DArray.FirstArraySlice = face;
            DXCALL(device->CreateDepthStencilView(mShadowmapTexture, &dsvDesc, &mShadowmapViews.at(face)));
        }

        D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc;
        ZeroMemory(&srvDesc, sizeof(D3D11_SHADER_RESOURCE_VIEW_DESC));
        srvDesc.Format = DXGI_FORMAT_R32_FLOAT;

        if (isCubeTexture)
        {
            srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBE;
            srvDesc.TextureCube.MipLevels = 1;
            DXCALL(device->CreateShaderResourceView(mShadowmapTexture, &srvDesc, &mShadowmapSRV));
        }
        else
        {
            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_TEXTURE2DARRAY;
            srvDesc.Texture2DArray.ArraySize = numTextures;
            srvDesc.Texture2DArray.MipLevels = 1;
            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;
    }

I tried turning of depth testing and let it write anyway, but still it seems to clamp depth values to 1.0f or less.

I didn't notice that you're writing to a depth buffer using SV_Depth. IIRC the depth range is limited to 0-1 (the viewport won't allow smaller/larger values), instead you should write to a (R32_FLOAT) render target.

This topic is closed to new replies.

Advertisement