# Anisotropic GGX BRDF implementation. How is it related to isotropic GGX BRDF?

## 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:

Here is the anisotropic GGX formula I have used:

If anisotropy is 0, then is equal to , therefore I can get the following from the second formula:

## 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:

And here is the anisotropic one:

## Notation

To complete the explanation of my solution, I use for the half-vector, for the normal, for roughness, for the tangent and for the bi-tangent (in my case these are simply aligned with the x and y axes respectively). and 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:

• Try substituting $1/\alpha^2$ with $\cos^2\phi/\alpha^2_x + \sin^2\phi/\alpha^2_y$. – lightxbulb Feb 7 at 3:11
• 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 Feb 8 at 6:44
• 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: blog.selfshadow.com/publications/s2012-shading-course – lightxbulb Feb 8 at 11:40

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

Yes, the formula is correct. The problem is hidden in the tangent and bi-tangent . When performing normal mapping, the tangent frame has to be perpendicular to the mapped normal, otherwise the term 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.)

This was indeed well answered by user @lightxbulb 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:

• is the angle between the normal and
• is the azimuthal angle between the tangent and