RayTracing/raytracing.github.io

Book1.11.4 Unclear Argument Passing in Schlick's Approximation for Fresnel Reflection

Closed this issue · 0 comments

The issue is related to the reflectance function in the dielectric class (material.h, line 86).

Schlick’s approximation for Fresnel reflection is given by:

$$ R(\theta_i) = R_0 + (1 - R_0)(1 - \cos\theta_i)^5 $$

where:

$$ R_0 = \left(\frac{\eta_i - \eta_t}{\eta_i + \eta_t}\right)^2 = \left(\frac{1 - \eta_t / \eta_i}{1 + \eta_t / \eta_i}\right)^2 $$

Here, the refractive index transitions from $\eta_i$ to $\eta_t$.

In the implementation, the variable ri represents etai_over_etat, or $\eta_i / \eta_t$. Given this, the function should ideally be called as reflectance(cos_theta, 1 / ri) rather than reflectance(cos_theta, ri). However, because of the squared term in the formula:

$$ R_0 = \left(\frac{\eta_i - \eta_t}{\eta_i + \eta_t}\right)^2 = \left(\frac{\eta_i / \eta_t - 1}{\eta_i / \eta_t + 1}\right)^2 = \left(\frac{1 - \eta_i / \eta_t}{\eta_i / \eta_t + 1}\right)^2 $$

the original implementation reflectance(cos_theta, ri) also produces the correct result.

That said, this implementation might be confusing for beginners (I must admit, I initially misinterpreted it myself XD). Since the book does not explicitly include the Fresnel reflection equation or Schlick’s approximation, it would be helpful to provide a brief introduction beforehand. Alternatively, the implementation could be made clearer by explicitly passing both refractive indices, like this:

// Usage
reflectance(cos_theta_i, ri, 1.0);

// Function
static double reflectance(double cos_theta_i, double eta_i, double eta_t) {
    double r0 = (eta_i - eta_t) / (eta_i + eta_t);
    r0 *= r0;
    return r0 + (1 - r0) * std::pow(1 - cos_theta_i, 5);
}

Also, I think it would be more helpful to briefly introduce the formulas for Fresnel reflection coefficients $R_s$ and $R_p$, and then explain that $R_0$ is an approximation taken at normal incidence.