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

Slope scaled depth-bias on shadow mapping

Started by Filousov Apr 8, 2005 at 6:27 AM 13 replies 21k views
Original Post
Filousov
Filousov
Hi, can anyone post here some shader code with explanation, how to implement slope scaled depth-bias on shadow mapping using shaders? Thanks, Filousov
Scoob Droolins
Scoob Droolins
Here's what I do (this is for color - not depth - shadows):

1. Dont use slope-scale depth bias.
2. Simply modify your view volume like this:
- save off your current front/back clip planes
- set new clips planes: near + 0.00025F, far + 0.0005F (experiment with these numbers to get values that work for you).
- create new view proj matrix with these values.
- render the shadows
- restore your original clip planes.

You dont need to change the z compare function. This works perfectly for me, is hw independent, and predicatable for a given front/back clip distance. Try it.
Soiled
Soiled
Quote:

1. Dont use slope-scale depth bias.

Does this work on really steep slopes with respect to the light's view ?
I've found the offset on steep slopes needs to be quite a bit bigger than flat slopes and you can't just use the offset required for steep slopes as a constant offset everywhere because then occluded objects right behind a thin occluder get incorrectly lit.

I dunno the accepted way to do slope-scale bias in a shader but here's how I calculate depth bias for an orthographic light. This is part of a shader that renders a full-screen quad over a depth shadowmap and adjusts the depth values by adding a bias. Once that's done the shadowmap can be used on the scene as usual...

