Skip to main content
GameDev.net gamedev.net
🔒 Locked

Slope-scale depth bias shadow map in HLSL

Started by Carradine Nov 5, 2014 at 8:41 PM 3 replies 17.9k views
Original Post
Carradine
Carradine

I have been searching the web for days for an answer to this, but I apparently cannot find what I need to, or I am overlooking the answer and not knowing it.

I have the issue of projective-aliasing happening with the terrain in my game, and apparently I need to use a slope-scale bias as well to calculate the shadow.

I know that the algorithm is:

m*SLOPESCALE + DEPTHBIAS

\Where m = max( | ?z/?x | , | ?z/?y | )

But apparently I am uncertain how to implement this inside of an HLSL shader. I already use a depth bias, I just need to compute the slope-scale bias as well.

Here is my code:

To render the shadow map:


technique Technique0Shadow
{
pass Pass0
{
Lighting = False;
CullMode = NONE;


VertexShader = compile vs_2_0 VS_Shadow();
PixelShader  = compile ps_2_0 PS_Shadow();

}
}

shadowType VS_Shadow( vertex IN )
{
shadowType OUT;

OUT.position = mul( worldViewProj, float4(IN.position, 1) );

OUT.fDepth.z = OUT.position.z;

return OUT;
}

pixel PS_Shadow( shadowType IN )
{
    pixel OUT;

    OUT.color = float4(IN.fDepth.z,IN.fDepth.z,IN.fDepth.z,1.0f);

    return OUT;
}

And here is the HLSL code for rendering the shadow onto the terrain ( edited for simplicity )

Vertex Shader:


fragment TerrainVS_Shadow( vertex IN )
{
    fragment OUT;
   
    OUT.color = float4(IN.color.r,IN.color.g,IN.color.b,IN.color.a);


    OUT.hposition = mul( worldViewProj, float4(IN.position, 1) );

    OUT.vProjCoord = mul( float4(IN.position, 1), g_matTexture );
    OUT.vProjCoord2 = mul( float4(IN.position, 1), g_matTexture2 );

    float4 posWorld = mul(float4(IN.position, 1), worldMat);

    OUT.distCalc = posWorld;

    return OUT;
}

pixel shader, the depth bias is 0.0005f;


pixel TerrainPS_Shadow( fragment IN )
{
    pixel OUT;
    
if ( IN.vProjCoord.x/IN.vProjCoord.w < 1.0f && IN.vProjCoord.x/IN.vProjCoord.w > 0.0f && IN.vProjCoord.y/IN.vProjCoord.w < 1.0f && IN.vProjCoord.y/IN.vProjCoord.w > 0.0f )
{
    fShadowTerm = tex2Dproj( ShadowSampler, IN.vProjCoord ) < (IN.vProjCoord.z - 0.005f) ? 0.4f : 1.0f;
}

OUT.color = float4(0,0,0,fShadowTerm  );


    return OUT;
}

My question is, how do I implement the slope-scale bias into my code as well? How do I add the equation: m*SLOPESCALE + DEPTHBIAS as the overall bias so I can eliminate my projective-aliasing?

Thanks for any info you can give!

--------------------------Vantage: Greenlit on Steam / Independent Games Festival Submission http://www.crystaldragon.com
Carradine
Carradine

So I decided to go a different route which was much simpler and seems to fix most of the issues, which was use the "normal offset" method, and it removed all of the jagged edges off of my terrain shadows, I simply had to add the line


IN.position = IN.position + (IN.vnormal * normalOffset);

Into my vertex shader for the shadow map render, and tweak the offset value for each cascade shadow map, and I have non-jagged self-shadowed terrain!

Of course there is a great deal more work to be done here, but this was a large step forward.

--------------------------Vantage: Greenlit on Steam / Independent Games Festival Submission http://www.crystaldragon.com
Hodgman
Hodgman

Depth Biasing with slope-scale and offset shouldn't be done in your shader -- there's fixed function hardware sitting around for that job!

The interpretation of the depth-bias values depends on the depth target format.
For integer depth buffers:
Bias = scale * max(|dzdx|,|dzdy|) + offset * 1/2^bits_in_z_format
For floating point depth buffers:
Bias = scale * max(|dzdx|,|dzdy|) + offset * 2^(exponent(max_z_in_primitive) - mantissa_bits_in_z_format)
Then:
If clamp is >0: FinalBias = min(Bias, clamp)
Else If clamp is <0: FinalBias = max(Bias, clamp)
Else: FinalBias = Bias
Z += FinalBias

In D3D9, you set the offset & scale variables above with this code (clamp is always 0 / unused in d3d9):


device.SetRenderState(D3DRS_DEPTHBIAS, offset);
device.SetRenderState(D3DRS_SLOPESCALEDEPTHBIAS, scale);

In D3D11, the offset/scale/clamp variables are part of the RasterState:


//make a raster state object
ID3D11RasterizerState* state;
D3D11_RASTERIZER_DESC desc = {};
desc.DepthBias = offset;
desc.DepthBiasClamp = clamp;
desc.SlopeScaledDepthBias = scale
desc.... = ...;//set all the other members too!
device->CreateRasterizerState(&desc, &state);

//set the raster state from the pre-prepared object
context.RSSetState( state );
Carradine
Carradine

Interesting, thanks for the informative information! I did see that a lot of examples set the render states outside of the shader, but just assumed that there was always an HLSL version which always seems to be more optimal.

--------------------------Vantage: Greenlit on Steam / Independent Games Festival Submission http://www.crystaldragon.com
Carradine
Carradine
For integer depth buffers:
Bias = scale * max(|dzdx|,|dzdy|) + offset * 1/2^bits_in_z_format
For floating point depth buffers:
Bias = scale * max(|dzdx|,|dzdy|) + offset * 2^(exponent(max_z_in_primitive) - mantissa_bits_in_z_format)

I am a bit confused about something however. Is this the code I use within my HLSL code for calculating the shadows with the depth map? Or is this outside of the HLSL code as well?

I assume dx/dy/dz is cameraPos - vertexPos so this would be done in the shader, correct?

device.SetRenderState(D3DRS_DEPTHBIAS, offset);
device.SetRenderState(D3DRS_SLOPESCALEDEPTHBIAS, scale);

Since these are render states outside of the shader, how do they apply to my final shader caculation to finally apply the shadow? What are they doing to the rendering of the primitives?

I think I am just confused what implementations I am supposed to be doing inside and outside the shader for the calculations.

--------------------------Vantage: Greenlit on Steam / Independent Games Festival Submission http://www.crystaldragon.com

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.