Interpolated PCF for integrated PSSM shadows

Anything and everything that's related to OGRE or the wider graphics field that doesn't fit into the other forums.
technique
Halfling
Posts: 91
Joined: Fri Oct 22, 2010 10:46 pm
x 8

Interpolated PCF for integrated PSSM shadows

Post by technique »

Hi,

did you ever want more smooth looking shadows from the build in PSSM-Shadow-Cam but you didnt know how to do this?


I will save your time showing how to do this (took me a few hours to get so far)..

This is the standard build in PCF for PSSM-Shadows used by the integrated RTSS-PSSM. It is a simple 2 x 2 Kernel (4 pixel lookups).

Image


And this is the 2x2 kernel * 4 for interpolating (16 pixel lookups) using interpolated depth values:

Image

And this was achived using a 4x4 kernel * 4 for interpolating e.g. 64 texture lookups:

Image

The following snippet works with CG-Shaders and the RTSS on Ogre 1.9 RC. You can easily port the few lines to your favorite shader-language (GLSL, GLSLES, HLSL).
Assuming you got the integrated RTSS PSSM already up and running (if you didnt - just use the search function and u will find threads which describing how to do this):

- Open the SGXLib_IntegratedPSSM.cg file (default path: RTShaderLib/Cg/).
- Now:

replace this function

Code: Select all

//-----------------------------------------------------------------------------
float _SGX_ShadowPCF4(sampler2D shadowMap, float4 shadowMapPos, float2 offset)
{
	shadowMapPos = shadowMapPos / shadowMapPos.w;
	float2 uv = shadowMapPos.xy;
	float3 o = float3(offset, -offset.x) * 0.3f;

	// Note: We using 2x2 PCF. Good enough (technique: but looks bad on low resolution :( ) and is a lot faster.
	float c =	(shadowMapPos.z <= tex2D(shadowMap, uv.xy - o.xy).r) ? 1 : 0; // top left
	c +=		(shadowMapPos.z <= tex2D(shadowMap, uv.xy + o.xy).r) ? 1 : 0; // bottom right
	c +=		(shadowMapPos.z <= tex2D(shadowMap, uv.xy + o.zy).r) ? 1 : 0; // bottom left
	c +=		(shadowMapPos.z <= tex2D(shadowMap, uv.xy - o.zy).r) ? 1 : 0; // top right
	return c / 4;
}
with this

Code: Select all

float _SGX_ShadowPCF4(sampler2D shadowMap, float4 shadowMapPos, float2 offset)
{
	shadowMapPos = shadowMapPos / shadowMapPos.w;
	float2 uv = shadowMapPos.xy;
	
	const int X = 3;
	const int Y = 3;
	uv.x = uv.x - X / 2 * offset.x;
	uv.y = uv.y - Y / 2 * offset.y;
	float c = 0;
	for(int x = 0; x < X; x++)
	{
		for(int y = 0; y < Y; y++)
		{
			float2 position;
			position.x = uv.x + float(x) * offset.x;
			position.y = uv.y + float(y) * offset.y;
			c += texture2DShadowLerp(shadowMap, offset, position, shadowMapPos.z);
			//c +=	(shadowMapPos.z <= tex2D(shadowMap, position).r) ? 1 : 0; 
		}
	}
	return c / (float(X) * float(Y));
}
and add the following functions to the file

Code: Select all

float texture2DCompare(sampler2D depths, float2 uv, float compare){
    float depth = tex2D(depths, uv).r;
    return step(compare, depth);
}