float4 ps_renderMidpointDepthShader(VS_OUTPUT input) : COLOR{	// Get depth value of first surface and second closest surfaces to light.	float z1 = tex2D(g_depthSampler, input.shadowTexCoord).r;	float z2 = tex2D(g_secondDepthSampler, input.shadowTexCoord).r;	const float fac = 1.5;	float slopeOffset;#ifdef OPTIMIZE_MIDPOINT_DEPTH_SHADER // (10t,27a) instructions...	float4 t1 = input.shadowTexCoord + g_shadowTexelAdj[0];	float4 t2 = input.shadowTexCoord + g_shadowTexelAdj[1];	float4 t3 = input.shadowTexCoord + g_shadowTexelAdj[2];	float4 t4 = input.shadowTexCoord + g_shadowTexelAdj[3];	float4 dz1;	dz1.x = tex2D(g_depthSampler, t1.xy).r;	dz1.y = tex2D(g_depthSampler, t1.wz).r;	dz1.z = tex2D(g_depthSampler, t2.xy).r;	dz1.w = tex2D(g_depthSampler, t2.wz).r;	float4 slopeOffset4 = max(fac*(dz1-z1),g_zMinOffset);	float4 dz2;	dz2.x = tex2D(g_depthSampler, t3.xy).r;	dz2.y = tex2D(g_depthSampler, t3.wz).r;	dz2.z = tex2D(g_depthSampler, t4.xy).r;	dz2.w = tex2D(g_depthSampler, t4.wz).r;	slopeOffset4 = max(fac*(dz2-z1),slopeOffset4);	float2 slopeOffset2 = max(slopeOffset4.xy, slopeOffset4.wz);	slopeOffset = max(slopeOffset2.x, slopeOffset2.y);#else // (10t,44a) instructions...	// Get max neighbour difference in first depth.	float dz00 = tex2D(g_depthSampler, input.shadowTexCoord +		float2(-g_shadowTexelWidth,-g_shadowTexelWidth)).r;	float dz01 = tex2D(g_depthSampler, input.shadowTexCoord +		float2(0,-g_shadowTexelWidth)).r;	float dz02 = tex2D(g_depthSampler, input.shadowTexCoord +		float2(g_shadowTexelWidth,-g_shadowTexelWidth)).r;	float dz10 = tex2D(g_depthSampler, input.shadowTexCoord +		float2(-g_shadowTexelWidth,0)).r;	float dz12 = tex2D(g_depthSampler, input.shadowTexCoord +		float2(g_shadowTexelWidth,0)).r;	float dz20 = tex2D(g_depthSampler, input.shadowTexCoord +		float2(-g_shadowTexelWidth,g_shadowTexelWidth)).r;	float dz21 = tex2D(g_depthSampler, input.shadowTexCoord +		float2(0,g_shadowTexelWidth)).r;	float dz22 = tex2D(g_depthSampler, input.shadowTexCoord +		float2(g_shadowTexelWidth,g_shadowTexelWidth)).r;	slopeOffset = max(fac*(dz00-z1),g_zMinOffset);	slopeOffset = max(fac*(dz01-z1),slopeOffset);	slopeOffset = max(fac*(dz02-z1),slopeOffset);	slopeOffset = max(fac*(dz10-z1),slopeOffset);	slopeOffset = max(fac*(dz12-z1),slopeOffset);	slopeOffset = max(fac*(dz20-z1),slopeOffset);	slopeOffset = max(fac*(dz21-z1),slopeOffset);	slopeOffset = max(fac*(dz22-z1),slopeOffset);#endif	// From "Shadow mapping based on dual depth layers".	float zbias = min(slopeOffset,(z2-z1)/2);	return z1 + zbias;}


This is using D3DFMT_R16F. 'g_zMinOffset' is 2*D3DX_16F_EPSILON. And zbuffer has values in the range [0,1]. The 'fac' of 1.5 is a bit arbitrary (but I think it should be >1). For perspective light I think use tex2Dproj instead of tex2D. I use diff (eg, dz21-z1) instead of abs(diff) which means only neighbour samples that are further from the light than the current fragment are considered.
z2 is the second front-facing surface wrt light obtained by depth-peeling away the first front-facing surface. The min(slopeOffset,(z2-z1)/2) stops a large bias penetrating the 2nd surface causing it to be incorrectly lit. So all-in-all this requires 3 passes to generate a useable shadow map... (1) render front-faces into buffer 1, (2) render front-faces again into buffer 2 but 'clip()' any pixels that have fragment_depth < buffer1 + D3DX_16F_EPSILON and (3) running the above biasing shader using buffers 1 and 2 as input. NOTE: passes must align pixels and texels exactly otherwise it won't work (see http://www.sjbrown.co.uk/directx_texels.html).

Three passes is quite a bit but I prefer to do all the work in the shadow generation phase instead of when projecting shadows onto scene because shadow can usually be updated less frequently than the frame-rate.

Another thing is when I project shadow onto scene I only need one shadow lookup per pixel (using point sampling - which is good because FP textures don't currently allow linear sampling). I use other methods to anti-alias the shadow edges.

As an alternative I tried using a constant bias and do a lookup of the 4 nearest shadowmap texels (when projecting shadow onto scene) and when any are further from light than current pixel then the pixel is lit - it helped but still got some artifacts on steep slopes even when increasing the constant offset by a factor of 5 (from what's used in the slope-scale bias method).

Can anyone else share their way of doing slope-scale bias ? I'd also be interested...

[Edited by - Soiled on April 9, 2005 9:47:28 PM]
Filousov
Filousov
I have written the summary of what I'm doing and how along with problems and links to pictures it follows. Maybe it will be helpful, maybe someone help me.

I'm trying to implement shadow mapping into car racing project I'm working on.
I think, that I have implemented basic technique as is for example stated in NVidia hardware
shadow mapping, but I'm still having difficulties with various artefacs - incorrect self-shadowing.
I'm working under DirectX 9.

My current implementation is as follows:
1) Dynamic shadows from lamp
a) Render depth of the scene from lamp into R32F texture. Lamp is spotlight.
Bias is added in light view space - moves objects further in z direction for about 5 - 10 cm.
Then orto-projection light matrix is used in order to avoid problems with non-linear z coord.
b) Render scene from camera view and compare z-values in the light projection space.
Since depth values are in texture following matrix is used to transform coordinates into
texture space:

Texture matrix:
(0.5, 0 , 0, 0
0 ,-0.5, 0, 0
0 , 0, 1, 0
0.5 + (0.5 / TexWidth), 0.5 + (0.5 / TexHeight), 0, 1)

The problem is:
There is a lot of artifacts on the faces, which are near parallel with light direction.

I have captured a picture for you, which demonstrates the problem.
www.celba.cz/car1.jpg

In this particular screen shot it's used NVidia face percentage closer filtering from my shader.
I also tried normal comparison, 3x3 jitter PCF, but with no real success of getting better
appearance.
The picture was rendered on NVidia GeForce 6800 GT. When I try the same setting on ATI Radeon
9800, the problem is even worse. The depth texture size is 1024. Lamp is about 7 m height
and pointing to area no bigger than 10x10 m.

Is there anything I can do to get rid of artefacts at least in the middle of shadow?
(The edges of shadow are not so much problem)
I'd also like not to spend another pass on rendering. So I prefer some method,
which will not add another rendering pass.

2) Sun shadows - I'm using Trapezoidal shadow mapping for this
I think, I'm computing it as it's stated in paper: Antialiasing and continuity with trapezoidal
shadow mapping.

Sun view and projection matrix settings:
Sun view matrix only rotates so, that the light direction is same as Z+ axis.
Sun projection matrix is made by D3DXMatrixOrthoOffCenterLH and parameter is
the smallest axis aligned bounding box, which includes camera frustrum (with far plane set to 150 m)
viewed from sun view space. So camera frustrum is in box [-1,-1,0] - [1,1,1].

For clarity the code to compute sun projection matrix is:
// Compute light projection matrix
// The offset added to Min.z and Max.z ensures, that objects not seen by player camera, but still
// casting shadows to camera frustrum will cast shadows correctly
D3DXMatrixOrthoOffCenterLH(LightProjMat, Min.x, Max.x, Min.y, Max.y, Min.z - 30000, Max.z + 30000);

The computation of N_T is same as stated in paper, the only difference is, that I used
another rule to compute remapping of Pl point (the end of focus region on center line).
My rule is so, that the base of trapezoid must not be longer than 4. I start iteration with mapping of Pl to 80% line
and then maps Pl to less percent line (79%, 78%, ...) until the length of the base of the trapezoid is smaller than 4.
I have precomputed Pl remaping in the table for each angle between light and camera direction.

When I have all matrixes computes than I:
a) Render depth scene from sun into R32F texture. I'm adding bias of 10 cm in sun view space.
For not having problem with z value being affected by N_T matrix transformation, I output z of sun projection space
into texture.

My shaders are:
// TSM depth shaders
struct TSMDEPTHVS_INPUT
{
float4 Position: POSITION;
};

struct TSMDEPTHVS_OUTPUT
{
float4 Position: POSITION;
float4 TexCoord: TEXCOORD;
};

// TSM depth shader - addresses z value problem
TSMDEPTHVS_OUTPUT TSMDepthVS(TSMDEPTHVS_INPUT Input)
{
TSMDEPTHVS_OUTPUT Output;

// Texture coordinates are coordinates in light projection space
Output.TexCoord = mul(Input.Position, AllMat); // AllMat - World matrix * View matrix * Projection matrix

// Position is in post-projection TSM space
Output.Position = mul(Output.TexCoord, PostProjMat);

// Just to be sure, that z <= 1
Output.Position.xy /= Output.Position.w;
Output.Position.z = Output.TexCoord.z / Output.TexCoord.w;
Output.Position.w = 1.0;

return Output;
};

// TSM Depth pixel shader
float4 TSMDepthPS(float4 TexCoord: TEXCOORD) : COLOR
{
float4 Color = (float4) 0;

Color.r = TexCoord.z;

return Color;
};

