Rendering equation's value can be estimated with Monte Carlo (Physically Based Rendering: Light Transport I: Surface Reflection):

$\begin{equation} \begin{split} L_o(p, \omega_o) &= \int_{S^2}f(p, \omega_o, \omega_i) L_i(p, \omega_i) |\cos\theta_i| \mathrm{d}\omega_i\\ & = \frac{1}{N} \sum_{j=1}^{N} \frac{f(p, \omega_o, \omega_i) L_i(p, \omega_j) |\cos\theta_j|}{p(\omega_j)} \end{split} \end{equation}$

So if the hit surface is a mirror and $\omega_j$ is the correct direction (only 1 sample is needed and $p(\omega_j)=1$) then this equation becomes:

$\begin{equation} \begin{split} L_o(p, \omega_o) & = f(p, \omega_o, \omega_i) L_i(p, \omega_j) |\cos\theta_j| \end{split} \end{equation}$

For a ideal mirror that reflect without any energe loss, $f(p, \omega_o, \omega_i)$ is 1.

Yet I couldn't find term $\cos\theta_j$ when reading code from Ray Tracing Weekend and smallpt.

In Ray Tracing Weekend, the ray tracing function is written as

color ray_color(
    const ray& r,
    const color& background,
    const hittable& world,
    shared_ptr<hittable> lights,
    int depth
) {
    hit_record rec;

    // If we've exceeded the ray bounce limit, no more light is gathered.
    if (depth <= 0)
        return color(0,0,0);

    // If the ray hits nothing, return the background color.
    if (!world.hit(r, 0.001, infinity, rec))
        return background;

    scatter_record srec;
    color emitted = rec.mat_ptr->emitted(r, rec, rec.u, rec.v, rec.p);

    if (!rec.mat_ptr->scatter(r, rec, srec))
        return emitted;

    if (srec.is_specular) {
        return srec.attenuation
             * ray_color(srec.specular_ray, background, world, lights, depth-1);

The last part

    if (srec.is_specular) {
        return srec.attenuation
             * ray_color(srec.specular_ray, background, world, lights, depth-1);

implies that when hitting a specular object, the result (color) got attenuated by srec.attenuation but the cosine term is gone.

Similarly, neither smallpt (line 61, 62) include cosine term when handling scatterring for specular object:

else if (obj.refl == SPEC)            // Ideal SPECULAR reflection 
    return obj.e + f.mult(radiance(Ray(x,r.d-n*2*n.dot(r.d)),depth,Xi));

it shoots a ray to a new direction without having $\cos(\theta)$ after reflection.

So, how does $\cos(\theta)$ got cancelled for both renderer? Or did I misunderstand the equation?


2 Answers 2


For a ideal mirror that reflect without any energe loss, $f(p,ω_o,ω_i)$ is 1.

I see your point, but unfortunately this is not true. If your material is an ideal mirror, your BRDF is proportionnal to a Dirac distribution, meaning that its integral is one, but all the reflectivity is contained in one single direction, that you note $\omega_j$.

Therefore $f(p, \omega_i, \omega_o) \propto \delta(\omega_o - \omega_j)$. You should notice that I didn't put an equal sign, this is because we want to be sure that your BRDF resends exactly the same amount of energy that it receives. We should check if it is the case:

$$\int_{S^2} L_i(p,\omega_i) \ \delta(\omega_o-\omega_j) \ |\cos (\theta_o) | \ \text{d}\omega_o = L_i(p,\omega_i) \ |\cos (\theta_j)|$$ By definition of a Dirac distribution. Therefore you should add in your BRDF a cosine value at the denominator to be sure to obtain $L_i(\omega_i)$ value as an output:

$$f(p,\omega_o, \omega_i) = \frac{\delta(\omega_o-\omega_j)}{|cos(\theta_j)|}$$

Computing numerically Dirac distributions is complicated, it is better to simplify the rendering equation, that now simply gives:

$$\int_{S^2} L_i(p,\omega_i) \ \frac{\delta(\omega_o-\omega_j)}{|cos(\theta_j)|} \ |\cos (\theta_o) | \ \text{d}\omega_o = L_i(p,\omega_i)$$

Your cosine is not gone, it just vanishes with another one at the denominator.

  • $\begingroup$ +1 this is correct. The specular reflection BRDF (and transmission BTDF) have a cosine term in them that cancels with the one in the rendering equation. See e.g. § 8.2.2 in PBRT 3. It's important to do that canceling, too, or you'll get numerical problems. $\endgroup$
    – geometrian
    Nov 18, 2023 at 19:00

Note that if $cos$ is not removed, the distribution function won't (necessarily) integrate to $1$ (energy is not conserved). Specular rays means that $f(...)$ is proportional to $C(\omega_r) \delta(\omega_i,\omega_r)$, where $\delta$ is dirac's delta function and $\omega_r$ is the reflection direction. Lets put $L_i=1$ and integrate the distribution: $C(\omega_r)cos(\theta_r) = 1$ which implies that $cos(\theta_r)$ must be cancelled.


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.