Lighting is Density

This is the first of several “educational” posts that I wanted to write.  The target audience is somebody who’s studied this stuff before but doesn’t quite “get it”.

We like to think of lighting calculations as dealing with the amount of light that is leaving a surface, but this is actually quite wrong. In this post, I hope to break down the fundamentals of lighting in a way that I hope will give the reader the correct intuition.


To begin with, we need to understand exactly what we’re calculating.  There are several important quantities in lighting things get confusing if you don’t keep them straight.

Flux, or radiant power, is essentially the number of photons per unit time moving into or out of a surface. This is probably the thing we intuitively think of when we think about “amount of light” but it is almost never the thing we actually want to calculate.

Irradiance, is flux per unit area on a surface. This might be called “flux density” (and often is).

Solid Angle is surface area on the unit sphere. That’s it. We often visualize a solid angle as a little cone centered around a direction of interest, but it’s actually a measure of the surface area on the unit sphere that intersects this cone. Solid angle has its own SI unit, the steradian, but it may be helpful to replace “steradian” with “unit area on the sphere”, because that’s all it is.

Radiant intensity is the density of flux over solid angle. It is usually associated with a particular direction, either incoming, or outgoing. I like to picture it in 2D, and tell myself that it’s just “photons per unit pie slice”. It’s a density, just like irradiance, but its a density over a different domain, so it gets a different name.

Radiance is flux per unit projected area, per unit solid angle.  This one’s a bit confusing, but you think think of it as “directional irradiance.”   It is the contribution of a particular set of directions to the irradiance at a surface.

Point Lights

There is no such thing as a point light source, but that doesn’t stop us from pretending. In graphics, we’ll typically fill our scenes with little magical singularities spewing radiation in all directions. They’re cheap to compute, easy to understand, and also a great little thought experiment for understanding the different quantities above.

Suppose we have a point light emitting flux P watts, and its shining those P watts uniformly across the unit sphere of directions. The area of a unit sphere is 4\pi, which means that the intensity (flux per solid angle) over any part of the sphere, is given by:


Now let’s imagine we have a surface and we want to know the irradiance at that surface.Remember that irradiance is just flux per unit area. Lets consider a particular subset of the unit sphere around our point light. If the area (solid angle) of this subset is w, then the flux passing through it is wLi. In the figure below, this is the amount of energy flowing from the light between the two lines.


If we move the surface further away from the light, we see that the same amount of flux is now spread over a wider area on the surface, which means that the irradiance at any point on the surface is reduced.


A similar thing happens if the surface is tilted.


If it’s confusing, just keep reminding yourself that you’re trying to measure density.

I’m going to skip a whole bunch of trigonometric hand-waving and just tell you that, when you take all these factors into account, it turns out the irradiance at any point is:

 I = \frac{L_icos(\theta)}{r^2} watts per unit area.

That looks familiar.  It turns out that irradiance is the thing that we know of as diffuse lighting, and light color is more precisely light radiant intensity

Directional Lights

There is no such thing as a directional light. Directional lights are what we use when we want to light something by the sun, then realize how much floating-point pain we’d suffer if we tried to simulate a really bright point light 93 million miles away. We just pretend that all the light is coming from the same direction, and ignore falloff, since at the scales we’re dealing with these things are negligible anyway. The irradiance from a directional light is:

I = L_icos(\theta) watts per unit area.

For a directional light: light color is, again light radiant intensity

What Rendering Does

Consider an arbitrary hunk of surface, and imagine that it has uniform brightness. Suppose we are rendering an image of this itty-bitty hunk of surface. The projection of this itty-bitty hunk covers a particular area (solid angle) on our image plane. If it helps, you can think of this little hunk as the area under a pixel, as shown:


We are trying to compute the radiant intensity that the observer measures over the projection of our itty-bitty hunk of surface. Put another way, it is the number of photons arriving from the green rectangle, divided by the area of the red rectangle. Note that the intensity does not change with distance. If we hold the red rectangle fixed and move the triangle further away, then the size of the green rectangle will increase, such that the intensity remains constant.  If we hold the green rectangle fixed and move the triangle, the size of the red rectangle will decrease, and again, the intensity will be constant.

We do not compute amount of light in rendering, we compute intensity of light, which is another way of saying density of light.