float texture2DShadowLerp(sampler2D depths, float2 offset, float2 uv, float compare){
    float2 texelSize = offset;
	float2 size = 1 / offset;
    float2 f = frac(uv * size + 0.5);
    float2 centroidUV = floor(uv * size + 0.5) / size;

    float lb = texture2DCompare(depths, centroidUV + texelSize * float2(0.0, 0.0), compare);
    float lt = texture2DCompare(depths, centroidUV + texelSize * float2(0.0, 1.0), compare);
    float rb = texture2DCompare(depths, centroidUV + texelSize * float2(1.0, 0.0), compare);
    float rt = texture2DCompare(depths, centroidUV + texelSize * float2(1.0, 1.0), compare);
    float a = lerp(lb, lt, f.y);
    float b = lerp(rb, rt, f.y);
    float c = lerp(a, b, f.x);
    return c;
}
- choose your favorite kernel size changing the consts X, Y in _SGX_ShadowPCF4() - remind texture lookups are expensive (keep these values as small as you can bear!)
- done! :)


My source was that guide:
http://codeflow.org/entries/2013/feb/15 ... ltered-vsm
Last edited by technique on Thu Nov 28, 2013 3:04 pm, edited 1 time in total.
Image
Kingdoms Defender offers Tower Defense action with breathtaking 3-D graphics for your mobile Android device.

Give it a try:
Free-Version:
http://play.google.com/store/apps/detai ... ender_free

Full-Version:
http://play.google.com/store/apps/detai ... msdefender
technique
Halfling
Posts: 91
Joined: Fri Oct 22, 2010 10:46 pm
x 8

Re: Interpolated PCF PSSM shadows

Post by technique »

Since its very unefficient to grab pixelinformations twice - i wrote a small but less generic solution for a 3 x 3 interpolated kernel. It just uses 9 lookups and not 9 x 3 = 27. I think this is a quite nice mixture between performance and quality.

The code uses the texture2DCompare() function from above.

Code: Select all

float _SGX_ShadowPCF4(sampler2D shadowMap, float4 shadowMapPos, float2 offset)
{	
	shadowMapPos = shadowMapPos / shadowMapPos.w;
	float2 uv = shadowMapPos.xy;

    float2 texelSize = offset;
	float2 size = 1 / offset;
    float2 centroidUV = floor(uv * size + 0.5) / size;
	float2 f = frac(uv * size + 0.5);

	const int   X = 3;

	float2 topLeft = centroidUV - texelSize * 1.5;

	// load all pixels needed for the computation
	// this way a pixel wont be loaded twice
	float kernel[9];
	for(float i = 0; i < X; i++)
		for(float j = 0; j < X; j++)
			kernel[i * X + j] = texture2DCompare(shadowMap, topLeft + float2(i, j) * texelSize, shadowMapPos.z);


	float kernel_interpolated[4];

	kernel_interpolated[0] = kernel[0] + kernel[1] + kernel[3] + kernel[4];
	kernel_interpolated[0] /= 4.0; 
	kernel_interpolated[1] = kernel[1] + kernel[2] + kernel[4] + kernel[5];
	kernel_interpolated[1] /= 4.0; 
	kernel_interpolated[2] = kernel[3] + kernel[4] + kernel[6] + kernel[7];
	kernel_interpolated[2] /= 4.0; 
	kernel_interpolated[3] = kernel[4] + kernel[5] + kernel[7] + kernel[8];
	kernel_interpolated[3] /= 4.0; 

	float a = lerp(kernel_interpolated[0], kernel_interpolated[1], f.y);
    float b = lerp(kernel_interpolated[2], kernel_interpolated[3], f.y);
    float c = lerp(a, b, f.x);
	return c;
}
This is the result:

Image
Last edited by technique on Wed Sep 04, 2013 4:08 pm, edited 2 times in total.
Image
Kingdoms Defender offers Tower Defense action with breathtaking 3-D graphics for your mobile Android device.

Give it a try:
Free-Version:
http://play.google.com/store/apps/detai ... ender_free

Full-Version:
http://play.google.com/store/apps/detai ... msdefender
Pulas
Halfling
Posts: 61
Joined: Sat Oct 29, 2011 9:39 am

Re: Interpolated PCF PSSM shadows

Post by Pulas »

Wow, great, thanks for your sharing!