Skip to main content
gamedev.net
🔒 Locked

Variance Shadow Mapping

Started by XVincentX 26 replies 5.7k views
Original Post
XVincentX
XVincentX
I was giving a look to Variance Shadow Mapping and the alghoritm looks like very easy. I just wanted to try without the usual gaussian blur. So i modified my code in pixel shader:

int ShadowValue = (step((LightToVecDist/FarClip),SpotShadow.x) && Atten != 0);
		
		float lit = (float2)0.0f;
		float E_x2 = SpotShadow.y;
		float Ex_2 = SpotShadow.x * SpotShadow.x;
		float variance = E_x2 - Ex_2;
		float mD = SpotShadow.x - (LightToVecDist/FarClip);
		float mD_2 = mD * mD;
		float p = variance / (variance + mD_2);
		lit = max( p, ShadowValue );
		
    
		LightToVec = In.WPos - LightInfo.xyz;
		LightToVecDist = length(LightToVec);
		float CubeShadow = Cubemap.Sample(SamplText,(LightToVec/LightToVecDist));	
		int CubeValue =  step((LightToVecDist / FarClip),CubeShadow);
		
		float4 SpotLight = (Atten * dot( In.Nor, LightToVecDirection ));
		return ((PointLight * CubeValue) + (SpotLight * lit) + 0.1f) * BaseColor;

And obiously modified the depth pass return float2(depth,pow(depth,2)); But it does not work: the shadow remains perfectly the same. BTW, a concept is not really clear to me. Why have i to store depth^2 in the second channel and retrive it? Can't i compute it directly in Pixel shader retriving original depth? http://img125.imageshack.us/img125/6706/imgm.jpg As i imagined, E_x2 = E2_x
undead
undead
To put it simple: if you applied gaussian blur, you'd see the second component change.

Unintuitively, it is going to change for the same reason a non-linear (gamma unaware) rendering pipeline is wrong. But in this case it is the desired result. :)

Edit: there are implementations where the second component isn't x*x, but (x+somevalue)*(x+somevalue). Try playing with ddx and ddy.. ;)
fboivin
fboivin
As far as why you don't just store x instead of [x,x^2] is because of the blurring process. blur(x)^2 != blur(x^2).

For example, if you store [x, x^2] and have the following 9 values
[0.1, 0.01] [0.2, 0.04] [0.1, 0.01][0.2, 0.04] [0.4, 0.16] [0.2, 0.04][0.1, 0.01] [0.2, 0.04] [0.1, 0.01]


If you apply some blur kernel to those values, the value at [0.4, 0.16] might come out as [0.225, 0.0625]. It becomes clear that if you only stored x (ie: 0.4 which becomes 0.225 after blurring) and square it the difference is major (0.225^2=0.050625 where we expect 0.0625, a 0.011875 difference)
XVincentX
XVincentX
So i can't find change becouse i do not make blur.
I tried in this way

float Depth = blablabla;  float dx = ddx(Depth);    float dy = ddy(Depth);   float Depth_2 = Depth*Depth + 0.25*(dx*dx + dy*dy);  return float2(Depth,Depth_2);


As suggested from GPU Gems 3, but it still does not work.
Mabye i'm forced to use blur...
undead
undead
I can only think of two possible problems: filtering and small values.

As for filtering, is your HW capable of bilinear filtering Floating Point buffers? Did you enable it?

As for small values, try adding something like
"variance = max(variance,0.001);" (or another small value) after "float variance = E_x2 - Ex_2;".

Maybe a comparative screenshot could be helpful, could you post one?
FeverGames
FeverGames
Quote:
Original post by undead
I can only think of two possible problems: filtering and small values.

As for filtering, is your HW capable of bilinear filtering Floating Point buffers? Did you enable it?

As for small values, try adding something like
"variance = max(variance,0.001);" (or another small value) after "float variance = E_x2 - Ex_2;".

Maybe a comparative screenshot could be helpful, could you post one?


