Skip to content

Lights In VEX

This is a simple concept that has been utilized in a few big VFX productions where we wanted to avoid expensive light rendering. Imagine lightning in the clouds or hundreds of street/neon lights in a vast city. Such scenarios could become very heavy for the renderer. While baking light into a texture is not a new idea, how can we bake light into a volume?

vex-light-1

Math and VEX to the rescue! The idea is quite simple - we do exactly the same math as the renderer but using our VEX skills.

Light Decay

First, we want to calculate light decay. Depending on the medium, we can apply different techniques here. The most popular would be to calculate light decay using the (Inverse-square law](https://en.wikipedia.org/wiki/Inverse-square_law).

The inverse-square law states that the intensity of light radiating from a point source is inversely proportional to the square of the distance from the source. The formula is: $$ \Huge\ I = \frac{P}{d^2} $$ where $I$ is the intensity, $P$ is the power of the light, and $d$ is the distance from the light source.

VEXpression
light_decay = (1 / (light_dist*light_dist))*light_power;

Another popular decay model we might want to apply here is the Beer-Lambert law , which describes how light is absorbed and scattered as it travels through a medium.

The Beer-Lambert law is given by: $$ \Huge\ I = I_0 \cdot e^{-\alpha d} $$

where $I$ is the transmitted intensity, $I_0$ is the initial intensity, $\alpha$ is the absorption coefficient, and $d$ is the distance through the medium.

Shading

When light hits a surface, it gets reflected. The way light gets reflected depends on the surface type itself. The simplest reflectance calculation is Lambertian reflectance , which defines an ideal "matte" or diffusely reflecting surface.

The Lambertian reflectance is given by:

$$ \Huge\ L \cdot N = |N||L| \cos(\alpha) = \cos(\alpha) $$

where $L$ is the direction of the incoming light, $N$ is the normal to the surface, and $\alpha$ is the angle between $L$ and $N$.

VEXpression
lambert = max(0, dot(normalize(v@N),normalize(light_dir)));

Light accumulation

Once we calculate everything, we should accumulate our light and apply color.

VEXpression
v@lout += light_color*(light_decay/no_lights)*lambert;

vex-light-2

Calculating light for the volume is even simpler as we can skip reflectance model entirely!

vex-light-3

WARNING: This provided scene file is not production ready! - Its not optimised (it is a brute-force loop over every point/light which also makes light accumulation not very precise) in a scene and it is just a prove of concept.