Skip to main content
gamedev.net
🔒 Locked

SAVSM Numeric Instability Problems

Started by Oni 0 replies 1.7k views
Original Post
Oni
Oni
[color="#333333"]Hi guys. Im having issue with loss of precision in my SAVSM setup.
5614133967_c57d44dfdc_z.jpg

when you see the light moving around the effect is very striking; there is a lot of noise with fragments going black and white all the time. This can be somewhat lessened by using the minvariance (thus ignoring anything below a certain threshold) but then we get even worse effects with incorrect falloff (see my other post).

Im using GLSL 1.2 because I'm on a mac so I dont have access to the modf function in order to split the precision across two channels as described in GPU Gems 3 Chapter 8.

Im using GL_RGBA32F_ARB textures with a Framebuffer object and ping ponging two textures to generate a summed area table which i use with the VSM algorithm.

Shadow Shader.

The Summed tables do seem to be working. I know this because I have a function that converts back from the summed table to the original depth map and the two images do look pretty much the same. Im also using the -0.5 + 0.5 trick in order to get some more precision but it doesnt seem to be helping frown.gif




uniform sampler2D ShadowMap;

uniform float minVariance;




uniform float ambientLevel;

uniform float lightAttenuation;

uniform int shadowMapSize;

uniform float filterSize;

varying vec4 ShadowCoord;

uniform vec4 lightPosition;

varying vec4 lightDir, eyeVec;

varying vec3 vertexNormal;

varying vec3 vertexNormalWorld;




vec3 ShadowCoordPostW;




float step = 1.0 / float(shadowMapSize);




float g_DistributeFactor = 1024.0;




// This is somewhat specific to the sub objects within our QC File so might need to be changed

// TODO - Minus light dir do we think?




float lightLevel() {

float d = ShadowCoordPostW.z - lightPosition.z;

float attenuation = 1.0 / (d * d * lightAttenuation);

return attenuation * max(dot(vertexNormalWorld, -lightDir.xyz), 0.0);

}




float linstep(float min, float max, float v)

{

return clamp((v - min) / (max - min), 0.0, 1.0);

}




float ReduceLightBleeding(float p_max, float Amount)

{

// Remove the [0, Amount] tail and linearly rescale (Amount, 1].

return linstep(Amount, 1.0, p_max);

}




// Box Sample Blur - WITH SUMMED TABLES!




vec4 btex2D(vec2 uv) {

float ss = step * filterSize / 2.0;

float xmax = uv.x - ss;

float xmin = uv.x + ss;



float ymax = uv.y - ss;

float ymin = uv.y + ss;




vec4 total = texture2D(ShadowMap, vec2(xmax,ymax)) - texture2D(ShadowMap, vec2(xmax,ymin))

- texture2D(ShadowMap, vec2(xmin,ymax)) + texture2D(ShadowMap, vec2(xmin,ymin));




return total / (filterSize * filterSize);

}







// Upper Bound VSM Shadow code




float chebyshevUpperBound()

{

// We retrive the two moments previously stored (depth and depth*depth)

// these are split over r,g and b,a



vec4 moments = btex2D(ShadowCoordPostW.xy);

// float FactorInv = 1.0 / g_DistributeFactor;




// vec4 moments = vec4(splits.y * FactorInv + splits.x, splits.w * FactorInv + splits.z, 0.0,0.0);



//vec4 moments = vec4(splits.x * FactorInv + splits.y, splits.z * FactorInv + splits.w,0.0,0.0);



//moments.x += 0.5; // Since using SUMMED Tables we need to adjust

//moments.y += 0.5;



//vec2 moments = texture2D(ShadowMap,ShadowCoordPostW.xy).rg;



// Surface is fully lit. as the current fragment is before the light occluder

// Hardly ever occurs because the distances will always be greater or very close to equal

if (ShadowCoordPostW.z <= moments.x)

return 1.0 ;




// The fragment is either in shadow or penumbra. We now use chebyshev's upperBound to check

// How likely this pixel is to be lit (p_max)

float variance = moments.y - (moments.x * moments.x);

variance = max(variance, minVariance);




float d = ShadowCoordPostW.z - moments.x ;

float p_max = variance / (variance + d * d);



return p_max;



//return max (1.0 - p_max, 0.0);

}







void main()

{

ShadowCoordPostW = 0.5 * (ShadowCoord.xyz / ShadowCoord.w + 1.0);




float shadow = ReduceLightBleeding(chebyshevUpperBound(),0.15);

float litFactor = (1.0 - ambientLevel) * (shadow) * lightLevel();

//gl_FragColor = gl_Color * smoothstep(ambientLevel,1.0,max(lightLevel(),shadow));

gl_FragColor = gl_Color * (litFactor + ambientLevel);

}











varying vec4 v_position;

varying float tDepth;







float g_DistributeFactor = 1024.0;





void main()

{

// Is this linear depth? I would say yes but one can't be utterly sure.

// Could try a divide by the far plane?



float depth = v_position.z / v_position.w ;

depth = depth * 0.5 + 0.5; //Don't forget to move away from unit cube ([-1,1]) to [0,1] coordinate system




vec2 moments = vec2(depth, depth * depth);







// Adjusting moments (this is sort of bias per pixel) using derivative

float dx = dFdx(depth);

float dy = dFdy(depth);

moments.y += 0.25 * (dx*dx+dy*dy);



// Subtract 0.5 off now so we can get this into our summed area table calc



//moments -= 0.5;



// Split the moments into rg and ba for EVEN MORE PRECISION



// float FactorInv = 1.0 / g_DistributeFactor;




// gl_FragColor = vec4(floor(moments.x) * FactorInv, fract(moments.x ) * g_DistributeFactor,

// floor(moments.y) * FactorInv, fract(moments.y) * g_DistributeFactor);







gl_FragColor = vec4(moments,0.0,0.0);

}






[color="#333333"]So close and yet so far! Argh! ><
If its your first night, you have to fight!

Topic Locked

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

Sign in to reply to this topic.