Shadowmaps for a point light

Started by
7 comments, last by INVERSED 17 years, 7 months ago
Hello, I have shadowmapping working for a spotlight. Now I would like to have it for a pointlight as well. I guess the common approach is to render the scene 6 times for each pointlight and store the results in a cube map. What I got did not look quite right. I assume the problem lies in the way I project the shadow back to the scene. Here is what I do for a spotlight:

//prepare the shader:
Matrix4 worldViewProj = world * m_view * m_proj * s_bias;
effect->setMatrix("g_texTransform", worldViewProj);

//vertex shader:
output.projCoord = mul(pos, g_texTransform);

//pixelshader:
float shadow = (tex2Dproj(shadowMap, input.projCoord).rgb + SHADOW_EPSILON < input.projCoord.z / input.projCoord.w) ? 0.0f: 1.0f;

Here is what I do for the pointlight:

//prepare the shader:
effect->setMatrix("g_texTransform", world * s_bias);

//vertex shader:
output.projCoord = mul(pos, g_texTransform);

//pixelshader:
float shadow = (texCUBEproj(shadowCubeMap, input.projCoord).rgb + SHADOW_EPSILON < input.projCoord.z / input.projCoord.w) ? 0.0f: 1.0f; 

Could it be that I set up the g_texTransform-matrix in a wrong way for the point light? I have no experience with cubemaps. Thanks for any help.
Advertisement
For point lights, you can't use the depth for comparision. Quadratic distance from the light source works well here and is just as fast. Be sure to set up proper view and projection matrices when rendering into the shadow map. FOV should be exactly 90°, width-to-height-relation exactly 1.0f, direction and up vectors as described in the (DX/OGL) docs. No need for projected cubemap lookup, a simple cubemap lookup will do. Just be sure to scale down the distance so it stays inside the 0..1 range.

Example shaders could look like this:

Shadow pass, Vertex Shader
...// transform vertex positionvout.position = mul( MODELVIEWPROJ, vin.position);// scaled vertex position is also light vectorvout.shadowdir = vout.position * (1 / YOUR_LIGHT_RANGE);...


And the pixel shader calculates the shadow depth from that direction vector
float4 ps( const PixelIn pin) : COLOR0{  // store quadratic distance to surface in the shadow map.  return dot( pin.shadowdir, pin.shadowdir);}


You might possibly run into precision problem when using a simple A8R8G8B8 texture. R32F or R16F, if available, work fine.

The scene vertex shader part is simple:
...// transform light dir into tangent space. // Just keep the (scaled) length of the vector for depth comparision.vout.lightdir = mul( worldToTangentSpace, (worldPos - LIGHTPOS) * (1 / YOUR_LIGHT_RANGE));...


The comparision in the pixel shader is straight forward then:

  ...  // fetch shadow distance from cubemap  float shadowDistance = texCUBE( TexShadow, pin.lightdir);  // compare against quadratic length of the light vector   float shadow_term = (shadowDistance > dot( pin.lightdir, pin.lightdir)) ? 1.0f : 0.0f;  ...


That's all about it. Good luck.

Bye, Thomas

[edit] Format, comments.
----------
Gonna try that "Indie" stuff I keep hearing about. Let's start with Splatter.
Quote:Original post by Schrompf
For point lights, you can't use the depth for comparision.

Why not?
Can't I create the shadowmaps for the pointlight by pretending to shine a spotlight in 6 different directions?

EDIT:

I believe I can see your point now.
Does the quadratic disctance approach generally have any drawbacks compared to the depth test approach, or why is it not always used?



[Edited by - B_old on September 6, 2006 9:22:53 AM]
depth test is faster, especially if you can use the 2x depth write on current hardware.
You can use the depth for testing. The problem is that the vector component that holds the "depth" changes with the side of the cube. For the left and right sides of the cube, X is the depth, for up and down it's Y and so on. If you want to compare the depth against the "depth" value read from the cubemap, you first have to determine on which side of the cube you are, then select the appropriate vector component and then compare that against the cubemap value. You can simply avoid that by using a value that is direction-independent. For example the distance to the light.

The problem in using the simple distance to light is that calculating the length of the vector involves three instructions, both when rendering the shadow map and when rendering the scene. You can reduce the calculation to a single instruction (the dot()) by using the square of the length. If a < b, than a*a < b*b, so the comparision also works fine for squared values. It's a simple performance reason, using the linear distance works fine also, albeit slower.

There is no actual "depth test" (meaning test against the ZBuffer) in it, so forget about that "2x depth write" wolf spoke of. He propably missed the topic.

Bye, Thomas
----------
Gonna try that "Indie" stuff I keep hearing about. Let's start with Splatter.
OK, with the technique you described I am able to render the shadows.
But I just can't get them to be projected right. Something is always a little bit odd about them.

When I project my spotlight I need a bias matrix (I am using D3D). But I can't get it right here.
Im not sure what people are talking about here.. there is no difference between pointlights and spotlights as far as the shadowing algorithm goes. The only difference is the fact you need 6 views.

Just render each view to the cubemap and then project it and do your depth comparison as normal.

To project your cubemap properly generate the texture coords like this:
float4 shadowproj = vertexworldpos - lightposition;

and then do a normal cubemap lookup..
It seems to work relatively OK now. The shadows are projected correctly and all.
It is just that objects that lie relatively close to the end of the lights radius don't cast shadows well, or maybe don't recieve shadows well. Is there any obvious thing you can point out about this?
Hey as an aside to what you asked earlier, I believe that you can fake a point light with six spot lights, that's what they're doing in the Oblivion engine. Anyone else read that article in GPU Gems 2 book? Also, I didn't see it mentioned here, but can't you also pack your six cube faces into a 2D texture? That way you get the full speed of a depth compare and not having to use a cubemap. I think you end up wasting some texture memory though.
Write more poetry.http://www.Me-Zine.org

This topic is closed to new replies.

Advertisement