Shadows

You may need to read the articles about cubemaps and framebuffers before this one.


Shadow Maps

The most common way to rasterize shadows is with shadow maps, which are textures that contain depth data from the point of view of a light source. A shadow map can be created by rendering the scene to a framebuffer with a depth attachment from the point of view of the light source. The fragment's depth is stored in the first (r) channel of the depth texture.

Any surface that falls within the frustum of the projection but has a lower depth value than the shadow map at that position is in shadow. For example:

#version 300 es

precision highp float;

in vec2 v_texcoord;
in vec4 v_projectedTexcoord;

uniform vec4 u_color;
uniform sampler2D u_texture;
uniform sampler2D u_projectedTexture;

out vec4 outColor;

void main() {
	vec3 projectedTexcoord = v_projectedTexcoord.xyz / v_projectedTexcoord.w;
	float depth = projectedTexcoord.z;

	bool inRange = projectedTexcoord.x >= 0.0
		&& projectedTexcoord.x <= 1.0
		&& projectedTexcoord.y >= 0.0
		&& projectedTexcoord.y <= 1.0;

	float projectedDepth = texture(u_projectedTexture, projectedTexcoord.xy).r;
	float shadowLight = inRange && projectedDepth <= depth ? 0.0 : 1.0;

	vec4 textureColor = texture(u_texture, v_texcoord) * u_color;
	outColor = vec4(textureColor.rgb * shadowLight, textureColor.a);
}

Shadow Acne

The strange patterns in the example above are called shadow acne and are a result of the limited resolution of the shadow map causing surfaces to shade themselves. It can be mitigated by adding a bias to the calculated depth of each fragment. For example:

float depth = projectedTexcoord.z + u_bias;

The bias required to remove shadow acne from a surface depends on the angle from the light source to that surface, so varying the bias value based on that angle can improve the appearance of the scene. For example:

float bias = max(
	u_biasFactor * (1.0 - dot(v_normal, u_lightDirection)),
	u_biasMinimum);

Adding a bias can lead to an effect called peter panning in which shadows appear to be slightly disjointed from their caster. This can be mitigated by culling front faces when making the shadow map.


This article is a work-in-progress. In the meantime, consider reading these other articles about the same topic:


The next article is about fog.