KHR_materials_volume: Absorption and Thickness in glTF

The KHR_materials_volume extension in glTF enables realistic rendering of transparent and translucent objects by turning a polygonal shell into a virtual solid volume. This article examines how the extension defines physical thickness and light absorption, detailing the parameters used to calculate ray travel distance and the exponential decay of light through tinted media such as colored glass, liquids, and gems.

Thickness in KHR_materials_volume

In traditional real-time rendering, transmissive surfaces (enabled via KHR_materials_transmission) are treated as infinitely thin planes. The KHR_materials_volume extension addresses this by assigning physical depth to surfaces using two key properties:

  • thicknessFactor: A scalar floating-point value that defines the thickness of the volume in the mesh's local coordinate space (measured in meters). A value of 0.0 represents an infinitely thin boundary, which disables volumetric calculations.
  • thicknessTexture: A 2D texture map where thickness variations are stored (typically in the green channel). When provided, the sampled texture value is multiplied by the thicknessFactor to allow localized variations across the surface, such as the thin rim and thick base of a wine glass.

During rendering, this thickness parameter approximates the distance a refracted light ray travels through the object along the surface normal. When combined with the index of refraction (defined in KHR_materials_ior), shaders can calculate the actual path length of light through the interior without requiring multi-pass depth-peeling or complex ray marching.

Absorption and Attenuation Properties

Once light enters a volume, its intensity and spectrum change depending on the interior medium. KHR_materials_volume models this behavior via the Beer-Lambert law, which dictates that light absorbs exponentially relative to the distance traveled. The absorption profile is parameterized through two attributes:

  • attenuationColor: An RGB vector representing the linear color of light after it has traveled a specific distance through the volume. The default value is [1.0, 1.0, 1.0], indicating no color tint or selective wavelength absorption.
  • attenuationDistance: A positive floating-point value indicating the distance (in meters) light must travel through the material for its color to reach the exact value defined by attenuationColor. If set to infinity (or left unspecified as +Infinity), light passes through without any absorption.

From these parameters, renderers compute the volumetric absorption coefficient (\(\sigma_a\)) per color channel:

\[\sigma_a = -\frac{\ln(\text{attenuationColor})}{\text{attenuationDistance}}\]

Combining Thickness and Absorption

To render the final transmitted color, the engine determines the path length (\(d\)) of the light ray through the volume, scaled by the surface thickness and refracted angle. The transmitted light intensity (\(I\)) is then derived exponentially:

\[I = I_0 \cdot e^{-\sigma_a \cdot d}\]

Thicker regions of a mesh naturally absorb more light and exhibit a deeper, more saturated tint, while thinner edges appear clearer and closer to the original light source color. By decoupling the absolute color (attenuationColor) from the scale of the object (attenuationDistance), the extension ensures physically accurate and scale-consistent results across different lighting environments.