~/imallett (Ian Mallett)

Introduction

David Immel et al. and James "Jim" Kajiya simultaneously introduced the rendering equation into graphics in 1986. All of the online formulations I have found have notation I found unclear or were insufficiently explained (update: Wikipedia's is almost okay in the former regard). This page is my attempt to understand it myself, explain it properly, and show how it can be adapted to Monte Carlo methods.

My formulation is somewhat different from others'. Also, for simplicity, \(\lambda\), wavelength, and \(t\), time, and subsurface effects are not included in this formulation. My notation will be somewhat more verbose, but this aims to make it more clear.

The Rendering Equation

First, you have to know what the rendering equation does. Simply put, the rendering equation defines how much light leaves from any point in a 3D scene. Most surfaces simply reflect light, but others (also) emit it.

Putting this into more mathematical language: The light exiting any point in a particular direction is the sum of the amount of light it emits in that direction and the amount of light it reflects in that direction from any incoming light.\[ \vec{L}(\vec{x},\vec{\omega}_o) = \left[ \left[ \int_{\Omega_{\vec{x},\vec{N}_{\vec{x}}}} \vec{L}(\vec{y}_i,\,-\vec{\omega}_i) \left| \vec{N}_{\vec{x}} \cdot \vec{\omega}_i \right| \vec{f}_r(\vec{x},\vec{\omega}_i,\vec{\omega}_o) \, d\,\vec{\omega}_i \right] + \overrightarrow{Em}(\vec{x},\vec{\omega}_o) \right] \frac{1}{\left|\vec{N}_{\vec{x}}\cdot\vec{\omega}_o\right|} \]This is the monster. I have formulated it to read as much like an English sentence as possible, but have also tried to keep the notation as similar to existing sources as possible (except where doing so would be confusing) and to make it as complete as possible. Let's try to understand it. Here's a list of (most of) the symbols:

  • \(\vec{x}\) is the position we're considering.
  • \(\vec{N}_{\vec{x}}\) is the surface normal at \(\vec{x}\).
  • \(\Omega_{\vec{x},\vec{N}_{\vec{x}}}\) is the set of all possible rays on a unit hemisphere centered on \(\vec{x}\) and around the surface normal \(\vec{N}_{\vec{x}}\). In general, this is a sphere, but for our purposes, we restrict it.
  • \(\vec{\omega}_i\) is a ray direction in \(\Omega_{\vec{x},\vec{N}_{\vec{x}}}\).
  • \(\vec{\omega}_o\) is the outgoing ray direction, also in \(\Omega_{\vec{x},\vec{N}_{\vec{x}}}\).
  • \(\vec{f}_r\) is the bidirectional reflectance distribution function (BRDF).

\(\vec{L}(\vec{x},\vec{\omega}_o)\) is the quantity we're trying to calculate. Read it as: the light coming from \(\vec{x}\) along vector \(\vec{\omega}_o\).

Description

The integration part of the formula actually does a very simple thing. From every direction \(\vec{\omega}_i\) you could look from \(\vec{x}\) (it's a hemisphere of directions, \(\Omega_{\vec{x},\vec{N}_{\vec{x}}}\)), the integral finds the light coming in from each of those directions, calculates the reflection (if any) of that light along the outgoing direction, and then sums it all up.

Say you look in direction \(\vec{\omega}_i\) from \(\vec{x}\). To simplify things, suppose any direction you look, you see a point. We'll call this point \(\vec{y}_i\). We need the amount of light coming from direction \(\vec{\omega}_i\), so we need the amount of light coming from \(\vec{y}_i\) along \(\vec{\omega}_i\). The function that does that is exactly the function that we're defining—a function that returns the amount of light from a point along a direction! Therefore, we can use recursion and get the term \(L(\vec{y}_i,\,-\vec{\omega}_i)\). Read that as: the incoming light from direction \(\vec{\omega}_i\).

Notice that \(\vec{\omega}_i\) is pointing from \(\vec{x}\) to \(\vec{y}_i\)—opposite the direction of the incoming light. This is done because \(\vec{\omega}_i\) is most intuitively pulled from a hemisphere centered around \(\vec{N}_{\vec{x}}\); that hemisphere naturally describes the set of directions you could travel from \(\vec{x}\). Just remember that the incoming light propagates along \(-\vec{\omega}_i\).

The term \(\vec{N}_{\vec{x}}\cdot\vec{\omega}_i\) accounts for the attenuation of incoming light due to falling on an angled surface. On Earth, the seasons are mostly caused by the Earth's tilt—a beam of light from the sun is spread out over more area in the winter, since the planet tilts away. In the summer, light hits perpendicularly, causing more energy to land in a given area (see inset).

This kind of attenuation is exactly what this dot product term does. Note that here this term must be nonnegative since \(\vec{\omega}_i\) is from a hemisphere centered around \(\vec{N}_{\vec{x}}\). However, it is still written in an absolute value, since in general it could be negative (for example if \(\vec{\omega}_i\) instead came from a sphere).

The term \(\vec{f}_r(\vec{x},\vec{\omega}_i,\vec{\omega}_o)\) is the BRDF of the surface at point \(\vec{x}\). This function says what proportion of light coming from direction \(\vec{\omega}_i\) is reflected in direction \(\vec{\omega}_o\). It is vector-valued because each channel of light gets its own proportion. This can be one channel for every frequency, as it is in real life, or some basis for the color space (e.g. red, green, and blue channels).

The \(\overrightarrow{Em}(\vec{x},\vec{\omega}_o)\) term is the amount of light that \(\vec{x}\) emits along direction \(\vec{\omega}_o\), the output direction. It will be \(\vec{0}\) for most surfaces, and some nonzero vector for lights.

The whole thing is then divided by \(\vec{N}_{\vec{x}}\cdot\vec{\omega}_o\). The purpose of this term is very similar to the other dot product term, and is also nonnegative (here) for the same reason. Now that the light is leaving the surface along \(\vec{\omega}_o\), the same kind of attenuation (now due to the outgoing ray's angle) applies (it's divided since the light is leaving, not arriving).

This last point is the major difference between myself and other authors. Others include that last reciprocal term implicitly in the BRDF function and the emission term. There are some good reasons for this:

  • It causes the BRDF to have the nice property of being truly bidirectional in the sense of Helmholtz Reciprocity. Reciprocity allows us to reverse all the rays in the calculation without affecting the answer. There are various (easy) ways to get the same effect, such as simply multiplying the term back in, but technically, the BRDF in my formulation isn't actually natively bidirectional in the same sense.
  • It might be more intuitive to represent the BRDF in terms of the projection. Take the diffuse BRDF: it has a constant intensity no matter with direction you look. That this happens because the light is being emitted according to a cosine distribution that is then later canceled out by the reciprocal projection term is not necessarily helpful.
  • It simplifies calculations a lot. Believe me. I've tried.
The key point is this: now that you know that the BRDF "actually" has a projection term in it implicitly, you can then use the implicitly-multiplied version while still knowing what you're doing.

Note: other content was previously on this page showing (among other things) how to do Monte Carlo integration and how to derive the diffuse BRDF from Lambert's Cosine Law. These have been moved into other tutorials, which you can browse here.


COMMENTS
Ian Mallett - Contact -
Donate
- 2018 - Creative Commons License