# Who said you can't do at least a cheap simulation of penumbras with shadow maps?

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

## Recommended Posts

Recently, I've been doing some reading around on shadow mapping, I found that one characteristic of them is that they can have a flexible blur factor (which I found looks really crappy if you don't have much multisampling on the map, or a low-res map) determined on a per-light basis. So, if we can do that, how come no one (that I have seen) has done any simulations of penumbra wedges with shadow maps? It would be possible to determine the blur factor on a perpixel basis by multiplying the light size, distance from shadow caster to shadow receiver, and the inverse of the distance from light to caster. Of course, outside any casters (read: distance from caster to receiver = 0) the blur factor would not be correct, but I don't think a lot of people would be able to notice it as the pixel shader would end up doing a tight sampling of a lit area, resulting in no artifacts.

##### Share on other sites
Maybe I've missed something, but how would this be superior to a typical spotlight algorithm?

##### Share on other sites
Promit> what do you mean by spotlights? maybe I'm thinking about something totally different for "spotlights", but I'm not sure it has anything to do with what Cypher19 described. I think he's talking about penumbre wedges for real soft shadows, the amount of blur (so indirectly, the size of the penumbra) to be applied depending on the distance from the shadowed surface to the shadow caster (shadow caster -> the depth stored in the shadow map), and on the light radius and distance.
but maybe I misunderstood too...

##### Share on other sites
sBibi has got it right: I whipped up a variation that works in the DX9SDK Summer 2004 shadowmap example, that might help understand it a bit better:

(early in the .fx file)

#define LIGHT_SIZE 0.05f

and here's the code in the actual shadowing algo, from the pixscene function:

// insert pre-sampling work here	// Determine the blur factor (for realistic penumbra wedges)	// vPosLight.z = distance from light	float Distance = tex2D(g_samShadow, ShadowTexC) + SHADOW_EPSILON; // reminder: g_samshadow is a sample for the shadowmap, shadowtexC is the texture coordinates to look up for the pixel being rendered, and shadow epsilon is a bias to reduce shadow map artifacts	float BlurFactor = ((vPosLight.z - Distance) * LIGHT_SIZE) / Distance; // I'll provide a visual image of this later	float SampleWidth = (BlurFactor/SMAP_SIZE);	//read in bilerp stamp, doing the shadow checks        float sourcevals[4];	float DepthMin = vPosLight.z / vPosLight.w; // this is just to eliminate a glaring inefficiency in the original pixel shader	float LerpVal; 	// read in the values normally, in the cross formation        sourcevals[0] = (tex2D( g_samShadow, ShadowTexC + float2(0, -SampleWidth) ) + SHADOW_EPSILON < DepthMin)? 0.0f: 1.0f;          sourcevals[1] = (tex2D( g_samShadow, ShadowTexC + float2(SampleWidth, 0) ) + SHADOW_EPSILON < DepthMin)? 0.0f: 1.0f;          sourcevals[2] = (tex2D( g_samShadow, ShadowTexC + float2(0, SampleWidth) ) + SHADOW_EPSILON < DepthMin)? 0.0f: 1.0f;          sourcevals[3] = (tex2D( g_samShadow, ShadowTexC + float2(-SampleWidth, 0) ) + SHADOW_EPSILON < DepthMin)? 0.0f: 1.0f;  // tighten the samplewidth, rinse, and repeat.// note the +=!	SampleWidth/=2;        sourcevals[0] += (tex2D( g_samShadow, ShadowTexC + float2(0, -SampleWidth) ) + SHADOW_EPSILON < DepthMin)? 0.0f: 1.0f;          sourcevals[1] += (tex2D( g_samShadow, ShadowTexC + float2(SampleWidth, 0) ) + SHADOW_EPSILON < DepthMin)? 0.0f: 1.0f;   etc.

Afterwards, lerp the sourcevals together, then divide the light amount by the number of times you do the cross sampling.

Btw, right now I can't provide many screenshots, because I don't have a PS2.0 compatible video card, so moving around in the demo is tricky (hooray for ref drivers!)

Edit: I'll have the pics posted around 10PM MST (I'd complain about photobucket, but it's hard to complain about free stuff...)

[Edited by - Cypher19 on October 5, 2004 3:39:53 PM]

##### Share on other sites
nice to see someone is finally implementing this. I had a thought about it too some time ago, but didn't bother implementing anything since I'm currently re-doing my whole lighting/shadowing pipeline.
looking forward to see your screenshots... :)

EDIT: btw, this trick will be very likely to produce nasty artifacts when multiple shadowed objects are next to each other, as just increasing the samples spacing in the shadow map will sample shadows from the other objects, and count them in the final blurred color... and even if it should be taken into account, you'll miss lots of information in very blurred shadows, due to the large sample spacing. dunno if this is too noticeable though... probably not...

EDIT2: mmh, didn't notive the sample width tightening in your shader snippet, this will solve some of the problems, and the number of refinements could be done dynamically on sm3.0, using loops.

[Edited by - sBibi on October 5, 2004 4:22:32 PM]

##### Share on other sites
Actually, I could easily rewrite the sampling using loops since with 16 samplings the instruction count got, apparently, too high for SM2.0, so I had to tell the effect to recompile in SM3.0.

Btw, I know that you realized your mistake in thinking that blurry shadows won't look right, but I was actually extremely surprised when I found that they look a bit better than okay (they still look kinda weird upclose though because of the low shadow map resolution).

##### Share on other sites

These pics show the algo off in action, with different light sizes:

Light size 0.00

Light size 0.05

Light size 0.15

Light size 0.25

Light size 0.35

Light size 1.00

They do show off how the light blurs, and if you see the arrow's shadow on the right blue arch or the earth at the bottom, you'll see different levels of blurriness.

You'll also notice that a light size of 1.00 still has a little bit of a shadow, but you can see that the shadows start to look a little glitchy. I'm not sure what's causing this exactly, but I think a light of the size would be fairly unrealistic for a gaming scenario, since in real life that light would be, like, the size of half a wall in a room.

Also, here's how the blur factor math was derived:

a = Light Size
c = Distance
c1 = Distance from receiver to caster
a1 = final blur factor

Because of similar triangles, we know that a/c = a1/c1, therefore to isolate a1, we get a*c1/c.

##### Share on other sites
That algo is pretty cool. And the math was enlightening.

##### Share on other sites
Very nice! Will there be a demo soon? Do you have an example using omni lights?

##### Share on other sites
nice to see it work, looks really cool...

Quote:
 but you can see that the shadows start to look a little glitchy. I'm not sure what's causing this exactly, but I think a light of the size would be fairly unrealistic for a gaming scenario, since in real life that light would be, like, the size of half a wall in a room.

not necessarily unrealistic in a gaming scenario, it could be used as a cheap way to fake area lights...
and about these glitches, have you tryed with hw pcf to see if it improves them? (or non hw if you have an ATI) looks like they're here because of the shadow map aliasing, using pcf could might them quite a bit.

also, this is with normal uniform shadow maps right? I haven't thought about it yet, but it will very likely break down with non uniform maps like PSMs or TSMs, although there certainly is a way around it...
btw, I have a GF6800 right here, if you want I can test your demo to see the performance of the algo on this hardware.

• ### Game Developer Survey

We are looking for qualified game developers to participate in a 10-minute online survey. Qualified participants will be offered a \$15 incentive for your time and insights. Click here to start!

• 15
• 22
• 17
• 46