Kévin Boulanger

High-Frequency Shadows for Real-Time Rendering of Trees

Tree with shadows Tree with shadows Tree with shadows

We present a fast and simple method for adding high-frequency shadows into the foliage of trees rendered in real-time (click on the thumbnails above for full-size images). When leaves of a tree project shadows onto other leaves, determining the relationships between cast shadows and the corresponding occluders is a visually difficult task. We present a method based on this assumption to quickly determine shadows cast by leaves onto other leaves. To this end, we simulate the presence of these shadows rather than projecting them exactly. The characteristics of these simulated shadows (movement, parallax, size, softness and color) evolve realistically when the lighting conditions change. Our method is fast and supports soft shadows.

The following images show a comparison between renderings without and with high-frequency shadows. Both images feature low-frequency lighting, translucency and indirect lighting. Click on the images to see the full-size renderings.

Tree without shadows Tree with shadows

We represent a tree by a set of branches and leaves, where each leaf of the foliage is modeled with a semi-transparent textured quadrilateral. Using assumptions of uniform density of the foliage and constant leaf area, we determine the shadows only based on the distance from the leaves to the foliage border in the light source direction. For each leaf and any direction, we need the distance to the foliage border. We present a way to compute this parameter in our work on trees at this page.

Ray-shadow mask intersection

We base our method on a texture cube map, called shadow mask, surrounding each leaf and representing the set of neighbor leaves occluding the environment. This texture contains the visibility function for each possible incoming light direction (image above). In principle, one shadow mask should be defined for each single leaf, which is too memory demanding. Instead, we use a single generic mask for the whole tree (image below).

Shadow mask for the single layer method

Given a leaf of the tree, the number of neighbor leaves casting shadows varies with the sun direction, depending on the thickness of the leaf layer to be traversed. We would need a shadow mask for each possible thickness. Instead, we assign a gray level to each of the neighbor leaf in the shadow mask (proportional to the distance from the neighbor leaf to the current leaf), we then perform thresholding when rendering. The closer to the rendered leaf, the darker the gray level (image above). At rendering time, we compute the distance of the leaf to the foliage border in the sun direction. This distance becomes a threshold: every texel of the shadow mask greater than this threshold is set to 1, every texel equal to or below the threshold is set to 0, keeping the closest leaves ass occluders.

The following images show one of the face of the shadow mask (in grayscale) then four different thresholded versions of the same texture (click to zoom in). This method allows a virtually infinite number of shadow masks to be defined. Additionally, if the thresholding is done per texel (in the fragment shader), then any tree shape can be used at rendering time.

Thresholding of the shadow mask

The content of the shadow mask is procedurally generated with only the parameters of the leaf distribution and the opacity channel of the leaf texture. We generate a sphere containing uniformly distributed quadrilaterals with uniform distribution of normals and with a constant density, computed from the input tree mesh. We render the quadrilaterals inside the sphere with a constant gray level proportional to the distance from the sphere center. These quadrilaterals are semi-transparent, the opacity channel of the leaf texture determines which fragments are rendered. We set the background of the scene to white, corresponding to the absence of occluding leaves. We finally place six cameras at the center of the sphere to render the faces of the resulting shadow mask cube map.

Intersection with the shadow mask

At rendering time, for each rendered point of each leaf (P1 and P2 for example in the image above), we send rays in the light direction, we then intersect the sphere of the shadow mask (points A and C) and we finally use the PA and PC directions to access the shadow mask.

This approach works only with a constant distance r to the neighbor leaves. The resulting effect is a feeling of flatness of the leaf shadows when the sun direction changes, similar to the effect obtained by projecting light through a planar stencil with leaf-shaped holes onto a plane and when moving the light source. Using several spaced layered stencils results in a more three-dimensional look of the projected set of leaves.

We use a layered approach by partitioning the set of neighbor leaves into four concentric hollow spherical regions. We assign a shadow mask to each of these layers, we then perform the previous intersection approach independently for each layer, and we finally combine the values provided by the four shadow masks. The four shadow masks are stored in the RGBA of a single cube map texture (image below, click to zoom in).

Shadow mask for the multi layer method

The following four images represent the RGBA channels of one of the face of the cube (click to zoom in).

Channels of the shadow mask

We tested our method on a GeForce 9600 GT graphics card and 1920x1200 resolution, with 3 and 100 trees displayed at the same time and covering most of the viewport, with indirect lighting and shadows on the ground, and with HDR lighting. The rendering speed is mainly related to the number of texture accesses in the fragment shader, which offers a choice between quality and speed.

Shadowing method 3 trees 100 trees
Multi-layer shadow mask (RGBA)
143 fps
27 fps
Single layer shadow mask (grayscale)
189 fps
36 fps
No high-frequency shadows (but still low-frequency and indirect lighting)
226 fps
40 fps

The trees are modeled using the XFrog software.



Downloads coming soon.

If you have any comment, you can contact me at info@kevinboulanger.net