<p>I wasn't really expecting that, no. The formula in the paper is not the most elegant - there's quite a few parentheses in there. In this case I think it's just a matter of shuffling the parentheses around a little in the get_k implementation - both terms should be divided by <code>(1-r)</code>:</p>

<pre><code>    float get_k(float r, float n) {
        return std::sqrt( 1.0/(1.0-r) * ( r*std::pow(n+1.0, 2) - std::pow(n-1.0, 2) ));
    }
</code></pre>

CC BY-SA 4.0
2018-12-04T22:01:30.503Z
Big up for the paper's author to register and provide an answer!
zeno
CC BY-SA 4.0
2018-12-04T19:54:07.357Z
2018-12-04T19:54:07.357Z
Ole
<p>While implementing the "Artist Friendly Metallic Fresnel" paper by Ole Gulbrandsen (<a href="http://jcgt.org/published/0003/04/03/paper.pdf" rel="noreferrer">http://jcgt.org/published/0003/04/03/paper.pdf</a>), I read that it is fully reversible:</p>

<blockquote>
  <p>(section 2.3.2): for all r,g ∈ (0,1), the map from (n, k) to (r,g) is a bijection</p>
</blockquote>

<p>However, this doesn't seem to be the case? When converting (r, g) to (n, k) and back to (r, g), I do not get a matching output to my initial parameters. <strong>Is this expected?</strong></p>

<pre><code>r: 0.7 0.55 0.075
g: 0.8 0.65 0.35
n: 2.39006 2.5479 1.44145
k: 4.98834 3.604 0.537046
r_check: 0.737183 0.601524 0.0773389
g_check: 0.827686 0.699977 0.35995
</code></pre>

<p>Here is the implementation to verify, note that I only implemented <code>get_k()</code>, the other functions were already supplied by the paper. <code>get_k()</code> is derived from the following formula in the paper:</p>

<p><span class="math-container">$$k(r, n(r,g)) = \sqrt{ \frac {1}{1-r}(r(n(r,g)+1)^2 - (n(r,g) -1)^2)}$$</span></p>

<pre class="lang-cpp prettyprint-override"><code>#include &lt;iostream&gt;
#include &lt;vector&gt;
#include &lt;cmath&gt;

float n_min(float r) {
  return (1.0-r)/(1.0+r);
}

float n_max(float r) {
  return (1.0 + std::sqrt(r))/(1.0-std::sqrt(r));
}

float get_n(float r, float g) {
  return g*n_min(r) + (1.0-g)*n_max(r);
}

float get_k(float r, float n) {
  return std::sqrt( (1.0/(1.0-r)) * (r * std::pow(n+1.0, 2)) - std::pow(n-1.0, 2) );
}

//float get_k2(float r, float n) {
//  float nr = (n+1.0)*(n+1.0)*r-(n-1.0)*(n-1.0);
//  return nr/(1.0-r);
//}

float get_r(float n, float k) {
  return (std::pow(n-1.0, 2)+std::pow(k, 2)) / (std::pow(n+1.0, 2)+std::pow(k, 2));
}

float get_g(float n, float k) {
  float r = get_r(n, k);
  return (n_max(r)-n)/(n_max(r)-n_min(r));
}



int main() {
  std::vector&lt;float&gt; r = {0.7, 0.55, 0.075};
  std::vector&lt;float&gt; g = {0.8, 0.65, 0.35};
  std::cout &lt;&lt; "r: " &lt;&lt; r[0] &lt;&lt; " " &lt;&lt; r[1] &lt;&lt; " " &lt;&lt; r[2] &lt;&lt; std::endl;
  std::cout &lt;&lt; "g: " &lt;&lt; g[0] &lt;&lt; " " &lt;&lt; g[1] &lt;&lt; " " &lt;&lt; g[2] &lt;&lt; std::endl;


  std::vector&lt;float&gt; n(3);
  std::vector&lt;float&gt; k(3);
  for (int i = 0; i&lt;3; i++) {
    n[i] = get_n(r[i], g[i]);
    k[i] = get_k(r[i], n[i]);
  }
  std::cout &lt;&lt; "n: " &lt;&lt; n[0] &lt;&lt; " " &lt;&lt; n[1] &lt;&lt; " " &lt;&lt; n[2] &lt;&lt; std::endl;
  std::cout &lt;&lt; "k: " &lt;&lt; k[0] &lt;&lt; " " &lt;&lt; k[1] &lt;&lt; " " &lt;&lt; k[2] &lt;&lt; std::endl;


  std::vector&lt;float&gt; r_check(3);
  std::vector&lt;float&gt; g_check(3);
  for (int i = 0; i&lt;3; i++) {
    r_check[i] = get_r(n[i], k[i]);
    g_check[i] = get_g(n[i], k[i]);
  }
  std::cout &lt;&lt; "r_check: " &lt;&lt; r_check[0] &lt;&lt; " " &lt;&lt; r_check[1] &lt;&lt; " " &lt;&lt; r_check[2] &lt;&lt; std::endl;
  std::cout &lt;&lt; "g_check: " &lt;&lt; g_check[0] &lt;&lt; " " &lt;&lt; g_check[1] &lt;&lt; " " &lt;&lt; g_check[2] &lt;&lt; std::endl;
}
</code></pre>

<raytracing><brdf><shading><fresnel>
"Artist Friendly Metallic Fresnel" reversible?
zeno
# "Artist Friendly Metallic Fresnel" reversible?

(Asked by: username_0 on 2018-11-27)

While implementing the "Artist Friendly Metallic Fresnel" paper by Ole Gulbrandsen (<http://jcgt.org/published/0003/04/03/paper.pdf>), I read that it is fully reversible:

> (section 2.3.2): for all r,g ∈ (0,1), the map from (n, k) to (r,g) is a bijection

However, this doesn't seem to be the case? When converting (r, g) to (n, k) and back to (r, g), I do not get a matching output to my initial parameters. **Is this expected?**

```
r: 0.7 0.55 0.075
g: 0.8 0.65 0.35
n: 2.39006 2.5479 1.44145
k: 4.98834 3.604 0.537046
r_check: 0.737183 0.601524 0.0773389
g_check: 0.827686 0.699977 0.35995

```

Here is the implementation to verify, note that I only implemented `get_k()`, the other functions were already supplied by the paper. `get_k()` is derived from the following formula in the paper:

$$k(r, n(r,g)) = \sqrt{ \frac {1}{1-r}(r(n(r,g)+1)^2 - (n(r,g) -1)^2)}$$

```cpp
#include <iostream>
#include <vector>
#include <cmath>

float n_min(float r) {
  return (1.0-r)/(1.0+r);
}

float n_max(float r) {
  return (1.0 + std::sqrt(r))/(1.0-std::sqrt(r));
}

float get_n(float r, float g) {
  return g*n_min(r) + (1.0-g)*n_max(r);
}

float get_k(float r, float n) {
  return std::sqrt( (1.0/(1.0-r)) * (r * std::pow(n+1.0, 2)) - std::pow(n-1.0, 2) );
}

//float get_k2(float r, float n) {
//  float nr = (n+1.0)*(n+1.0)*r-(n-1.0)*(n-1.0);
//  return nr/(1.0-r);
//}

float get_r(float n, float k) {
  return (std::pow(n-1.0, 2)+std::pow(k, 2)) / (std::pow(n+1.0, 2)+std::pow(k, 2));
}

float get_g(float n, float k) {
  float r = get_r(n, k);
  return (n_max(r)-n)/(n_max(r)-n_min(r));
}

int main() {
  std::vector<float> r = {0.7, 0.55, 0.075};
  std::vector<float> g = {0.8, 0.65, 0.35};
  std::cout << "r: " << r[0] << " " << r[1] << " " << r[2] << std::endl;
  std::cout << "g: " << g[0] << " " << g[1] << " " << g[2] << std::endl;

  std::vector<float> n(3);
  std::vector<float> k(3);
  for (int i = 0; i<3; i++) {
    n[i] = get_n(r[i], g[i]);
    k[i] = get_k(r[i], n[i]);
  }
  std::cout << "n: " << n[0] << " " << n[1] << " " << n[2] << std::endl;
  std::cout << "k: " << k[0] << " " << k[1] << " " << k[2] << std::endl;

  std::vector<float> r_check(3);
  std::vector<float> g_check(3);
  for (int i = 0; i<3; i++) {
    r_check[i] = get_r(n[i], k[i]);
    g_check[i] = get_g(n[i], k[i]);
  }
  std::cout << "r_check: " << r_check[0] << " " << r_check[1] << " " << r_check[2] << std::endl;
  std::cout << "g_check: " << g_check[0] << " " << g_check[1] << " " << g_check[2] << std::endl;
}

```

**Tags:** raytracing, brdf, shading, fresnel


---

## 1 answers

### Answer by username_1 on 2018-12-04. Score: 6 | Accepted answer

I wasn't really expecting that, no. The formula in the paper is not the most elegant - there's quite a few parentheses in there. In this case I think it's just a matter of shuffling the parentheses around a little in the get\_k implementation - both terms should be divided by `(1-r)`:

```
    float get_k(float r, float n) {
        return std::sqrt( 1.0/(1.0-r) * ( r*std::pow(n+1.0, 2) - std::pow(n-1.0, 2) ));
    }

```

> **Comments:**
> **username_0**: Big up for the paper's author to register and provide an answer!  

---

