Tuesday, June 30, 2009

Light Prepass Cascaded Shadow Mapping, continued

Ah... shadow map filtering, I didn't know before that there's a lot of them. (The screenshot were using a 512x512 resolution shadow map to easy compare the various filtering I implemented.) In my older screenshots, I'm using the 5x5 PCF (middle) ( short for Percentage Closer Filtering) which, of course better in smoothing out the shadows but the heaviest of all implementation I did. The 4 Tap PCF (left) is the fastest but the ugliest, not very nice if your shadow map resolution is low. I also tried Gaussian blur and random filters(not in screenshot). I also tried non PCFiltering like Variance Shadow mapping but considering I'm doing Cascaded Shadow Mapping, VSM has a bigger memory appetite considering it needs two channels to store depth and depth*depth. Which brings me to my own implementation.
Similar to what I've been doing in the past, and sometime good at it... I dub or name stuffs. I dubbed my implementation as 8 Tap O-PCF or Occasional-PCF. It may sound funny but thats only half of the point. My real point or reason why I name such is because how it self-optimized itself. Let me explain: (inhaled intensely)
The square first 4 tap of 8 will have enough information to proceed with the other 4 taps. By dissecting the texel into 3x3, the center is literally ignored due to the fact that the size and amount of the texels sampled are enough already. Sampling is basically 1/3 of the texel around the texel and should not go outside the texel. The wonderful thing about this is that I'm only sampling 8 times but only the first 4 if shadow test failed. If you closely examine its inner penumbra of the shadow, you'll notice the smoothness of the penumbra, almost to that point it appears to be using a higher shadow map resolution. Of course the outer penumbra will still be jagged but hey considering the 3 tones I added, and the edge are lightest, with a good Depth of Field, this will even look like 2048x2048 shadows. I like the flexibility of this filtering, much so that it can fake shadow bleeding already, beating the soft PCFs in terms of speed and control. So there you have it 8 Tap O-PCF... sounds like those nifty items in Monkey Island like Mug O'Grog or Spit O'Matic. Just say, 8-Tap-O'Pac-eF.... (windblowin tumble weed) ...nah that was corny. Ha!
I wouldn't say I'm finish with this topic... cause definitely, I'll be revisiting this when the renderer is formalized. Next stop, Dual Paraboloid Shadow Mapping for our indoor and outdoor shadowing daily needs.

Wednesday, June 24, 2009

Light Prepass Cascaded Shadow Mapping

Updates, updates, updates (actually, it's only an update. Singular). The image you see above is Light Prepass rendering with Cascaded Shadow Mapping. Each of the color changes in the image above represents the splits of the CSSM based on distance from the camera.

I first implemented the Nvidia's CSSM implementation (which I intentionally didn't post the results here because how I implemented it was too embarassing to show). I can't seem to stabilize that implementation. Then I tried Wolfgang Engel's [ShaderX5] CSSM, which I then simplify based from Michael Valiant's [ShaderX6]. Although, I didn't fully implementated exactly as he did. Primarily on MEC (minimal enclosing circle) part. I instead used a transformed-axis bounding box on each frustum split. With bounding box, I don't have to recompute the split even if I rotate or translate the camera. Depth can also be constant, depends on the requirements. But what I did is the just using the radius of the BB plus the length of the difference between center of BB and original light position. The center of the BB is the untransformed axis. I call it Camera-Frustum-Split Bound Depth. It's a just my fancy way of saying, 'getting enough' precision from an orthographic shadow kinda' thing. In simple terms, imagine a light tripod attached on top of a helmet beaming in a certain direction downward with a special gimbal ignoring panning, yawing and rolling of the head (rolling heads... o_O). The distance of the light from the eyes are constant. Though I think this is only applicable to outdoor sun type scene.

For the shadow view matrix, I preserved the light direction in the BB(transformed) space so whenever I turn or move the camera the depth and shadow view angle are constant.

I still have to work on the filtering and fading-into-the-next shadow split. The initial part I've done which is the transition of cascades are based from pixel depth(view space) distance vs the far distance of each split. My plan is to gradually fade in between splits. I choose this way because it make sense, atleast for me, that transitions are based from a field of view of the eye. I read somewhere, I can't remember where, that this also avoids shadow split transition popping.

Btw, I store the splits in per channel rather than a spliting a single channel texture. I don't know how this will affect my rendering or if this is better or worse, but good enough... for now.

Friday, June 12, 2009

Light Prepass Shadow Mapping

Ok, update on my Light Prepass journey. Shadow mapping is actually new to me, let alone ever using it in a deferred rendering. In fact this my first time to probably nail this thing to its head. Currently, its a simple shadow mapping... no magic.. no fancy footwork. The image you see here has a point light(w/ attenuation) casting shadow with 5x5 PCF (percent closer filtering). At this point, its all raw-brute-coding, definitely screaming for optimization. And obviously, I'm hiding all the shadow errors with a neat camera angle.

The model rendering however exhibits sound optimation. I've managed to remove all matrix computation in the pixel shader on the light passes (full screen quad and light convex mesh - I call these guys 'light blobs'). The mathematics here was such a nose bleed. The key here is making View Space your battle ground. Good thing the graphics gurus are around... MJP, Drilian, Engel, etc. Here's the link.

In terms of prepass packing, specifically the normals, the Cryengine 3 suggestion seem to produce some inaccuracies when I error check it with the fresh untouch normals. I added a weird value just to remove the error. Here's the code.

float4 PackDepthNormal(in float z, in float3 normal)
float4 output; normal = normalize(normal);
normal.x *= 1.000000000000001f; //<--- my nasty mod
output.xy = normalize(normal.xy) * sqrt(normal.z * .5f + .5f);
return PackDepth(output, z);

Anyway, I need to investigate this further. I find Pat Wilson's idea on converting normals to spherical coord better. I haven't profiled this but this seem to be a more optimized approach.

Back to shadows, my target is to use Cascaded Shadow Maps. Hopefully, in a few days time I can post the results.