i might sound stupid, but how does one enable bilinear filtering for floating point buffers?
undead
undead
At least in DX9 .fx files it's the gool old "xxxFilter=Linear;" in your sampler declaration. :)
XVincentX
XVincentX
I'm using D3D10. I will try all your suggestion and let you know.
XVincentX
XVincentX
This is the scene with shadow (look at car's shadow to see the strong alias).
Backbuffer = 800x600
ShadowMap = 128x128

Shader:

float2 ps_spotdepth (VPS_OUTPUT In)	:	SV_TARGET{		float depth = (distance(In.WPos,LightInfo3) / FarClip) + 0.0001f;	float dx = ddx(depth);	float dy = ddy(depth);	float depth_2 = depth * depth + 0.25 * (dx*dx+dy*dy);		return float2(depth,depth_2);	}		int ShadowValue = (step((LightToVecDist/FarClip),SpotShadow.x) && Atten != 0);				float lit = 0.0f;		float E_x2 = SpotShadow.y;		float Ex_2 = SpotShadow.x * SpotShadow.x;		float variance = E_x2 - Ex_2;		float mD = SpotShadow.x - (LightToVecDist/FarClip);		float mD_2 = mD * mD;		float p = variance / (variance + mD_2);		lit = p;				LightToVec = In.WPos - LightInfo.xyz;		LightToVecDist = length(LightToVec);		float CubeShadow = Cubemap.Sample(SamplText,(LightToVec/LightToVecDist));			int CubeValue =  step((LightToVecDist / FarClip),CubeShadow);				float4 SpotLight = (Atten * dot( In.Nor, LightToVecDirection ));		return ((PointLight * CubeValue) + (SpotLight * lit) + 0.1f) * BaseColor;


Do not take care about PointLight

http://img16.imageshack.us/img16/7817/imgvai.jpg

Using max(variance,0.001) it "works" (also if not perfeclty), but i would like to avoid this.
Sampler is MIN_MAG_MIP_LINEAR
undead
undead
Quote:
Original post by XVincentX
Using max(variance,0.001) it "works" (also if not perfeclty), but i would like to avoid this.

I understand your point, but I looked at GoW shaders to double check and they also have a tweakable epsilon. We should probably ask AndyTX, but I remember seeing an epsilon in many VSM implementations.

Quote:

Sampler is MIN_MAG_MIP_LINEAR

It seems you're enabling bilinear filtering for mipmaps (I might be wrong as I only had a brief look at DX10). Are you generating all mipmap levels? Does switching between point and bilinear mipmap filtering produce different results?
XVincentX
XVincentX
Yes, on 128x128 texture i've got 7 mipmap levels (and all works)
I will try also with point filtering and we will see.
But i do not understand how filtering can help me about variance. I'm not using nothing such SampleCmp or SampleLevel or SampleCmpLvlZero. MipMapping are automatically choosen based on camera distance? (If yes or no, can you explain me how does it works?)

I think i will try gaussian blur to entire depth texture and see if something changes.
MJP
MJP
Mipmap levels are selected based on the derivatives of the texture coordinates with regard to the 4 pixels in a quad that are being shaded. So if the derivatives are very small you get a smaller mip level. You can also explicitly specify a mip level, if you want.
undead
undead
Quote:
Original post by XVincentX
But i do not understand how filtering can help me about variance.

I don't know if it's 100% correct, but it makes sense to me.

Suppose you're drawing a simple textured triangle. By switching filtering type, you get different results because the way the texel is fetched is different.
In case of a black/white texture if bilinear fintering is enabled, the edge gets grey because there's an interpolation.

(on that subject there's an old (and somehow surprising) post on IQ's dev journal about the way filtering is performed on HW, not entirely IT but it deserves a look)

Since interpolation (in this case bilinear filtering) "smooths" transitions, and since VSM takes advantage of a probability function (which can be smoothed), enabling "xxxfilter=linear" should make your shadows softer.

Although not as noticeable as a gaussian blur pass, bilinear filtering should improve your VSM rendering.

In my case, under extreme conditions, HW not supporting floating point bilinear filtering results in AWFUL shadows. When I say AWFUL I mean: blocky shadows vs gently smoothed ones. And it was only a matter of point/linear filtering, as the entire rendering pipeline (blur passes included) was exactly the same.
XVincentX
XVincentX
I've got a HD2400, i think it will support trilinear filtering. Anyway, i've got also a HD3870 crossfire, so i will try also on it.
Anyway, D3D10 has not got CAPS, but a minimal function set it's supported (and i think there is also trilinear filtering).

Saying truth, i've not still full understand how Variance Shadow Map works (so i do not know how is involved mipmapping and all other), i hoped to learn after coded it (as i usually do).

Anyway, remains that E_x2 = E2_x, and so Variance has no effect on the shadow.
May this be involved due also to light position??
undead
undead
Quote:
Original post by XVincentX
Saying truth, i've not still full understand how Variance Shadow Map works (so i do not know how is involved mipmapping and all other), i hoped to learn after coded it (as i usually do).

Instead of performing multiple tests to check the depth distribution around a point (standard PCF), VSM takes advantage of Chebyshev's inequality to directly work on an estimation of the depth distribution.

Quote:

Anyway, remains that E_x2 = E2_x, and so Variance has no effect on the shadow.
May this be involved due also to light position??

By looking at the shot you posted, I guess they should be different as there's an object (car) in front of a wall.

Light position is surely important, as the output depth and depth^2 values come from "length(lightpos-worldpos)/farclipplane". The scale (and thus the farclipplane value) affects the precision required.

I have noticed some artifacts using Float16 channels and in extreme cases I need a 32-bit per component depth buffer. This happens because some values are too small to be represented with a 16-bit per component format. After all, floats on computers are always an approximation.

Assuming your shader code is fine, your problem could be related to precision issues. Common cases are:
1- you're using a 16-bit per component buffer
2- your farclipplane is set very far away

To allow my shadow maps to get the maximum available precision, each light has a proper far clip plane set, as I don't need my lights to render something 10km away. If you don't do it already, set a small far clip plane when rendering the VSM depth map. 30-40 meters should be enough, as the light contribution is probably going to be attenuated to zero before 30-40 meters.

Adjusting the far clip plane should improve precision. If you still don't get differences between E_x2 and Ex_2 there's probably something wrong elsewhere.

ps sorry if my language isn't scientific, it would be easier for me to explain that in italian. :)
XVincentX
XVincentX
MA IO SONO ITALIANO!!!
undead
undead
I know you're italian, it's written close to your username. :)

My ps was some kind of: "if you'll ever need/want to write me a PM, it's ok if you do it in italian". :P

News about this VSM issue?
XVincentX
XVincentX
Quote:

1- you're using a 16-bit per component buffer
2- your farclipplane is set very far away


I'm using 32-bit per component buffer, so there is enough precision.
Point filtering did not help me.
My farclip plane is set to 200.0f (the same of Projection Matrix), i just choosed that value randomly. I tryed play with it but even if 10, 100, 1000 or 400 the shadow remains aliased.
XVincentX
XVincentX
I made a lot of reseraches, but as i can see my implementation is correct...but it looks like VSM does not improve my shadows. The only way to have some improvements is to add some Gaussian Blur...someone made VSM correctly?
FeverGames
FeverGames
if i am right vsm does correct shadowing with mipmapping (distances). If one adds things like gaussian blur it adds the idea of soft shadow maps.

I dunno about your implementation, but I got all my information about it from:
Punkbuster

Quote:

Figure 1: Comparison of anisotropic filtering vs. no anisotropic filtering. Top: Regular shadow map with bilinear percentage
closer filtering. Bottom: Variance shadow map with mipmapping and 16x anisotropic filtering.

from the original paper

Topic Locked

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

Sign in to reply to this topic.