Sign in to follow this  

Jittered shadow map, wrong results

This topic is 2847 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

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[i]*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]

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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 64

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;

float sum= 0.0f;
for(int i= 0; i< numSamples; ++i){
float occluderDepth= texture2D(shadowMap, texcoord+ samplingVectors[i]*kernelRotationMatrix*kernelRadius*5.0).x;

if(shadowTexCoord.z< occluderDepth+ 0.005)
result+= length(samplingVectors[i]);

sum+= length(samplingVectors[i]);
}

return result/sum;


Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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?

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
One more thing, pragma, could you also tell me how to pull the same trick on a sphere?

I just realized that I have a sky-dome sampling algorithm that doesn't weights each sample according to the area of the dome it represents. And that may be the reason why the results seem a little weird.

Thanks in advance!

Share this post


Link to post
Share on other sites
Yeah, no problem. If theta is the polar angle and phi is the azimuthal angle, then let
theta = acos(random(0,1))
phi = random(0, 2 pi)
This will sample the upper hemisphere. If you want the whole sphere just set
theta = acos(random(-1,1))

Share this post


Link to post
Share on other sites
Thanks, once again it worked fine!

Meanwhile I found a resource called "Global Illumination Compendium" that contains these and many other formulas. Its very usefull stuff for anyone that is developing any kind of sampling algorithm.

Share this post


Link to post
Share on other sites

This topic is 2847 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this