Dual Paraboloid shadow mapping and tesselataion issue

Started by
13 comments, last by gboxentertainment 12 years, 3 months ago
I was expecting to have issue with tesselation and dual paraboloids mapping before using this technique, but such a worse result was not expected at all. Seams can be easily seen on this screenshot: Dual Paraboloid shadow mapping shaded And it comes with such tesselation: Dual Paraboloid shadow mapping wireframe It's becoming even worse when light is close to shadow receivers. Just for infromation, maybe it's important, i'm using VSM and shadow map resolution is limited to 1024x1024.
Advertisement
Hi! Yes, it's very hard to get high quality results. The first thing I'd point out is that your floor shouldn't need to be tesselated at all (unless it's going to be casting on something else).

http://osman.brian.googlepages.com/dpsm.pdf

That paper talks about the simple fix so that you don't need to tesselate receivers (only casters). The basic idea is that you do the WorldSpace -> Paraboloid transformation in the pixel shader during your lighting pass. That avoids having the paraboloid co-ordinates interpolated incorrectly.

You still might have some problems, though ... I can't really tell if that table is going to cast correct shadows. I think it will, but I'd like to see new screen shots after you move the shadow lookup math into the pixel shader. Good luck!

Thanks for the link! Exactly what I was looking for. I'll post how it will work.
Shadow lookup done correctly with transformation inside pixel shader. Issue in generation phase where I simply use HLSL's clip function to cull pixels that dont belong to needed hemisphere, looks like culling should be done using alpha test as in Brabec's paper.
Tesselating casters on hardware is not possible for me as I limited to DX9, actually dont understand their logic - hardware tesselation requires geometric shaders but if you have them you can render to cubemap in one render instead of six, than why one will be using paraboloids, as even for hemisphere light cubemap would be a better solution.
In worst case I'll be using paraboloid only for hemispherical lights, it's much faster than cubemap anyway on DX9.

[Edited by - Viik on November 29, 2008 4:03:03 PM]
Yes, I think you should use the alpha-test technique to do the clipping. When we mentioned the hardware tesselation, we were actually talking about Xbox 360. That's still DX9 hardware (no geometry shaders), but the GPU can do automatic tesselation. For PCs, you're right that it doesn't make much sense.

Even with some of the changes you must make, it's definitely faster than using cubemaps!
Maybe one point to note as well is that you don't even need to worry about the cracks between paraboloids if you just point one of the paraboloids downward. Then the crack will by somewhere like the equator of your scene, where it will likely be much less noticeable.
Quote:Maybe one point to note as well is that you don't even need to worry about the cracks between paraboloids if you just point one of the paraboloids downward. Then the crack will by somewhere like the equator of your scene, where it will likely be much less noticeable.

That's definately an option. Especially for static lights. Just want to achive more "general" solution.
I'm getting better results with "culling" on shadow lookup phase instead of depth render phase.
Didnt used "alpha" approach like in Brabeck's paper - as I see it requires to store separate alpha on depth render phase. I'm using VSM and that's already consumes G32R32F texture, adding alpha would require 4 channel buffer and two of them would be wasted (32 bit for alpha is too much) and actually two such textures needed. So, I completely droped culling on depth render and did a proper texture lookup on shadow render, choosing proper paraboloid texture. It works quite OK, but there are still small bug:
Tinny bug
"Small black line just under the light" - it's solely because of the choosing right "texture", it's still there with/without blur or VSM.

float3	LightVec = normalize(IN.pos - LightPos);	float3  P = mul(LightVec, (float3x3)ParaboloidBasis);	float	myDepth = length(IN.pos - LightPos);	float2	moments;	bool	branch = (P.z >= 0.0f); //forward or backward	if(branch)	{		float2 	front;			front.x = (P.x / (2*(1 + P.z))) + 0.5;			front.y = 1-((P.y / (2*(1 + P.z))) + 0.5);		float2 	forward = tex2D(ShadowSampler, front).xy;			moments = forward;	}	else	{		float2 	back;			back.x = (P.x / (2*(1 - P.z))) + 0.5;			back.y = 1-((P.y / (2*(1 - P.z))) + 0.5);		float2 	backward = tex2D(ShadowSampler, back).zw;			moments = backward;	}	float	VSM = VSMFactor(moments, myDepth);

Quote:...When we mentioned the hardware tesselation, we were actually talking about Xbox 360. That's still DX9 hardware (no geometry shaders), but the GPU can do automatic tesselation. For PCs, you're right that it doesn't make much sense.
Even with some of the changes you must make, it's definitely faster than using cubemaps!

Didn't realized that it's actually your article ))) Missed a last paragraph where you mention that it's form Xbox 360.
According to your article, correct blurring of paraboloid might be an expensive operation. I used guassian separate blur of 5x5 tap in texture space. If I do it in paraboloid space that would lead to at least 10 vector x matrix multiplications, to get all needed samples.

[Edited by - Viik on December 1, 2008 7:34:43 AM]
Haha. I wouldn't worry about "correct blurring". We never implemented that. We used PCF on a previous game, and we're using VSM now, with a regular (texture-space) 5x5 separable gaussian blur. I'm not sure about that black line. Our lookup is done very similar to yours (we have both hemispheres in a single texture, and pick the correct one + lookup in the pixel shader).


Of course, we still have light cracks between the hemispheres, but we always rotate our lights so that the hemispheres point "up" and "down". Most of our lights are above the scene, so it's rare that people can see the crack. It's still on our list of things to debug, although we've tried several times without luck. =(
Small black line was because of the black border used in sampler. Now it works like a sharm, except sometimes on depth render stage geometry from back paraboloid projected on front (i'm not using any kind of clipping), but that would be easy to fix just do clipping with small bias.
Thanks again to everybody for your help.
Hi Viik,
How did you perform the transformation in the pixel shader?
Are you supposed to just code the world position transformation code in the pixel shader instead of the vertex shader?
When I tried this, the texture just comes out black. Btw, I am still not up to the shadow mapping phase of dual paraboloid (still just testing the reflection mapping) so forgive me if this is not in the correct topic.

Here's my Vertex shader code:


DPOutput DP_VS(float3 pos : POSITION0, float3 normal : NORMAL0, float2 texcoord: TEXCOORD0)
{
DPOutput output = (DPOutput)0;
float4 posWorld = mul(float4(pos,1), World);
float4 viewPos = mul(posWorld, View);
output.Position = mul(float4(pos,1.0f), WorldViewProj);
output.Texcoords = texcoord;
output.Normal = mul(float4(normal,0), (float3x3)WorldInvTrans);
output.WorldPos = posWorld;

output.Position = output.Position/output.Position.w;
output.Position.z = output.Position.z*Direction;
float L = length(output.Position.xyz);
output.Position = output.Position/L;
output.z = output.Position.z;
output.Position.z = output.Position.z + 1;
output.Position.x = output.Position.x/output.Position.z;
output.Position.y = output.Position.y/output.Position.z;
output.Position.z = (L - 0.1f)/(1000-0.1f);
output.Position.w = 1;
return output;
}


Thanks,

Gary

This topic is closed to new replies.

Advertisement