PSSM + PCF + directional light + Caelum problems

Problems building or running the engine, queries about how to use features etc.
lukeneorm
Halfling
Posts: 61
Joined: Wed Apr 01, 2009 12:03 am

PSSM + PCF + directional light + Caelum problems

Post by lukeneorm »

Hi, I'm currently using Caelum with its PSSM+PCF approach for directional lights (CaelumSample/PSSM/ShadowCaster material), and I'm trying to achive softer shadows editing the PCF code, but I have some problems. :?
This is Caelum sample.cg, where in MainFP funcion we calculate the shadow amount per split plane:

Code: Select all

	void PssmShadowCasterVP(
		float4 position         : POSITION,
		out float4 oPosition    : POSITION,
		out float2 oDepth       : TEXCOORD0,
		uniform float4x4 wvpMat)
	{
		// this is the view space position
		oPosition = mul(wvpMat, position);

		// depth info for the fragment.
		oDepth.x = oPosition.z;
		oDepth.y = oPosition.w;

		// clamp z to zero. seem to do the trick. :-/
		//oPosition.z = max(oPosition.z, 0);
	}

	void PssmShadowCasterFP(
		float2 depth        : TEXCOORD0,
		out float4 oColour  : COLOR,
		uniform float4 pssmSplitPoints)
	{
		float finalDepth = depth.x / depth.y;
		oColour = float4(finalDepth, finalDepth, finalDepth, 1);
	}

	// Vertex program entry point.
	void MainVP(
			in float4 iPosition : POSITION,
			in float2 iTexCoord : TEXCOORD0,
			in float4 iNormal : NORMAL,

			uniform float4x4 texWorldViewProjMatrix0,
			uniform float4x4 texWorldViewProjMatrix1,
			uniform float4x4 texWorldViewProjMatrix2,

			uniform float4x4 worldviewproj_matrix,
			uniform float4x4 inverse_transpose_worldview_matrix,

			out float4 oLightPosition2 : TEXCOORD5,
			out float4 oLightPosition1 : TEXCOORD4,
			out float4 oLightPosition0 : TEXCOORD3,
			out float  oSplitPoint : TEXCOORD2,

			out float2 oTexCoord : TEXCOORD0,
			out float3 oNormal : TEXCOORD1,
			out float4 oPosition : POSITION)
	{
		oPosition = mul(worldviewproj_matrix, iPosition);
		oTexCoord = iTexCoord;
		oNormal = normalize(mul(inverse_transpose_worldview_matrix, iNormal).xyz);

		// Calculate the position of vertex in light space
		oSplitPoint = oPosition.z;
		oLightPosition0 = mul(texWorldViewProjMatrix0, iPosition);
		oLightPosition1 = mul(texWorldViewProjMatrix1, iPosition);
		oLightPosition2 = mul(texWorldViewProjMatrix2, iPosition);
	}

	// Fragment program entry point.
	void MainFP(
		in float2 iTexcoord : TEXCOORD0,
		in float3 iNormal : TEXCOORD1,
		in float4 iSplitPoint : TEXCOORD2,
		in float4 iLightPosition0 : TEXCOORD3,
		in float4 iLightPosition1 : TEXCOORD4,
		in float4 iLightPosition2 : TEXCOORD5,

		uniform float4 invShadowMapSize0,
		uniform float4 invShadowMapSize1,
		uniform float4 invShadowMapSize2,
		uniform float4 pssmSplitPoints,

		uniform sampler2D shadowMap0 : register(s0),
		uniform sampler2D shadowMap2 : register(s2),
		uniform sampler2D shadowMap1 : register(s1),
		uniform sampler mainTexture : register(s3),

		uniform float4 light_position_view_space,
		uniform float4 derived_light_diffuse_colour,
			   
		out float4 oColour : COLOR)
	{
		// calculate shadow
		float shadowing = 1.0f;
		float4 splitColour;
		if (iSplitPoint <= pssmSplitPoints.y) {
			splitColour = float4(0.1, 0, 0, 1);
			
			//here we calculate the shadow amount if the pixel is in the first split plane
			shadowing = shadowPCF(shadowMap0, iLightPosition0, invShadowMapSize0.xy);
		} else if (iSplitPoint <= pssmSplitPoints.z) {
			splitColour = float4(0, 0.1, 0, 1);

			//here we calculate the shadow amount if the pixel is in the second split plane
			shadowing = shadowPCF(shadowMap1, iLightPosition1, invShadowMapSize1.xy);
		} else {
			splitColour = float4(0.0, 0.0, 1.0, 1);

			//here we calculate the shadow amount if the pixel is in the third split plane
			shadowing = shadowPCF(shadowMap2, iLightPosition2, invShadowMapSize2.xy);
		}

		oColour = float4(0, 0, 0, 0);
		float4 baseColour = tex2D(mainTexture, iTexcoord);
		baseColour = baseColour*2;
		float3 normal = normalize(iNormal);
		float diffuse_factor = max(0, dot(float4(normal, 1), light_position_view_space));
		float4 light_colour = diffuse_factor * derived_light_diffuse_colour * shadowing;
		oColour += baseColour * light_colour;
	}

