Jittered shadow map, wrong results

Started by
11 comments, last by jcabeleira 14 years, 1 month ago
Hello, I'm trying to get soft shadow mapping by using jittered samples per pixel but unfortunately I'm getting strange results: As you can see, the shadow is a bit jittered but the transition is not soft. In fact, the aliased shadow is still visible in the middle of the jittered effect. For the jittering I'm using a disk of 16 sampling positions that are passed to the shader as constants and are "randomly" rotated per-pixel. The random rotation is performed by reading a texture that contains a different rotation angle per pixel which is used to generate a 2D rotation matrix that is applied to all the sampling positions (not the most efficient solution, I know). The shadowmap sampling and depth comparison is done manually since I'm using linear depth shadowmaps. Here is the code. Can someone tell me what is the problem?

#define MAX_SAMPLES	32

uniform vec2		samplingVectors[MAX_SAMPLES];
uniform int			numSamples;
uniform sampler2D	noiseTexture;


float sampleSoftShadowMap(sampler2D shadowMap, vec4 shadowTexCoord, float kernelRadius= 0.007){

	float result= 0.0;
	float kernelRotationAngle= texture2D(noiseTexture, gl_TexCoord[0].st*vec2(42.5, 32.8)*2.0).x*2.0*3.14;
	mat2 kernelRotationMatrix= mat2(cos(kernelRotationAngle), -sin(kernelRotationAngle), sin(kernelRotationAngle), cos(kernelRotationAngle));

	vec2 texcoord= shadowTexCoord.xy/shadowTexCoord.w*0.5+ 0.5;
	
	for(int i= 0; i< numSamples; ++i){
		float occluderDepth= texture2D(shadowMap, texcoord+ samplingVectors*kernelRotationMatrix*kernelRadius*5.0).x;
		
		if(shadowTexCoord.z< occluderDepth+ 0.005)
			result+= 1.0;
	}
		
	return result/numSamples;
}

[Edited by - jcabeleira on February 23, 2010 8:19:02 PM]
Advertisement
Can you post a list of the vectors you pass in as the samplingVectors[MAX_SAMPLES] uniform? My guess would be that they contain some zero-vectors or a high amount of small vectors.
The problem isn't the vectors, I've checked that.
Currently the vectors are randomly generated which could cause the problem you mentioned. To exclude that possibility I've replaced them by a poisson disk, that I got from an NVidia sample, but the problem remains.
Show us a screen with fewer samples and one with more if possible.

Also what happens if you don't rotate your kernel?

That line could be a lot of things. Such as your sample vectors being too small... or perhaps its a rounding error.
I've manage to get good results, but the way I've done is weird.
I remembered that most smoothing filters require an weight factor based on the distance of each sample to the center of the filter kernel. Intuitively, distant samples should have less impact on the shadow than closer samples. I've added that weight factor but I still got wrong results.

I turns out that it only works if I use the inverted weight function, where distant samples have more impact on the shadow result than closer ones! But does that make sense?

Here is the code. Since each sampling vectors has a length between 0 and 1, I use it to weight the sample. The final result is then renormalized to account for the weighting:

#define MAX_SAMPLES	64uniform vec2		samplingVectors[MAX_SAMPLES];uniform int			numSamples;uniform sampler2D	noiseTexture;float sampleSoftShadowMap(sampler2D shadowMap, vec4 shadowTexCoord, float kernelRadius= 0.007){	float result= 0.0;	float kernelRotationAngle= texture2D(noiseTexture, gl_TexCoord[0].st*vec2(42.5, 32.8)*2.0).x*2.0*3.14;	mat2 kernelRotationMatrix= mat2(cos(kernelRotationAngle), -sin(kernelRotationAngle), sin(kernelRotationAngle), cos(kernelRotationAngle));	vec2 texcoord= shadowTexCoord.xy/shadowTexCoord.w*0.5+ 0.5;		float sum= 0.0f;	for(int i= 0; i< numSamples; ++i){		float occluderDepth= texture2D(shadowMap, texcoord+ samplingVectors*kernelRotationMatrix*kernelRadius*5.0).x;				if(shadowTexCoord.z< occluderDepth+ 0.005)			result+= length(samplingVectors);					sum+= length(samplingVectors);	}		return result/sum;
It all depends how you generated your sampling vectors. By any chance did you do it by generating a random length and a random angle? That would explain your problem, and also why weighting by the length fixed it.
"Math is hard" -Barbie
Yes the vectors were generated with random angle and random length (length between 0 and 1). Is there a problem with this approach?

Anyway, like I said before, I replaced it by a poisson disk but the problem remains.
Yeah that's the problem then. Choose the length of your vector as length = sqrt(random(0,1)). This will give you a uniform distribution of vectors in two dimensions. Note that if you do this, don't weight your samples by their distance.
"Math is hard" -Barbie
Damn, Pragma, your advice worked really nice! Thank you very much!

I spent a few minutes figuring out why it worked and I think I understood it. The problem is that I was using a random angle and length because I believed that it would provide the most uniform distribution, but that is not correct for disc shapes since it assumes that each sample represents the same area.
On a disc shape, the samples that are more distant to the kernel represent a wider area and so should have a bigger weight. Instead of weighting each sample, your trick just distributes more samples distant to the kernel center to compensate.

Did I got it right?
You're welcome, I'm glad it worked. Your explanation of why it works is exactly right. I'd just add that the reason you make length = sqrt(random) is that area = length^2 = random. Thus the areas are uniformly distributed, so there will be the same number of samples in any fixed area.
"Math is hard" -Barbie

This topic is closed to new replies.

Advertisement