Skip navigation

I posted the following on the Card Kingdom development blog.

More Lightning

In a somewhat recent post, I showed off the initial stages of the lightning effect that I’ve been working on. One of the major hurtles that was tripping me up, and the reason why in the old video it is hard to actually see the lightning, was the blend states in DirectX 11. There is a flag in the blend state descriptor called IndependentBlendEnable. If this is set the false, all render targets that are bound only use the first render target blend descriptor. For many applications this works perfectly fine. For others, this breaks everything. Well, not break per-se, but gives unintended, unwanted, and incorrect results. So, the issue that I was running into was when rendering the depth render target, the blend mode was set to additive. It was trying to additively blend the depth with an already existing depth value or zero from the depth render target. The fix was to specify IndependentBlendEnable = true and define the depth render target to use an alpha blend to overwrite any value that was previously rendered.

Once this was fixed, I could move forward and test the new flashing and jitter functionality. The lightning can now flash any color and for a specified amount of time. Jitter is added in the geometry shader when the lines are “exploded” out into two intersecting planes and oscillates the points some amount over time. This gives the effect a more “alive” feel, as bolts no longer just sit in the air, waiting to fade out.

Here is a demonstration of the new lightning:

Shadows

The next thing I wanted to tackle was shadows. I already had crude shadow mapping implemented for spot lights and somewhat for direction lights before we went to GDC, but I was incredibly unhappy with the results and disabled it for the build. Every bad thing that could happen in shadow mapping was present in the old version: the shadows were aliased; they had horrible banding on near-parallel surfaces; objects close to a plane “Peter Panned” (“Peter Panning” is a term for a shadow that separates from an object when it’s close to or on a plane).

Now that there is more time, relatively speaking, I took up the task to re-implement shadow mapping. After going through online and book resources, I realized I wasn’t far off from a working solution; I just needed to tweek some values and properly setup my orthographic projection, among other things.

Since the game mainly takes place in a confined space and there isn’t much in the background going on, I do not need to implement anything fancy when it comes to shadow mapping. The only fancy aspect to the shadows is that I’m using PCF, percentage-closer filtering, soft shadows. PCF in DirectX 11 is quite nice as it has hardware support for doing the depth comparison by way of SampleCmp and SampleCmpLevelZero (which is the same as SampleCmp, but for MIP level 0 only). These functions do a comparison on the hardware and return either true or false, 1 or 0, if the comparison passes or fails, respectively. The calculation for the percent of a surface being shadowed or not then becomes simply this:

const float dx = 1.f / SHADOW_MAP_SIZE;
const float2 offsets[9] =
{
	float2( -dx, -dx ), float2( 0, -dx ), float2( dx, -dx ),
	float2( -dx,   0 ), float2( 0,   0 ), float2( dx,   0 ),
	float2( -dx,  dx ), float2( 0,  dx ), float2( dx,  dx )
};

float shadowPercent = 0;
[unroll]
for( int i = 0; i < 9; ++i ) {
	shadowPercent += shadowMap.SampleCmpLevelZero(
		shadowComparisonSampler,
		shadowTexturePosition.xy + offsets[i],
		shadowTexturePosition.z - DEPTH_BIAS
	);
}
shadowPercent /= 9.f;

A depth bias, defined as DEPTH_BIAS above, is something that needs to be tuned to your needs. This bias eliminates severe banding in the scene when set to a small value, but also causes “Peter Panning” if set too high. During my initial testing, here are the differences between not having a depth bias and having one.

Without a depth bias:

With a depth bias:

Once all the kinks were worked out, here are the resulting shadows: