<p>After coming back to the problem now with a fresh pair of eyes, I have managed to find my mistake.</p>

<h2>Ad Question 1</h2>

<p>Yes, the formula is correct. The problem is hidden in the tangent <img src="https://chart.googleapis.com/chart?cht=tx&amp;chl=X" alt="X"> and bi-tangent <img src="https://chart.googleapis.com/chart?cht=tx&amp;chl=Y" alt="Y">. When performing normal mapping, the tangent frame has to be <strong>perpendicular</strong> to the mapped normal, otherwise the term 
<img src="https://i.sstatic.net/9Wq9Q.gif" alt="tangent-halfway dot"> will not carry any information about the normal mapping.</p>

<p>Another issue here might be that once the normal is mapped, the tangent frame is not uniquely determined. Some more info on that can be found here: <a href="http://www.thetenthplanet.de/archives/1180" rel="nofollow noreferrer">http://www.thetenthplanet.de/archives/1180</a>. I have decided to solve this the same way as Blender does (look for <code>make_orthonormals_tangent()</code> in their repo) because I need to be compatible with it. Here is my algorithm for that:</p>

<pre><code>bitangent_ortho = norm(cross(normal, tangent))
tangent_ortho = cross(bitangent_ortho, normal)
</code></pre>

<p>(Assuming that the <code>normal</code> is mapped and has unit length and the <code>tangent</code> was a valid tangent before the normal was mapped.)</p>

<h2>Ad Question 2</h2>

<p>This was indeed well answered by user <a href="https://computergraphics.stackexchange.com/users/10096/lightxbulb">@lightxbulb</a> in a comment to my question. My only problem with the "Physically based shading at Disney" course notes was that the variable symbols weren't very clearly explained. I have since learned that the convention is the following:</p>

<ul>
<li><img src="https://chart.googleapis.com/chart?cht=tx&amp;chl=%5Ctheta_v" alt="\theta_v"> is the angle between the normal and <img src="https://chart.googleapis.com/chart?cht=tx&amp;chl=v" alt="v"> </li>
<li><img src="https://chart.googleapis.com/chart?cht=tx&amp;chl=%5Cphi_v" alt="\phi_v"> is the azimuthal angle between the tangent and <img src="https://chart.googleapis.com/chart?cht=tx&amp;chl=v" alt="v"></li>
</ul>

<h2>Ad Question 3</h2>

<p>The only pieces of advice that were useful for me here were the following:</p>

<ol>
<li>Always come up with the simplest toy examples possible</li>
<li>Verify your assumptions</li>
<li>Visualize everything</li>
</ol>

<h2>Conclusion</h2>

<p>I hope this is helpful for somebody else than just me. Good luck implementing BRDFs!</p>

CC BY-SA 4.0
2024-04-04T17:25:57.967Z
Seems like some of your math symbols don't show up anymore.
Tara
CC BY-SA 4.0
2019-07-09T09:05:12.937Z
2019-07-09T09:05:12.937Z
honzukka
<h2>Introduction</h2>

<p>I am implementing anisotropic GGX BRDF and have encountered strange behaviour of my implementation. I thought that if I compare the microfacet distribution function I have with the one of an isotropic GGX, then they should be equal when the anisotropy parameter is 0. I haven't been able to do that, though.</p>

<p>Here is the GGX formula I have used:</p>

<p><a href="https://i.sstatic.net/3MkPf.png" rel="noreferrer"><img src="https://i.sstatic.net/3MkPf.png" alt="enter image description here"></a></p>

<p>Here is the anisotropic GGX formula I have used:</p>

<p><a href="https://i.sstatic.net/evdjG.png" rel="noreferrer"><img src="https://i.sstatic.net/evdjG.png" alt="enter image description here"></a></p>

<p>If anisotropy is 0, then <img src="https://chart.googleapis.com/chart?cht=tx&amp;chl=%5Calpha_x" alt="\alpha_x"> is equal to <img src="https://chart.googleapis.com/chart?cht=tx&amp;chl=%5Calpha_y" alt="\alpha_y">, therefore I can get the following from the second formula:</p>

<p><a href="https://i.sstatic.net/TeG4e.png" rel="noreferrer"><img src="https://i.sstatic.net/TeG4e.png" alt="enter image description here"></a></p>

<h2>Problem 1</h2>

<p>The problem now is that the third formula can never match the first one because of the negative 1 in the first one.</p>

<h2>Problem 2</h2>

<p>The specific issue I have when rendering using my anisotropic GGX is that the normals seem to be ignored in the result.</p>

<p>Here is a visualization of the distribution function of isotropic GGX on a flat material patch with normal mapping:</p>

<p><a href="https://i.sstatic.net/Z5oHJ.png" rel="noreferrer"><img src="https://i.sstatic.net/Z5oHJ.png" alt="enter image description here"></a></p>

<p>And here is the anisotropic one:</p>

<p><a href="https://i.sstatic.net/uxw6j.png" rel="noreferrer"><img src="https://i.sstatic.net/uxw6j.png" alt="enter image description here"></a></p>

<h2>Notation</h2>

<p>To complete the explanation of my solution, I use <img src="https://chart.googleapis.com/chart?cht=tx&amp;chl=H" alt="H"> for the half-vector, <img src="https://chart.googleapis.com/chart?cht=tx&amp;chl=N" alt="N"> for the normal, <img src="https://chart.googleapis.com/chart?cht=tx&amp;chl=%5Calpha" alt="\alpha"> for roughness, <img src="https://chart.googleapis.com/chart?cht=tx&amp;chl=X" alt="X"> for the tangent and <img src="https://chart.googleapis.com/chart?cht=tx&amp;chl=Y" alt="Y"> for the bi-tangent (in my case these are simply aligned with the x and y axes respectively). <img src="https://chart.googleapis.com/chart?cht=tx&amp;chl=%5Calpha_x" alt="\alpha_x"> and <img src="https://chart.googleapis.com/chart?cht=tx&amp;chl=%5Calpha_y" alt="\alpha_y"> represent roughness in the corresponding directions.</p>

<h2>Questions</h2>

<ol>
<li>Is there a mistake in my reasoning? Is my anisotropic GGX correct?</li>
<li>What is the relationship between the two? Is there a simple explanation for the extension from GGX to anisotropic GGX?</li>
<li>Do you have any general tips for verifying the correctness of one's BRDF implementation?</li>
</ol>

<h2>References</h2>

<p>Here are my main references:</p>

<ul>
<li><a href="https://github.com/Kink3d/AnisotropicStandardShader/blob/master/AnisotropicStandardShader/CGIncludes/UnityAnisotropicBRDF.cginc" rel="noreferrer">Anisotropic GGX implementation</a></li>
<li><a href="http://graphicrants.blogspot.com/2013/08/specular-brdf-reference.html" rel="noreferrer">BRDF formulas</a></li>
</ul>

CC BY-SA 4.0
2019-02-07T03:11:47.937Z
Try substituting $1/\alpha^2$ with $\cos^2\phi/\alpha^2_x + \sin^2\phi/\alpha^2_y$.
lightxbulb
CC BY-SA 4.0
2019-02-08T06:44:20.143Z
Thanks for your comment, @lightxbulb. That does indeed seem to make the two formulas equal if anisotropy is 0. Before I can investigate further, can you please clarify what ϕ stands for in your formula?
honzukka
CC BY-SA 4.0
2019-02-08T11:40:16.160Z
It's the azimuthal angle (the one accounting for anisotropy). Go to the course notes of "Physically based shading at Disney". Specifically at the end of the pdf you have derivations in the appendix: https://blog.selfshadow.com/publications/s2012-shading-course/
lightxbulb
<rendering><brdf><ggx>
Anisotropic GGX BRDF implementation. How is it related to isotropic GGX BRDF?
honzukka
# Anisotropic GGX BRDF implementation. How is it related to isotropic GGX BRDF?

(Asked by: username_0 on 2019-02-05)

## Introduction

I am implementing anisotropic GGX BRDF and have encountered strange behaviour of my implementation. I thought that if I compare the microfacet distribution function I have with the one of an isotropic GGX, then they should be equal when the anisotropy parameter is 0. I haven't been able to do that, though.

Here is the GGX formula I have used:

[![enter image description here](https://i.sstatic.net/3MkPf.png)](https://i.sstatic.net/3MkPf.png)

Here is the anisotropic GGX formula I have used:

[![enter image description here](https://i.sstatic.net/evdjG.png)](https://i.sstatic.net/evdjG.png)

If anisotropy is 0, then ![\alpha_x](https://chart.googleapis.com/chart?cht=tx&chl=%5Calpha_x) is equal to ![\alpha_y](https://chart.googleapis.com/chart?cht=tx&chl=%5Calpha_y), therefore I can get the following from the second formula:

[![enter image description here](https://i.sstatic.net/TeG4e.png)](https://i.sstatic.net/TeG4e.png)

## Problem 1

The problem now is that the third formula can never match the first one because of the negative 1 in the first one.

## Problem 2

The specific issue I have when rendering using my anisotropic GGX is that the normals seem to be ignored in the result.

Here is a visualization of the distribution function of isotropic GGX on a flat material patch with normal mapping:

[![enter image description here](https://i.sstatic.net/Z5oHJ.png)](https://i.sstatic.net/Z5oHJ.png)

And here is the anisotropic one:

[![enter image description here](https://i.sstatic.net/uxw6j.png)](https://i.sstatic.net/uxw6j.png)

## Notation

To complete the explanation of my solution, I use ![H](https://chart.googleapis.com/chart?cht=tx&chl=H) for the half-vector, ![N](https://chart.googleapis.com/chart?cht=tx&chl=N) for the normal, ![\alpha](https://chart.googleapis.com/chart?cht=tx&chl=%5Calpha) for roughness, ![X](https://chart.googleapis.com/chart?cht=tx&chl=X) for the tangent and ![Y](https://chart.googleapis.com/chart?cht=tx&chl=Y) for the bi-tangent (in my case these are simply aligned with the x and y axes respectively). ![\alpha_x](https://chart.googleapis.com/chart?cht=tx&chl=%5Calpha_x) and ![\alpha_y](https://chart.googleapis.com/chart?cht=tx&chl=%5Calpha_y) represent roughness in the corresponding directions.

## Questions

1. Is there a mistake in my reasoning? Is my anisotropic GGX correct?
2. What is the relationship between the two? Is there a simple explanation for the extension from GGX to anisotropic GGX?
3. Do you have any general tips for verifying the correctness of one's BRDF implementation?

## References

Here are my main references:

* [Anisotropic GGX implementation](https://github.com/Kink3d/AnisotropicStandardShader/blob/master/AnisotropicStandardShader/CGIncludes/UnityAnisotropicBRDF.cginc)
* [BRDF formulas](http://graphicrants.blogspot.com/2013/08/specular-brdf-reference.html)

**Tags:** rendering, brdf, ggx

> **Comments:**
> **username_1**: Try substituting $1/\alpha^2$ with $\cos^2\phi/\alpha^2_x + \sin^2\phi/\alpha^2_y$.  
> **username_0**: Thanks for your comment, @username_1. That does indeed seem to make the two formulas equal if anisotropy is 0. Before I can investigate further, can you please clarify what ϕ stands for in your formula?  
> **username_1**: It's the azimuthal angle (the one accounting for anisotropy). Go to the course notes of "Physically based shading at Disney". Specifically at the end of the pdf you have derivations in the appendix: https://blog.selfshadow.com/publications/s2012-shading-course/  

---

## 1 answers

### Answer by username_0 on 2019-07-09. Score: 4 | Accepted answer

After coming back to the problem now with a fresh pair of eyes, I have managed to find my mistake.

## Ad Question 1

Yes, the formula is correct. The problem is hidden in the tangent ![X](https://chart.googleapis.com/chart?cht=tx&chl=X) and bi-tangent ![Y](https://chart.googleapis.com/chart?cht=tx&chl=Y). When performing normal mapping, the tangent frame has to be **perpendicular** to the mapped normal, otherwise the term
![tangent-halfway dot](https://i.sstatic.net/9Wq9Q.gif) will not carry any information about the normal mapping.

Another issue here might be that once the normal is mapped, the tangent frame is not uniquely determined. Some more info on that can be found here: <http://www.thetenthplanet.de/archives/1180>. I have decided to solve this the same way as Blender does (look for `make_orthonormals_tangent()` in their repo) because I need to be compatible with it. Here is my algorithm for that:

```
bitangent_ortho = norm(cross(normal, tangent))
tangent_ortho = cross(bitangent_ortho, normal)

```

(Assuming that the `normal` is mapped and has unit length and the `tangent` was a valid tangent before the normal was mapped.)

## Ad Question 2

This was indeed well answered by user [@username_1](https://computergraphics.stackexchange.com/users/10096/username_1) in a comment to my question. My only problem with the "Physically based shading at Disney" course notes was that the variable symbols weren't very clearly explained. I have since learned that the convention is the following:

* ![\theta_v](https://chart.googleapis.com/chart?cht=tx&chl=%5Ctheta_v) is the angle between the normal and ![v](https://chart.googleapis.com/chart?cht=tx&chl=v)
* ![\phi_v](https://chart.googleapis.com/chart?cht=tx&chl=%5Cphi_v) is the azimuthal angle between the tangent and ![v](https://chart.googleapis.com/chart?cht=tx&chl=v)

## Ad Question 3

The only pieces of advice that were useful for me here were the following:

1. Always come up with the simplest toy examples possible
2. Verify your assumptions
3. Visualize everything

## Conclusion

I hope this is helpful for somebody else than just me. Good luck implementing BRDFs!

> **Comments:**
> **username_2**: Seems like some of your math symbols don't show up anymore.  

---