And this is the shadowPCF function used in MainFP to calculate the shadow amount:

Code: Select all

	float shadowPCF(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 and is alot 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;
	}
I try to modify it by adding a loop to allow more steps and so softer shadows, in this way:

Code: Select all

	//Try to achive soft shadow PCSS + PCF with constant penumbrae 
	float shadowPCF(sampler2D shadowMap, float4 shadowMapPos, float2 offset)
	{
		shadowMapPos = shadowMapPos / shadowMapPos.w;
		float2 uv = shadowMapPos.xy;

		float radius = offset;
		float steps = 4;	
		double stepSize = 2.0 * radius / steps;
		uv.xy -= radius.xx;
		float total = 0;
		for (int x = 0; x < steps; ++x)
			for (int y = 0; y < steps; ++y)
				total += (shadowMapPos.z <= tex2D(shadowMap, float2(uv.xy + float2(x * stepSize, y * stepSize))).r) ? 1 : 0;
		
		return total / (steps * steps);
	}
it works for 'steps' values 1, 2, 3 and 4 (image on the left).. but if I try to use more than 4 steps, the shadows are incorrect: the floor seem to be completely lit, while the objects in the air seems to be partially lit (image on the right)..

Image

I can't really figure out where is the issue.. am I doing some errors in how I modify shadowPCF code?
Thank you for any kind of help!
iigor
Gnome
Posts: 387
Joined: Thu May 08, 2008 3:46 pm
Location: Russia, Moscow

Re: PSSM + PCF + directional light + Caelum problems

Post by iigor »

Look at Ogre.log, also try higher shader profile ( ps_3_0 ).
lukeneorm
Halfling
Posts: 61
Joined: Wed Apr 01, 2009 12:03 am

Re: PSSM + PCF + directional light + Caelum problems

Post by lukeneorm »

iigor wrote:Look at Ogre.log, also try higher shader profile ( ps_3_0 ).
You're right. If I use more than 4 steps in shadowPCF function, ogre.log report this error:

Code: Select all

	error C6002: Instruction limit of 512 exceeded; 745 instructions needed to compile program