b) Render scene from camera and compare
For this I'm using following shaders:

struct INPUT
{
float4 Position: POSITION;
float3 Normal: NORMAL;
float2 TexCoord: TEXCOORD;
};

// Output structure
struct TSM_OUTPUT
{
float4 Position: POSITION;
float4 Light: COLOR0;
float2 TexCoord: TEXCOORD0;
float4 TSMCoord: TEXCOORD1; // x,y where to look into depth texture
float4 LightCoord: TEXCOORD2; // z to compare with z from depth texture
};

// Directional light with TSM vertex shader
TSM_OUTPUT DirLightWithTSMVS(INPUT Input)
{
float3 N;
float3 V;
float3 D;
float4 World;
TSM_OUTPUT Output = (TSM_OUTPUT) 0;

// compute position
Output.Position = mul(Input.Position, AllMat);

// compute texture coordinates
Output.TexCoord = Input.TexCoord;

// compute texture coordinates for TSM
World = mul(Input.Position, WorldMat);
Output.LightCoord = mul(World, ShadowMat); // ShadowMat = Light view matrix * Light projection matrix
Output.TSMCoord = mul(Output.LightCoord, PostProjMat); // PostProjMat = N_T * texture matrix

// compute lighting
N = normalize(mul( Input.Normal, (float3x3) WorldViewMat ) );
V = -normalize(mul(Input.Position, (float3x3) WorldViewMat) );
D = -normalize(mul(DirLight.Direction, (float3x3) ViewMat) );

COLOR_PAIR Lighting = (COLOR_PAIR) 0;
Lighting = DoDirLight(N, V, D, DirLight);

Output.Light = DoResultLight(Lighting, Material, Ambient);

return Output;
};

struct TSM_PS_INPUT
{
float4 Light: COLOR0;
float2 TexCoord: TEXCOORD0;
float4 TSMCoord: TEXCOORD1;
float4 LightCoord: TEXCOORD2;

};

// Directional light with TSM shadow mapping pixel shader
float4 DirLightWithTSMPS(TSM_PS_INPUT Input): COLOR0
{

float4 ShadowCol;
float4 LightMapCol;
float4 Tex;
float Shadow;
float4 Color;

// Get standard texture
Tex = tex2D(TexSamp1, Input.TexCoord);

// Get light map
LightMapCol = tex2D(TexSamp0, Input.TexCoord); // light map for lamp - not important for sun shadows


// Get shadow map depth to x
ShadowCol = tex2Dproj(TexSamp2, Input.TSMCoord);

// compare

Shadow = (ShadowCol.r >= Input.LightCoord.z );

Color.a = Input.Light.a;
Color.rgb = saturate(Input.Light.rgb * Shadow + LightMapCol.rgb) * Tex.rgb;

return Color;
};

The problem I have is, that with small focus region set - for example 30 or less m of 150 m the artefacts appear on lamp.
The lamp is approximately 20 cm thin.

for ilustration I made these picture:
www.celba.cz/SunShadows1.jpg - in the middle of lamps, there is black area (notice, that not only on first)
www.celba.cz/SunShadows2.jpg - everything is ok, when looking from other side from enough distance from lamp
www.celba.cz/SunShadowsDown.jpg - but when near the lamp and looking down the shadow disappear in the middle of the lamp

Depth texture is 2048*2048. Focus region is set to 30 m of 150 m. Bias is 10 cm in all pictures.

What can I do in order to avoid this behaviour? Do I something bad?
Also when looking with camera in the direction of the light the shadows has a bad quality, which is of course expected.
But what trick, would you suggest me to use, in order to get better quality of shadows, when looking same direction as light?

Thank you for help.

Soiled
Soiled
Quote:

The problem is:
There is a lot of artifacts on the faces, which are near parallel with light direction.

Yeah, those artefacts are enough to drive you mad and wanna remove self-shadowing altogether :)

