OniDaito

04-12-2011, 04:30 PM

Hi guys. Im having issue with loss of precision in my SAVSM setup.

http://farm6.static.flickr.com/5146/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 :(

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(),shado w));

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);

}

http://farm6.static.flickr.com/5146/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 :(

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(),shado w));

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);

}