To get shadows in a 3d scene you have to render it in a prepass from the point of view of the light source (eg. the sun.) This will give you depth texture that contains everything that is directly visible from the light source. When you render the main pass you need to sample that texture for every pixel you are rendering. If the pixel you are rendering is not visible from the point of view of the light source, it is in the shadow and vice versa.

To be able to map the pixel you are rendering you need to pass the world position from the vertex shader to the fragment shader. By default those vec3 values get interpolated for every triangle you render. That means you also get the interpolated world position for every pixel. When you have the world position you multiply it with the light source’s view projection matrix:

    vec4 world_pos_light_space = shadowcaster_vp * vec4(world_pos, 1);
    world_pos_light_space.xyz /= world_pos_light_space.w; // Perform the perspective division

This gives you the distance from the light source in the z value. To compare it with the actual shadow map you first need to sample it:

	float depth_light_space = world_pos_light_space.z;
    // get the position in the depth texture we should compare to.
	shadow_map_coords.x = world_pos_light_space.x / 2.0 + 0.5f;
	shadow_map_coords.y = -world_pos_light_space.y / 2.0 + 0.5f;

Be aware that the shadow map can have different coordinates (-1 to 1 or 0,-1) and the Y-axis is sometimes flipped.

Shadow acne

To prevent surfaces casting shadows upon themself and getting weird artefacts, you can apply a bias to compare wether the surface is in the shadow:

    // bias can be constant 0.0001 or some function that takes into account the angle of the sun ray.
    if (depth_light_space - bias > shadow_map_depth) {
        // in shadow
    }
 

Percentage Closer Filtering

A shadow map has a resolution, and that means there will be limited detail availble for determining whether pixels are in the shadow or not. Percentage Closer Filtering is a technique to soften the hard edges if you have lower resolution available. Instead of only sampling the current pixel, you sample the surrounding pixels as well. You then take the percentage of samples that are in the shadow and darken by that percentage. For edges of a shadow that means they will be more gradual.