If you have ever been puzzled by physically based shading, and wondered why a normalized spec lobe can turn a light color of x into a specular response of 1000x, this is the reason. If the incoming density is x watts per steradian, and the size of the specular lobe is 0.001 steradians, then a density of 1000 is the only way we can guarantee that all the energy that came in ends up going out.

Given a point at the center of our surface hunk, we’ll to compute the reflected intensity from that point, and use it as an approximation for the whole hunk. Let’s assume we have just a point light. We’ll write:

 L_o(\omega_o) = f(\omega_o,\omega_i)I

What this says is that an observer viewing the surface from direction \omega_o will observe a radiant intensity of L_o watts per unit solid angle. Alternatively, it says that the Irradiance on the image plane is L_o watts per unit area. What we’ve got is the irradiance at our surface, multiplied by the BRDF, which is a function that determines how much of the energy from one direction is scattered into another direction. The BRDF is defined as the ratio of reflected intensity (radiance) to irradiance:


where I(\omega_i) is the irradiance on the surface due to light arriving from direction \omega_i
If we expand the irradiance, and assume a simple diffuse BRDF f(\omega_o,\omega_i)=k_d, we have something that we should all recognize from graphics class:

L_o(w_o) = k_d L_i\frac{cos(\theta)}{r^2}

The Rendering Equation

In the general case, taking into account multiple bounces, area sources, and all that good stuff, our graphics textbooks tell us that the value we want to compute is given by the rendering equation:

L_o(\omega_o) = L_e + \int_\Omega L_i(\omega_i) f(\omega_o,\omega_i) cos(\theta_i) \,d\omega

There is light arriving at the surface from every direction, and the contribution of any particular direction depends on the product of the BRDF, and the contribution that this direction makes to the surface irradiance.

The terms are these:
L_e: Radiant intensity of emitted light (for a light source).
L_o(\omega_o): Radiant intensity leaving through a particular solid angle centered around direction \omega_o
L_i(\omega_i): Radiant intensity arriving through a particular solid angle centered around direction \omega_i
cos(\theta_i): Cosine of angle between the surface normal and the incoming light direction.
f(\omega_o,\omega_i): The BRDF.

Monte Carlo Integration

At this point, you might be wondering what we do next? How the heck do we evaluate this integral? It doesn’t look like any integration problem that we ever solved in calculus class? How do you integrate something without knowing its definition? It turns out that an integral is nothing more than the limit of the average. If you could somehow take infinity samples of the incoming lighting and average them all together, you could solve the integration problem.

Monte Carlo integration is a numerical integration technique which does exactly this. We use the fact that:

\int f(x)\,dx \approx \frac{M}{n} \displaystyle\sum_{i=0}^{n} f(x_i)

In english, the integral is well approximated by the average of a set of samples, multiplied by the measure of the domain. Measure is just a fancy mathematical word for length, area, volume, and such like, depending on how many dimensions you’re dealing with.

Suppose we want to solve the following calculus problem:

\int\limits_{x=1.1}^{2.2} 3x^2 + 2x \,dx

Since we know the function we’re integrating, we could use elementary calculus and learn that the answer is:

 F(2.2) - F(1.1) = 2.2^3 + 2.2^2 - 1.1^3 - 1.1^2 = 12.947

But we can also apply monte-carlo integration. Here are a few lines of C code which give us approximately the same answer:

#include <stdio.h>
#include <stdlib.h>
#define N 100000
double f ( double x ) {return 3*x*x + 2*x; }
int main()
    double a = 1.1, b=2.2, sum=0;
    for( int i=0; i<N; i++ )
        sum += f( a + (b-a)*(rand()/(double)RAND_MAX) );
    printf("Answer is: %f\n", sum*((b-a)/N) );
    return 0;

The cool thing about Monte Carlo is that we can use it to integrate anything, as long as we have the ability to take point samples. For light transport, we integrate over the unit hemisphere above our shading point, which means our samples are ray directions, and our measure is 2\pi. We just do this:

L_o = \frac{2\pi}{N} \displaystyle\sum_{i=0}^{N} L_i(\omega_i)f(\omega_o,\omega_i)cos(\theta_i)

That’s it. You now have everything you need to know to write a terrible path tracer.



One Comment

  1. Sakib Saikia

    Great article. I like how you have skipped some of the derivations to keep the article concise and engaging. I’ve read these concepts several times elsewhere but have found myself being confused, mainly because of the explanations either being too lengthy or too many equations thrown at me. I can now say that I “get it” 🙂

Comments are closed.