Have you tried D3DRS_SLOPESCALEDEPTHBIAS ? Although Carmack, in his 2004 Quakecon keynote, stated that polygon offset is not robust (due to sub-pixel polygons).

Quote:

I'd also like not to spend another pass on rendering. So I prefer some method,
which will not add another rendering pass.

Have you tried just rendering back-faces to shadowmap instead of front-faces.

Quote:

I also tried normal comparison, 3x3 jitter PCF, but with no real success of getting better
appearance.

I think the PCF is good for anti-aliasing shadow edges but not alot of help with bias problems.

Quote:

Also when looking with camera in the direction of the light the shadows has a bad quality, which is of course expected.
But what trick, would you suggest me to use, in order to get better quality of shadows, when looking same direction as light?

This is the reason I don't use PSM, TSM, etc (that and you have to update them when view changes).
For sunlight I use multiple, overlapping orthographic shadows - see Carmack's keynote for a good description.
Filousov
Filousov
1) To D3DRS_SLOPESCALEDEPTHBIAS. I actually don't know, how to use it right? Since this renderstate only works with depth buffer. I'm using R32F texture and shader to render depth. I tried moving vertexes along normals, but this have no good effect, because object like box starts to dissolve into parts.
And moving vertex only so small, that object don't start to dissolve doesn't help me. So how to do it right?

2) Rendering back faces. Yes, this I have of course tried.
While it almost entirely removes artifact from the side of car nearer to lamp.
It introduces incorrect shadowing on the side of car further from lamp.
I have taken some new screen shots in order to show you:

Both pictures taken with bias = 0 cm and front-face culling.

www.celba.cz/car2.jpg - the car from the side further from lamp
The doors should be shadowed entirely, but there is only strip of shadow
coming from faces making windows little inside the car.

www.celba.cz/car3.jpg - the car from the side nearer to lamp - I think not so bad, only problem with door-handle. (Compare with www.celba.cz/car1.jpg)

The profile of car in detail for getting better idea:
www.celba.cz/CarProfile.jpg

3) To PCF - I also tried 3x3 PCF with jitter around big area of shadow map, which introduces about 20 cm soft shadow and helped with artefacts a lot,
but passes between strength of shadow are not much pleasant.

www.celba.cz/CarPCF.jpg

4) I'm satisfied with TSM, but the problem is that with small focus region as I stated in post before. I think, this is something with triangle interpolation being different in depth and camera render, but don't know, how to get rid of it.

Filousov









SimmerD
SimmerD
Slopescaledepthbias only affects the z value itself, not any color value you output from the shader. Therefore, it may help prevent z fighting when creating your shadow map, but not when you perform shadow testing.

Also, your lighting may help hide some shadow artifacts if you are shadowing lights whose n.l <= 0.0.
Soiled
Soiled
Quote:

Slopescaledepthbias only affects the z value itself, not any color value you output from the shader. Therefore, it may help prevent z fighting when creating your shadow map, but not when you perform shadow testing.

Oops, that's right.

Quote:

2) Rendering back faces. Yes, this I have of course tried.
While it almost entirely removes artifact from the side of car nearer to lamp.
It introduces incorrect shadowing on the side of car further from lamp.
I have taken some new screen shots in order to show you:

What SimmerD said.
Try rendering your car with lighting and artefacts should reduce significantly here due to saturate(n.l) == 0 effectively putting this area in full shadow. Also lighting and noisy diffuse textures tend to reduce any artefacts in general through obscurity.

Quote:

www.celba.cz/car3.jpg - the car from the side nearer to lamp - I think not so bad, only problem with door-handle. (Compare with www.celba.cz/car1.jpg)

I'm afraid I'm running out of ideas for a single-pass approach. All I can suggest here is perhaps having a modified car mesh just for shadows that doesn't have fine detail stuff like door handles - you still get self-shadowing from stuff like the door mirror, wheels, etc.
Maybe try a multi-pass approach just for the car where the main problems seem to occur - maybe it's not as slow as you think. Perhaps a depth slope scale pass over the shadow map - this is pixel fill only.

