《用两天学习光线追踪》中,用了到折射向量的计算公式,最终实现效果如下:

项目链接:https://github.com/maijiaquan/ray-tracing-with-imgui


下面进行折射向量的推导。

对于折射,有个著名的公式:Snell’s law

sin ⁡ θ 1 sin ⁡ θ 2 = η 2 η 1 \dfrac{\sin\theta_1}{\sin\theta_2} = \dfrac{\eta_2}{\eta_1} sinθ2sinθ1=η1η2
其中, θ 1 , θ 2 \theta_1,\theta_2 θ1,θ2分别是入射角和折射角, η 1 , η 2 \eta_1, \eta_2 η1,η2分别是两种介质的折射率。

下面根据入射光线向量 I I I和法向量 N N N,推导折射光线向量 T T T
用一个单位圆来辅助理解:对于二维平面的折射, N N N为两介质分界平面的法向量, M M M为平面上和 N N N垂直的单位向量。

在单位圆内,可知
T = A + B (1) T = A + B \tag 1 T=A+B(1)
A A A B B B也很容易计算:
A = M sin ⁡ ( θ 2 ) B = − N cos ⁡ ( θ 2 ) (2) \begin{array}{l} A = M\sin(\theta_2)\\ \tag2 B = -N\cos(\theta_2) \end{array} A=Msin(θ2)B=Ncos(θ2)(2)

因为 ( I + C ) (I+C) (I+C)的方向就是 M M M的方向,且 ( I + C ) (I+C) (I+C) 的长度就是 sin ⁡ ( θ 1 ) \sin(\theta_1) sin(θ1),所以 ( I + C ) (I+C) (I+C) 除以其长度,就是单位圆上的单位向量 M M M
M = ( I + C ) sin ⁡ ( θ 1 ) (3) M = \dfrac{(I + C)}{\sin(\theta_1)} \tag 3 M=sin(θ1)(I+C)(3)

由图可知:

C = cos ⁡ ( θ 1 ) N (4) C = \cos(\theta_1)N \tag 4 C=cos(θ1)N(4)

( 2 ) ( 3 ) ( 4 ) (2)(3)(4) (2)(3)(4)带入 ( 1 ) (1) (1)有:

T = A + B , T = M sin ⁡ ( θ 2 ) − N cos ⁡ ( θ 2 ) , T = ( I + C ) sin ⁡ ( θ 2 ) sin ⁡ ( θ 1 ) − N cos ⁡ ( θ 2 ) , T = ( I + cos ⁡ ( θ 1 ) N ) sin ⁡ ( θ 2 ) sin ⁡ ( θ 1 ) − N cos ⁡ ( θ 2 ) \begin{array}{l} T = A + B,\\ T = M\sin(\theta_2) - N\cos(\theta_2),\\ T = \dfrac{(I + C)\sin(\theta_2)}{\sin(\theta_1)} - N\cos(\theta_2),\\ T = \dfrac{(I + \cos(\theta_1)N)\sin(\theta_2)}{\sin(\theta_1)} - N\cos(\theta_2) \end{array} T=A+B,T=Msin(θ2)Ncos(θ2),T=sin(θ1)(I+C)sin(θ2)Ncos(θ2),T=sin(θ1)(I+cos(θ1)N)sin(θ2)Ncos(θ2)

再结合折射定律 Snell’s law:
sin ⁡ ( θ 2 ) sin ⁡ ( θ 1 ) = η 1 η 2 (5) \dfrac{\sin(\theta_2)}{\sin(\theta_1)} = \dfrac{\eta_1}{\eta_2} \tag 5 sin(θ1)sin(θ2)=η2η1(5)

