Hi guys. Im having issue with loss of precision in my SAVSM setup.
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
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);
}
Depth Shader
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);
}