Quote:

4) I'm satisfied with TSM, but the problem is that with small focus region as I stated in post before. I think, this is something with triangle interpolation being different in depth and camera render, but don't know, how to get rid of it.

It could well be a triangle interpolation problem. The lamp post looks like it consists of very long quads and large, or long, polygons are where this effect is most noticeable. Also the lamp post goes black in the middle (which is part furthest from the vertices) and that's where the error would be most significant. Also when you look at the backside of the lamp post (the part facing away from sunlight) the middle section is again incorrect (it should be shadowed and it's not). I would try the fully-correct TSM method and see if it fixes it...

**EDITED** // The following has been edited quite extensively...

I'll just summarise what I think you're doing first...
1) In your shadow generation pass you're outputing position as (xT/wT,yT/wT,zL/wL,1) and passing texcoordL=(xL,yL,zL,wL) in vs and outputing texcoordL.z in ps. This means position is interpolated in screen-space as interp(zL/wL) and texcoordL.z is interpolated as interp(zL) presumably since wL==1.
2) In your shadow lookup pass you're passing texcoordT=(xT,yT,zT,wT) and texcoordL=(xL,yL,zL,wL) to pixel shader and comparing tex2Dproj(texcoordT) to texcoordL.z. texcoordL.z is interpolated in screen-space as interp(zL/w)/interp(wL/w) which is interp(zL/w)/interp(1/w) since wL==1. 'w' is from the output position. texcoordT is interpolated as interp(xT/w,yT/w)/interp(wT/w).

From what I see the partially correct approach (described in TSM paper in "Handling Polygon Offset Problem" section starting with "We have two notes. First, a simpler and more efficient approach...") is...
1) Shadow generation pass: output position as (xT,yT,zL*wT/wL,wT) and pass texcoord=(xT,yT,zL*wT/wL,wT) in vs and output texcoord.z in ps. Position is interp(zL/wL) and texcoord.z is interpolated as interp(zL*wT/wL/wT)/interp(wT/wT)=interp(zL/wL).
2) Shadow lookup pass: pass texcoord=(xT,yT,zL*wT/wL,wT) to pixel shader and compare tex2Dproj(texcoord) to texcoord.z. texcoordT is interpolated as interp(xT/w,yT/w)/interp(wT/w). texcoord.z is interp(zL*wT/wL/w)/interp(wT/w) which is not the same as interp(zL/wL), but is apparently good enough for all but the largest triangles.

It seems your approach and theirs differ in the shadow lookup pass.
Since you use orthographic to avoid non-linear z, then wL==1 and you can just pass (xT,yT,zL,wT) instead of (xT/wT,yT/wT,zL/wL,1) in shadow gen pass to save a couple of divides. Also in shadow lookup pass you could merge two texcoords into one and save a texture coordinate by using texcoord=(xT,yT,zL*wT,wT) - you compare tex2Dproj(texcoord) to texcoord.z.

The fully correct approach (decribed in TSM paper) that interpolates zL correctly is...

1) Shadow generation pass: output position as (xT,yT,zT,wT) in vs and pass texcoordL=(xL,yL,zL,wL) to pixel shader and output texcoordL.z/texcoordL.w in ps (since this differs from the z-value written to depth buffer by polygon you'll need depth replace in pixel shader). So the shadowmap is interp(zL/wT)/interp(wL/wT) which gives the perspective-correct z value.
2) Shadow lookup pass: pass texcoordT=(xT,yT,zT,wT) and texcoordL=(xL,yL,zL,wL) to pixel shader and compare tex2Dproj(texcoordT) to texcoordL.z/texcoordL.w. This means zCompare is interp(zL/w)/interp(wL/w) which gives the perspective-correct z value.