可得
T = η 1 η 2 ( I + cos ⁡ ( θ 1 ) N ) − N cos ⁡ ( θ 2 ) (6) T = \dfrac{\eta_1}{\eta_2}(I + \cos(\theta_1)N) - N\cos(\theta_2) \tag 6 T=η2η1(I+cos(θ1)N)Ncos(θ2)(6)
根据勾股定理有:
cos ⁡ 2 ( θ ) + sin ⁡ 2 ( θ ) = 1 → cos ⁡ ( θ ) = 1 − sin ⁡ 2 ( θ ) (7) \cos^2(\theta) + \sin^2(\theta) = 1 \rightarrow \cos(\theta) = \sqrt{1 - \sin^2(\theta)} \tag 7 cos2(θ)+sin2(θ)=1cos(θ)=1sin2(θ) (7)

根据Snell’s law有:
sin ⁡ ( θ 2 ) = η 1 η 2 sin ⁡ ( θ 1 ) (8) \sin(\theta_2) = \dfrac{\eta_1}{\eta_2} \sin(\theta_1) \tag 8 sin(θ2)=η2η1sin(θ1)(8)

( 7 ) ( 8 ) (7) (8) (7)(8)带入 ( 6 ) (6) (6)
T = η 1 η 2 ( I + cos ⁡ ( θ 1 ) N ) − N 1 − ( η 1 η 2 ) 2 sin ⁡ 2 ( θ 1 ) (9) T = \dfrac{\eta_1}{\eta_2}(I + \cos(\theta_1)N) - N\sqrt{1 - \left( \dfrac{\eta_1}{\eta_2} \right) ^2 \sin^2(\theta_1)} \tag 9 T=η2η1(I+cos(θ1)N)N1(η2η1)2sin2(θ1) (9)

再根据勾股定理的变形:
sin ⁡ 2 ( θ ) = 1 − cos ⁡ 2 ( θ ) \sin^2(\theta) = 1 - \cos^2(\theta) sin2(θ)=1cos2(θ)

消去 sin ⁡ 2 ( θ 1 ) \sin^2(\theta_1) sin2(θ1),得:
T = η 1 η 2 ( I + cos ⁡ ( θ 1 ) N ) − N 1 − ( η 1 η 2 ) 2 ( 1 − cos ⁡ 2 ( θ ) ) (10) T = \dfrac{\eta_1}{\eta_2}(I + \cos(\theta_1)N) - N\sqrt{1 - \left( \dfrac{\eta_1}{\eta_2} \right) ^2 (1 - \cos^2(\theta))} \tag {10} T=η2η1(I+cos(θ1)N)N1(η2η1)2(1cos2(θ)) (10)

根据向量点乘:
cos ⁡ ( θ 1 ) = N ⋅ I \cos(\theta_1) = N \cdot I cos(θ1)=NI
消去 cos ⁡ ( θ 1 ) \cos(\theta_1) cos(θ1),最终得到折射向量 T T T的公式:
T = η ( I + c 1 N ) − N c 2 (11) T = \eta(I + c_1 N) - N c_2 \tag{11} T=η(I+c1N)Nc2(11)

其中,
η = η 1 η 2 c 1 = N ⋅ I c 2 = 1 − η 2 ( 1 − c 1 2 ) \begin{array}{l} \eta = \dfrac{\eta_1}{\eta_2}\\ c_1 = N \cdot I\\ c_2 = \sqrt{1 - \eta^2 (1 - c_1^2)} \end{array} η=η2η1c1=NIc2=1η2(1c12)

写成代码:

bool refract(const vec3& v_in, const vec3& normal, float n1_over_n2, vec3& refracted) {
    vec3 uv = unit_vector(v_in);
    float dt = -dot(uv, normal);
    float discriminant = 1.0 - n1_over_n2*n1_over_n2*(1-dt*dt);
    if (discriminant > 0) {
        refracted = n1_over_n2*(uv + normal*dt) - normal*sqrt(discriminant);
        return true;
    }
    else
        return false;
}

参考资料:https://www.scratchapixel.com/lessons/3d-basic-rendering/introduction-to-shading/reflection-refraction-fresnel

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