# "Artist Friendly Metallic Fresnel" reversible?

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)}$$

#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;
}


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) {