Note: you need to divide in pixel shaders because even though wL==1, texcoordL.w = interp(wL/w)/interp(1/w) is not necessarily equal to one - it's only equal to one at the vertices which might explain the artefacts in middle of lamp-post (ie away from the vertices).

btw: texcoordL.z/texcoordL.w
= (interp(zL/w)/interp(1/w)) / (interp(wL/w)/interp(1/w))
= interp(zL/w) / interp(wL/w)

...this should get rid of any interpolation artefacts on the larger triangles but it outputs depth which kills any early z-reject in shadowmap generation, but not much can be done about that I guess.
Also you can't avoid the depth replace because there's no value of output.position.z in (1) that can match interp(zL/w)/interp(wL/w) in the texture coordinates because there's only one interpolator when rasterizing a triangle into render-target.

[Edited by - Soiled on April 19, 2005 7:53:46 PM]
Filousov
Filousov
1) To TSM triangle interpolation problem:
I solved the problem with TSM. Now it works as expected. The problem was in shaders. Once I send coords to pixel shader in 4D and secondly in 3D with w = 1.

2) I tried to use average of first and second depth from the light and it still produces artifacts on the car. Soiled, how important is that third pass?

3) What method do you think is best for getting better edges of shadows?

4) Is matrix stated in NVidia Hardware shadow mapping correct way to handle pixel and texel mapping problem?

Thanks for help.


Soiled
Soiled
Quote:

1) To TSM triangle interpolation problem:
I solved the problem with TSM. Now it works as expected. The problem was in shaders. Once I send coords to pixel shader in 4D and secondly in 3D with w = 1.

I'm not sure what you mean here.
Can you explain the changes you made in terms of (xL,yL,zL,wL) and (xT,yT,zT,wT) so that I can understand what made a difference ?

Quote:

2) I tried to use average of first and second depth from the light and it still produces artifacts on the car. Soiled, how important is that third pass?

For me the 3rd pass was important for the steep slopes and places where objects intersect with terrain (like a rock). It basically meant being able to have a non-constant zbias which meant I didn't have to pick a bias that worked for some objects and not others. However, the 3rd pass is quite a bit more expensive than 1st or 2nd on my ATI9800pro due to alot of pixel shader instructions needed to calculate slope offset. So for dynamic shadows I don't think this'll work very fast. Also I found that I needed to calculate 1st front-facing surface, 1st back-facing surface and 2nd front-facing surface that's behind 1st back-facing surface in order to remove artefacts where objects intersect terrain. All up it's very slow and I don't think suitable for your situation.

Quote:

3) What method do you think is best for getting better edges of shadows?

That's a sucky problem that I'm still trying to solve. "Rendering fake soft shadows with smoothies" is a good way to smooth out the jaggies - although you still get jaggies when occluder and reciever are close to each other. Also try "Extended Shadow Maps" - it's difficult to understand that one, but it appears to do an excellent job of removing jaggies although it has problems when occluders overlap.

Quote:

4) Is matrix stated in NVidia Hardware shadow mapping correct way to handle pixel and texel mapping problem?

No, you still have to make the usual adjustments to screen-space coords or tex coords to get the 1-to-1 mapping.
Filousov
Filousov
1) To TSM interpolation problem:

In depth shader I sent (xT/wT, yT/wT, zT/wT, 1) into pixel shader but in
second pass I sent (xT, yT, zT, wT) into pixel shader so I fixed it to (xT/wT, yT/wT, zT/wT, 1) and triangle interpolation problem disappeared.

2) Shadowing of car
I finally decided to use 3x3 Jittered PCF with constant bias, which is looking good on my GeForce 6600 GT. But little worse for example on ATI Radeon 9800. Seems, that computation precision is little bit different on cards.
I'll try orto matrix also for lamp.

