(Geometry shader option at the end. General concept stays the same. Not all platforms support geometry shaders yet and the vertex shader option might still be faster on some systems)
Mesh pre-processing:
- Copy the original mesh.
- Break/Split each vertex to be unique per poly.
- Set each vertex normals to the polygon's normal (making the mesh flat-shaded).
- insert collapsed quads (2 triangles, shown as the green line) inside every edges between the original triangles reusing each triangle's vertices (quick if the mesh is indexed, just add to the index array).
The normals on each side of the quad "edge" must match the triangle on each side (see the yellow normals in the picture).
Then, in the vertex shader (pseudo code, assuming all coordinates are in world space):
if (dot(light_normal, in.normal) <= 0){
out.position = in.position;
} else {
out.position = in.position + light_normal * volumetric_shadow_distance;
}
or the 1-line version (that may or may not be faster depending on GPU/drivers):
out.position = in.position + (light_normal * volumetric_shadow_distance) * max(0, sign(dot(light_normal, in.normal)));
This is for a directional light. For point & spot lights light_normal
= normal(in.position - light_position)
This will expand the shadow mesh away from your light source. It only requires vertex shader support.
If you have geometry shader support you can generate the edge quad geometry on the fly (skip step 4 above) by adding only 1 triangle per edge per source triangle in the geometry shader. The other source triangles will add the 2nd half of the quad for every edge (as long as they are oriented the same way: all CW or all CCW). You don't need full adjacency data, only the normal of the neighbor triangle for that edge's half-quad which you can put in the regular vertex data as a 2nd normal, a custom attribute, or stash it in the tangent, color, or 3D uv depending on the engine you use. Just make sure the 2 normals matches exactly (loses the same amount of precision) or you'll have visual glitches when only one half of the quad edge expands.