<p>The <span class="math-container">$N \cdot L$</span> term (I've seen &quot;geometry term&quot; thrown around; not quite accurate, but good enough for here) is part of the rendering equation.  What you're looking at here is an estimate of the inside of the rendering equation—that is, the incoming radiance <code>i</code> times the geometry term <code>dot(n,l)</code> times the BRDF.</p>
<p>The simple answer for why the Phong BRDF doesn't include the geometry term is &quot;Well, it probably should, but the Phong model predates the invention of the rendering equation by more than a decade.&quot;.</p>
<blockquote>
<p>Phong, B.T.: Illumination for computer generated pictures.  Communications of the ACM <strong>18</strong>(6), 311–317 (1975).<br/><br/>
Kajiya, J.T.: The rendering equation.  In: Proceedings of the 13th Annual Conference on Computer Graphics and Interactive Techniques, SIGGRAPH ’86, pp. 143–150.  ACM, New York, NY, USA (1986).</p>
</blockquote>
<p>This deficiency was fixed in what is now called the Modified Phong BRDF:</p>
<blockquote>
<p>Lewis, R.R.: Making Shaders More Physically Plausible.  Computer Graphics Forum <strong>13</strong>(2), 109–120 (1994)</p>
</blockquote>
<p>At the end of the day, whether the term is present or not isn't an issue of correctness per-se.  Not having the geometry term when you might expect it just means that the BRDF corresponds to a different surface reflection that doesn't foreshorten much at glancing angles.  Indeed, many other BRDFs have terms that look like reciprocal geometry terms, so that they cancel with the actual geometry term in the rendering equation.</p>
<p>In code, the BRDFs look like:</p>
<pre><code>vec3 f_phong    = rho_s/(TWO_PI/(a+1.0)) * pow(max(0.0,dot(r,v)),a) / dot(n,l);
vec3 f_modphong = rho_s/(TWO_PI/(a+2.0)) * pow(max(0.0,dot(r,v)),a);
</code></pre>
<p>And so when multiplied by the light and geometry term in the rendering equation (i.e., <code>i * f * dot(n,l)</code>), you get:</p>
<pre><code>vec3 spec_phong    = i * rho_s/(TWO_PI/(a+1.0)) * pow(max(0.0,dot(r,v)),a);
vec3 spec_modphong = i * rho_s/(TWO_PI/(a+2.0)) * pow(max(0.0,dot(r,v)),a) * dot(n,l);
</code></pre>
<hr />
<p>The obvious should be noted, however: while the Phong BRDF is simple to understand and implement, it is of limited accuracy on real-world materials.  It's not terrible, the way many people think it is, but it does lack important features that can be important on many materials.  It is not the state-of-the-art, and depending on your needs you may like to look elsewhere (GGX microfacet, etc.) for more modern BRDFs.</p>

CC BY-SA 4.0
2020-07-20T21:10:34.923Z
2020-07-20T21:10:34.923Z
geometrian
<p>Classical Phong reflection model, as described on <a href="https://en.wikipedia.org/wiki/Phong_reflection_model" rel="nofollow noreferrer">wikipedia</a>, computes diffuse and specular light as follows:</p>
<pre><code>vec3 diffuse = kd * dot(n, l) * i;
vec3 specular = ks * pow(dot(r, v), a) * i;
</code></pre>
<p>Where <code>n</code>, <code>l</code>, <code>r</code>, <code>v</code> are the normal, light, reflection and view vectors, <code>kd</code>, <code>ks</code> are surface diffuse and specular color, <code>a</code> is gloss and <code>i</code> is incoming light intensity.</p>
<p>I understand that the diffuse term is such as it is because 1) diffuse reflection is not dependent on viewer direction and 2) surfaces angled away from light recieve less light to scatter, thus the <code>dot(n, l)</code> term.</p>
<p>However, shouldn't the specular term <strong>also</strong> be multiplied by <code>dot(n, l)</code> for the same reason? Is my assumtion that <code>dot(n, l)</code> computes incoming light wrong?</p>
<p>I know that the Phong model is mostly a hack, but this seems like a simple and performant modification and I haven't found this issue discussed anywhere. I've also looked into the source code of the game Tesseract, which does seem to multiply the specular term by <code>dot(n, l)</code>.</p>

