1
$\begingroup$

I am currently experiencing a very obvious artifact that occurs when looking at objects on very grazing angles

Here is a picture of the artifact: artifact

I noticed that the issue was coming from my implementation of Cook-Torrance's Specular BRDF.

Here is the equation for the Cook-Torrance Specular BRDF: Cook-Torrance Specular BRDF

More specifically, calculating the denominator is causing the issue:

float denominator = 4 * max(dot(viewDir, normal), 0.0) * max(dot(lightDir, normal), 0.0) + 0.001;  // Prevents any division by zero

When I change the calculation of dot(viewDir, normal) to dot(halfway, normal), the artifact disappears:

float denominator = 4 * max(dot(halfway, normal), 0.0) * max(dot(lightDir, normal), 0.0) + 0.001;  // Prevents any division by zero

But this equation is no longer correct. I was wondering if anyone knows why my implementation causes these artifacts?

If you want to see my fragment shader you can find the code here:

https://github.com/Ershany/Arcane-Engine/blob/WIP_PBR/Arcane%20Engine%20Core/src/shaders/pbr_model.frag

Thank you!

$\endgroup$
8
  • 2
    $\begingroup$ Have you tried calculating the TBN in the fragment shader? When calculating it in the vertex shader, OpenGL will linearly interpolate the matrix for use in the fragment shader and that might cause the artifacts. Do make sure that you normalize the tangent, bitangent and normal in the fragment shader before using them (again because of the linear interpolation). $\endgroup$
    – bram0101
    Commented Oct 9, 2018 at 6:36
  • $\begingroup$ Yes I tried to do this in the fragment shader and normalized the vectors before, and I still get these strange artifacts $\endgroup$ Commented Oct 9, 2018 at 13:55
  • $\begingroup$ Tangents are multiplied by the model matrix, as they dont suffer from non-uniform scale disortion as normal vector do. Also, you do need a vertex normal and a normal vector extracted from the normal map, however, I see you are applying the normalization (normalize(normal * 2.0f - 1.0f)) to the same vector you used to create the TBN matrix $\endgroup$
    – Nadir
    Commented Oct 10, 2018 at 14:34
  • $\begingroup$ I thought the tangents and bitangents would also suffer from possibly being non-orthogonal to each other if scaled non-uniformly. Also I edited my fragment shader code above to showcase that I am actually sampling the normal from the normal map. My bad! $\endgroup$ Commented Oct 10, 2018 at 16:51
  • 1
    $\begingroup$ Have you identified which part of the code generates the color of the artifact? Have you tried to reduce your shader to a minimal reproduce case? I wouldn't be surprised if it was due to either a normal facing away from light, or a nearly 0 but negative parameter to a pow() function. $\endgroup$ Commented Oct 11, 2018 at 4:35

1 Answer 1

1
$\begingroup$

According to learnopengl.com, the term $G(l, v, n)$ in the Cook-Torrance Specular BRDF can be written as

$$ G=\frac{n \cdot v}{(n \cdot v)(1-k)+k} \cdot \frac{n \cdot l}{(n \cdot l)(1-k)+k} $$

and this is the specular term of the Cook-Torrance BRDF

$$ f=\frac{FGD}{4(n \cdot l)(n \cdot v)} $$

we can see that both $f$ and $G$ have $n \cdot v$ and $n \cdot l$ terms that cancel each other out. When $n \cdot v$ becomes really small and approaches 0 we get a $0/0$ situation which is what causes artifacts at grazing angles due to floating point precision errors near 0.

Solution

The problem can be solved by canceling the terms $n \cdot v$ and $n \cdot l$ in $f$ and $G$. The new specular Cook-Torrance BRDF becomes

$$ f=\frac{FGD}{4} $$

where

$$ G=\frac{1}{(n \cdot v)(1-k)+k} \cdot \frac{1}{(n \cdot l)(1-k)+k} $$

$\endgroup$

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