Simon’s Graphics Blog

Projected Solid Angle Is Projected

· Read in about 3 min · (497 Words)
path tracing maths

There are two quantities flying around when writing a physically based renderer:

  • Irradiance, which is power per unit area
  • Radiance, which is power per unit area per unit projected solid angle

Any BSDF is the ratio between the two: a patch receives unit irradiance from some incident direction, the BSDF defines the radiance emitted for each outgoing direction.

At no point is (non-projected) solid angle used as a measure for the patch in question, yet many references and implementations consider probability density relative to this measure. In this post I will argue that using projected solid angle directly is more natural.

For example, it is common to see BSDF sampling written as:

$$\frac{1}{N}\sum^N_{j=1}{\frac{f(\omega, \omega_j)|\cos \theta_j|}{p(\omega_j)}}$$

Where $p$ is the probability density of $w_j$ with respect to solid angle. Ever wonder why that cosine is there? It’s to correct for the fact that you’re sampling with respect to solid angle, but the BSDF is defined in terms of projected solid angle.

So why am I making a fuss? Let’s look at the most common BSDF: Lambertian. This BSDF is a constant, and can be analytically importance sampled using a cosine wieghted hemisphere sampling scheme. For this scheme, $p^{cw}$ (cosine-weighted sampling pdf wrt solid angle) is defined as:

$$p^{cw}(\omega_j) = \frac{|\cos \theta_j|}{\pi}$$

Plugging this into our sampling scheme, we get the following:

$$\frac{1}{N}\sum^N_{j=1}{\frac{\pi f(\omega, \omega_j)|\cos \theta_j|}{|\cos \theta_j|}} = \frac{1}{N}\sum^N_{j=1}{\pi f(\omega, \omega_j)}$$

We spent time computing the cosine for the sampling scheme, and again for the sum itself, but these terms then cancelled out. This not only wastes computation time, but relies on these quantities cancelling perfectly even when close to zero, which can be problematic in floating-point arithmetic.

Can we do better? Yes, consider sampling with respect to projected solid angle directly. Let’s call this function $p_\perp$. It can be defined as follows:

$$p_\perp(\omega_j) = \frac{p(\omega_j)}{|\cos \theta_j|}$$

We can then adjust our sampling scheme to use this density function directly:

$$\frac{1}{N}\sum^N_{j=1}{\frac{f(\omega, \omega_j)|\cos \theta_j|}{p(\omega_j)}} = \frac{1}{N}\sum^N_{j=1}{\frac{f(\omega, \omega_j)}{p_\perp(\omega_j)}}$$

Hurrah, our cosine terms all cancelled out! In general you should only need to consider cosine terms when computing the form factor between patches (called the geometry term in the Veach formulation), for example when connecting two sub-paths together during bidirectional path tracing. To extend a path using BSDF sampling, cosine terms are generally not required.

Better yet, the probably density function for the Lambertian BSDF just got simpler. For cosine weighted hemisphere sampling, we can define $p^{cw}_\perp$ as follows:

$$p^{cw}_\perp(\omega_j) = \frac{1}{\pi}$$

That looks pretty simple to compute. It should also seem more natural: if we are sampling a constant BSDF with its analytically-derived importance sampling scheme, you would expect the pdf to also be a constant.

The next time you refactor your path tracing framework, consider switching all your sampling functions to return pdfs with respect to either area (when sampling light/sensor surfaces) or projected solid angle (when sampling BSDFs or angular emission/importance functions). It’s well-defined, and in my experience does less work in common cases while being more numerically stable.