Why? The loop in that function performs step*step passes.. the difference between step=4 and step=5 would be only 9 istructions (5*5 = 25, instead of 4*4 = 16).. how can an overhead of 9 instructions make the shader code exceed from less than 512 to 745 instructions?! :shock:
PCF filtering is the only way I found to work to have soft-shadows with PSSM using directional lights, but the penumbra with 4 steps is too narrow to get good results for my project: I need more than 4*4 samples! :(

I also try to use vs_3_0 and ps_3_0 profiles in vertex and fragment programs, but Ogre rise up an exception in the startup, reporting many errors like this:

Code: Select all

Cannot assemble D3D shader CaelumSample/PSSM/OneLightFP Errors:
C:\OgreSDK\bin\release\memory(67,1): error X6077: texld/texldb/texldp/dsx/dsy instructions with r# as source cannot be used inside dynamic conditional 'if' blocks, dynamic conditional subroutines calls, or loop/rep with break*.
.. I use D3D and my video card is a NVidia GeForce 9600 GT, and ogre.log says that ps_3_0 profile is supported:

Code: Select all

  * Supported Shader Profiles: hlsl ps_1_1 ps_1_2 ps_1_3 ps_1_4 ps_2_0 ps_2_a ps_2_b ps_2_x ps_3_0 vs_1_1 vs_2_0 vs_2_a vs_2_x vs_3_0
..argh! :(
lukeneorm
Halfling
Posts: 61
Joined: Wed Apr 01, 2009 12:03 am

Re: PSSM + PCF + directional light + Caelum problems

Post by lukeneorm »

lukeneorm wrote:
iigor wrote:Look at Ogre.log, also try higher shader profile ( ps_3_0 ).
Thank you for your reply iigor, you're right. If I use more than 4 steps in shadowPCF function, ogre.log report this error:

Code: Select all

	error C6002: Instruction limit of 512 exceeded; 745 instructions needed to compile program
Why? The loop in that function performs step*step passes.. the difference between step=4 and step=5 would be only 9 istructions (5*5 = 25, instead of 4*4 = 16).. how can an overhead of 9 instructions make the shader code exceed from less than 512 to 745 instructions?! :shock:
PCF filtering is the only way I found to work to have soft-shadows with PSSM using directional lights, but the penumbra with 4 steps is too narrow to get good results for my project: I need more than 4*4 samples! :(

I also try to use vs_3_0 and ps_3_0 profiles in vertex and fragment programs, but Ogre rise up an exception in the startup, reporting many errors like this:

Code: Select all

Cannot assemble D3D shader CaelumSample/PSSM/OneLightFP Errors:
C:\OgreSDK\bin\release\memory(67,1): error X6077: texld/texldb/texldp/dsx/dsy instructions with r# as source cannot be used inside dynamic conditional 'if' blocks, dynamic conditional subroutines calls, or loop/rep with break*.
.. I use D3D and my video card is a NVidia GeForce 9600 GT, and ogre.log says that ps_3_0 profile is supported:

Code: Select all

  * Supported Shader Profiles: hlsl ps_1_1 ps_1_2 ps_1_3 ps_1_4 ps_2_0 ps_2_a ps_2_b ps_2_x ps_3_0 vs_1_1 vs_2_0 vs_2_a vs_2_x vs_3_0
..argh! :(
Vectrex
Ogre Magi
Posts: 1266
Joined: Tue Aug 12, 2003 1:53 am
Location: Melbourne, Australia
x 1

Re: PSSM + PCF + directional light + Caelum problems

Post by Vectrex »

Haven't READ your post, but a common thing with caelum is the farclip plane is MASSIVE due to the way the clouds are rendered. Just bring it right in and draw the cloud on a small dome that locks to the camera.
lukeneorm
Halfling
Posts: 61
Joined: Wed Apr 01, 2009 12:03 am

Re: PSSM + PCF + directional light + Caelum problems

Post by lukeneorm »

Vectrex wrote:Haven't READ your post, but a common thing with caelum is the farclip plane is MASSIVE due to the way the clouds are rendered. Just bring it right in and draw the cloud on a small dome that locks to the camera.
Hi Vectrex, "Caelum" in my topic title stands for "I use PSSM implementation that comes with Celum system", that's all. The problem here is that incrementing PCF sampling from 4 (the number of sampling that comes with PCF Caelum implementation) to 16 (using step = 4 in my modified code) works fine, but.. as soon I increment the samples.. Ogre arise this exception: "Instruction limit of 512 exceeded; 745 instructions needed to compile program" for the pixel shader.. and I don't think it's because of the farclip plane, because with 4 samples PCF shadows work well.. :?
Vectrex
Ogre Magi
Posts: 1266
Joined: Tue Aug 12, 2003 1:53 am
Location: Melbourne, Australia
x 1

Re: PSSM + PCF + directional light + Caelum problems

Post by Vectrex »

hmm, don't different shader functions take up different numbers of 'instructions'? I don't think it's directly related to just function calls. Either that or your loop has a loop inside it, so it's rising exponentially
lukeneorm
Halfling
Posts: 61
Joined: Wed Apr 01, 2009 12:03 am

Re: PSSM + PCF + directional light + Caelum problems

Post by lukeneorm »

Vectrex wrote:hmm, don't different shader functions take up different numbers of 'instructions'? I don't think it's directly related to just function calls. Either that or your loop has a loop inside it, so it's rising exponentially
I only changed shadowPCF function in CaelumSample.cg from this:

Code: Select all

float shadowPCF(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 and is alot 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;
}
Into this:

Code: Select all

//Try to achive soft shadow PCSS costant penumbrae --------------
float shadowPCF3(sampler2D shadowMap, float4 shadowMapPos, float2 offset)
{
    shadowMapPos = shadowMapPos / shadowMapPos.w;
    float2 uv = shadowMapPos.xy;

	float radius = offset;
	float steps = 4;	
    //double stepSize = 2.0 * radius / steps;
	float stepSize = 2.0f * radius / (steps - 1.0f);
    uv.xy -= radius.xx;
    float total = 0;
    for (int x = 0; x < steps; ++x)
        for (int y = 0; y < steps; ++y)
            total += (shadowMapPos.z <= tex2D(shadowMap, float2(uv.xy + float2(x * stepSize, y * stepSize))).r) ? 1 : 0;
	
	total = total / (steps * steps);
	return total;
	//return 1;
}
Yes, there is a loop inside another, but.. it is so clear that the entire loop takes only step*step instructions.. how can a change from step=4 to step=5 increment the instructions from less than 512 to 745.. :?
Smjert
Greenskin
Posts: 100
Joined: Fri Jul 10, 2009 9:33 pm

Re: PSSM + PCF + directional light + Caelum problems

Post by Smjert »

Use Amd GPU Shader Analyzer, it will show you the decompiled version as soon as the source is correct (you can edit it in the same window).
Also i think Ogre talks about assembly instructions, so 1 High level shader language (not HLSL only :P) could not be only 1 assembly instruction.
which pixel shader version are you using?

PS: if you use Pixel Shader < 3.0 the loops are unrolled, so instead of having one instruction that says "repeat this block of instructions x times", it copies the same instructions x times, so this is why you have more instructions.
Also to calculate the radius of sampling you have to do some multiplications, anyway more arithmetics than before.
lukeneorm
Halfling
Posts: 61
Joined: Wed Apr 01, 2009 12:03 am

Re: PSSM + PCF + directional light + Caelum problems

Post by lukeneorm »

Hi Smjert,
I try to use pixel shader 3.0, but the problem now is:

Code: Select all

C:\OgreSDK3\bin\debug\memory(58,1): error X6077: texld/texldb/texldp/dsx/dsy instructions with r# as source cannot be used inside dynamic conditional 'if' blocks, dynamic conditional subroutine calls, or loop/rep with break*. 
I found this thread on this problem:
http://www.ogre3d.org/forums/viewtopic.php?t=41325

It seems that in PS3.0 it isn't possible to do conditional loops without calculating the derivatives outside of the loop. So I try to use ddx/ddy functions as suggested by nullsquared in that thread:

Code: Select all

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

    float2 radius = offset;
    float steps = 4;	
    float stepSize = 2.0f * radius / (steps - 1.0f);
    uv.xy -= radius.xx;
    float total = 0;
    //Calulate derivates here ---------------------
    float4 dd = float4(ddx(uv.xy), ddy(uv.xy));
    for (int x = 0; x < steps; ++x)
        for (int y = 0; y < steps; ++y)
			total += (shadowMapPos.z <= tex2D(shadowMap, float2(uv.xy + float2(x * stepSize, y * stepSize)), dd.xy, dd.zw).r) ? 1 : 0;
	
    total = total / (steps * steps);
    return total;
}
But I still got the same problem.. :?
Smjert
Greenskin
Posts: 100
Joined: Fri Jul 10, 2009 9:33 pm

Re: PSSM + PCF + directional light + Caelum problems

Post by Smjert »

I think that the dynamic "if" the error refers to is the ternary operator.

i have this snippet of code:

Code: Select all

float4 shadowinfo;
		shadowinfo.x = p_In.texpos1.z / ZSCALE; // zReceiver
		shadowinfo.y = -2; // j
		shadowinfo.z = -2; // k
		shadowinfo.w = 1.0 / 1024.0; // texelsize
		float2 offset = 0.0;
		float step = 4; // this is 16 Sample

		[...]

		p_In.texpos1.xy = p_In.texpos1.xy / p_In.texpos1.w;
		for(int i = 0; i < step; ++i)
		{	
			for(int y = 0; y < step; ++y)
			{
				offset = shadowinfo.yz * shadowinfo.w;
				if(tex2D(shadowMap1, p_In.texpos1.xy + offset).r > shadowinfo.x)
					shadowZ += 1;
							
				++shadowinfo.z;
			}
			++shadowinfo.y;
		}

		shadowZ = shadowZ / 16.0;
And works without problem with pixel shader 3.0, i dont' think that the problem are derivates since till now you didn't use them, so try to replace the ternary operator with a if.

PS: Mind that this is HLSL...