CC BY-SA 4.0
2020-07-19T13:34:51.307Z
The cos term is actually a part of the rendering equation. The lambertian BRDF is just a constant $ R_{d}/{\pi}$. Hence it gets multiplied with the whole BRDF  (spec + diffuse)
gallickgunner
CC BY-SA 4.0
2020-07-19T13:50:51.577Z
Phong is a hack, so the specular brdf is actually $\frac{specular}{\cos\theta}$ which breaks energy conservation. There is a modified version of the brdf where it is energy conserving and the specular part is multiplied with $\cos\theta$.
lightxbulb
<shading>
Phong model: why no multiplication by N dot L in specular term?
Kuba
# Phong model: why no multiplication by N dot L in specular term?

(Asked by: username_0 on 2020-07-19)

Classical Phong reflection model, as described on [wikipedia](https://en.wikipedia.org/wiki/Phong_reflection_model), computes diffuse and specular light as follows:

```
vec3 diffuse = kd * dot(n, l) * i;
vec3 specular = ks * pow(dot(r, v), a) * i;

```

Where `n`, `l`, `r`, `v` are the normal, light, reflection and view vectors, `kd`, `ks` are surface diffuse and specular color, `a` is gloss and `i` is incoming light intensity.

I understand that the diffuse term is such as it is because 1) diffuse reflection is not dependent on viewer direction and 2) surfaces angled away from light recieve less light to scatter, thus the `dot(n, l)` term.

However, shouldn't the specular term **also** be multiplied by `dot(n, l)` for the same reason? Is my assumtion that `dot(n, l)` computes incoming light wrong?

I know that the Phong model is mostly a hack, but this seems like a simple and performant modification and I haven't found this issue discussed anywhere. I've also looked into the source code of the game Tesseract, which does seem to multiply the specular term by `dot(n, l)`.

**Tags:** shading

> **Comments:**
> **username_1**: The cos term is actually a part of the rendering equation. The lambertian BRDF is just a constant $ R_{d}/{\pi}$. Hence it gets multiplied with the whole BRDF  (spec + diffuse)  
> **username_2**: Phong is a hack, so the specular brdf is actually $\frac{specular}{\cos\theta}$ which breaks energy conservation. There is a modified version of the brdf where it is energy conserving and the specular part is multiplied with $\cos\theta$.  

---

## 1 answers

### Answer by username_3 on 2020-07-20. Score: 0 | Accepted answer

The $N \cdot L$ term (I've seen "geometry term" thrown around; not quite accurate, but good enough for here) is part of the rendering equation. What you're looking at here is an estimate of the inside of the rendering equation—that is, the incoming radiance `i` times the geometry term `dot(n,l)` times the BRDF.

The simple answer for why the Phong BRDF doesn't include the geometry term is "Well, it probably should, but the Phong model predates the invention of the rendering equation by more than a decade.".

> Phong, B.T.: Illumination for computer generated pictures. Communications of the ACM **18**(6), 311–317 (1975).  
>   
> Kajiya, J.T.: The rendering equation. In: Proceedings of the 13th Annual Conference on Computer Graphics and Interactive Techniques, SIGGRAPH ’86, pp. 143–150. ACM, New York, NY, USA (1986).

This deficiency was fixed in what is now called the Modified Phong BRDF:

> Lewis, R.R.: Making Shaders More Physically Plausible. Computer Graphics Forum **13**(2), 109–120 (1994)

At the end of the day, whether the term is present or not isn't an issue of correctness per-se. Not having the geometry term when you might expect it just means that the BRDF corresponds to a different surface reflection that doesn't foreshorten much at glancing angles. Indeed, many other BRDFs have terms that look like reciprocal geometry terms, so that they cancel with the actual geometry term in the rendering equation.

In code, the BRDFs look like:

```
vec3 f_phong    = rho_s/(TWO_PI/(a+1.0)) * pow(max(0.0,dot(r,v)),a) / dot(n,l);
vec3 f_modphong = rho_s/(TWO_PI/(a+2.0)) * pow(max(0.0,dot(r,v)),a);

```

And so when multiplied by the light and geometry term in the rendering equation (i.e., `i * f * dot(n,l)`), you get:

```
vec3 spec_phong    = i * rho_s/(TWO_PI/(a+1.0)) * pow(max(0.0,dot(r,v)),a);
vec3 spec_modphong = i * rho_s/(TWO_PI/(a+2.0)) * pow(max(0.0,dot(r,v)),a) * dot(n,l);

```

---

The obvious should be noted, however: while the Phong BRDF is simple to understand and implement, it is of limited accuracy on real-world materials. It's not terrible, the way many people think it is, but it does lack important features that can be important on many materials. It is not the state-of-the-art, and depending on your needs you may like to look elsewhere (GGX microfacet, etc.) for more modern BRDFs.


---