3) NVidia hardware shadow mapping
For NVidia graphic cards I also tried to use their hardware fake PCF. (D24S8 texture format). With this I have a problem, that it's not running on my GeForce 6600 GT, but exatly same program is shadowing correctly on NVidia GeForce FX 5900. Is the situation such, that newer NVidia cards does not support hardware shadow mapping?
(For my GeForce 6600 GT I can use R32F texture instead.)

Anyway here is 2.0 pixel shader to do hardware shadow mapping on NVidia HW.

// NVidia hardware shadow mapping pixel shader
float4 NVidiaHWShadowMapPS(LAMPMAP_PS_INPUT Input): COLOR0
{
float4 ShadowCol;
float4 Tex;
float4 Result = 0;

// Get standard texture
Tex = tex2D(TexSamp1, Input.TexCoord);

// Get shadow map
ShadowCol = tex2Dproj(TexSamp2, Input.TexShadow);

Result.rgb = Input.Light.rgb * Tex.rgb * ShadowCol.rgb;
Result.a = 1;
return Result;
};

I tried also NVidia HW shadow mapping for TSM shadows, but this doesn't worked
even on my friends FX 5900. The whole shadowed area went black.
(Maybe I have somewhere a bug or just bad pixel shader.)

The problem is, how to debug it, because there is no support for D24S8 texture in reference rasterizer and no support for NVidia overloaded texture load functionality.






Filousov
Filousov
To NVidia hardware shadow mapping:
The trick is to have retail DirectX Drivers selected.
Than shadowing is ok on my GeForce 6600 GT.


Soiled
Soiled
Quote:
Original post by Filousov
1) To TSM interpolation problem:

In depth shader I sent (xT/wT, yT/wT, zT/wT, 1) into pixel shader but in
second pass I sent (xT, yT, zT, wT) into pixel shader so I fixed it to (xT/wT, yT/wT, zT/wT, 1) and triangle interpolation problem disappeared.

I just realised my analysis in a previous post was incorrect (I've edited that post with corrections).

Ok, so this is what it looks like you're now doing...

2) In your shadow lookup pass you're passing texcoordT=(xT/wT,yT/wT,zT/wT,1) and texcoordL=(xL,yL,zL,wL) to pixel shader and comparing tex2Dproj(texcoordT) to texcoordL.z.
texcoordL.z is interpolated in screen-space as interp(zL/w)/interp(wL/w) which is interp(zL/w)/interp(1/w) since wL==1.
'w' is from the output position.
texcoordT is interpolated as interp(xT/wT/w,yT/wT/w)/interp(1/w).

...and the partially correct approach (described in TSM paper) is...

2) Shadow lookup pass: pass texcoord=(xT,yT,zL*wT/wL,wT) to pixel shader and compare tex2Dproj(texcoord) to texcoord.z.
texcoord.z is interp(zL*wT/wL/w)/interp(wT/w).
texcoordT is interpolated as interp(xT/w,yT/w)/interp(wT/w).

...it's interesting that both approaches are still different yet both produce good results.
Filousov
Filousov
I managed to run NVidia HW shadow on both my GeForce 6600 GT and friend's GeForce FX 5900. My friend reinstalled drivers to newest and all is ok.

It's really bless, because shadows look good and high resolution can be achieved. I took some pictures for you:
Sun - texture resolution 2048
cars - texture resolution - 256 - 1024 varying with distance

Running on at least 110 FPS on my GeForce 6600 GT with Athlon 3.8+

www.celba.cz/FlyBy.jpg
www.celba.cz/LampDetail.jpg
www.celba.cz/CarDetail.jpg

On ATI the question to get good shadows with big framerate still remains little bit open.
Soiled
Soiled
Looks good :)

Yeah, I think only ATI's latest hardware has depth stencil textures (hardware shadow mapping).

I had the same problem with nvidia h/w shadows in direct3d debug mode a couple years ago and I've heard other people have the same problem recently - seems it's one of those things that never got fixed or couldn't be fixed or something. Running in release mode never seemed a problem for anyone though.

Topic Locked

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

Sign in to reply to this topic.