use super::{context::RayMaterialContext, geometry::BounceRefractionGeometry};
use crate::optics::{
polarization::{MuellerMatrix, StokesVector},
raytracer::{NUM_CHANNELS, uniaxial_fresnel},
};
use glam::Vec3;
#[inline]
pub(crate) fn tir_phase_delta(n1k: f32, cos_i: f32, sin_i: f32) -> f32 {
let tan_half_delta_k = (cos_i * (n1k * n1k * sin_i).mul_add(sin_i, -1.0).max(0.0).sqrt())
/ (n1k * sin_i * sin_i).max(1e-6);
2.0 * tan_half_delta_k.atan()
}
pub(in crate::optics::raytracer) fn apply_tir_bounce(
ctx: &RayMaterialContext,
geo: &BounceRefractionGeometry,
is_extraordinary: bool,
k_hat: Vec3,
normal: Vec3,
stokes: &mut [StokesVector; NUM_CHANNELS],
path_pdf: &mut [f32; NUM_CHANNELS],
) -> (Vec3, Option<f32>) {
if let Some(frame) = geo.uniaxial_frame
&& k_hat.cross(ctx.c_axis).length_squared() > 1e-6
{
let mut exact_p_o = None;
for k in 0..NUM_CHANNELS {
let n_inc = geo.n1_ch[k];
let n_o_k = geo.n_o_ch[k];
let n_e_k = ctx.material.extraordinary_index_at(ctx.lambdas[k], n_o_k);
let sol = uniaxial_fresnel::internal_solve(
n_inc,
n_o_k,
n_e_k,
ctx.c_axis,
&frame,
!is_extraordinary,
);
let flux_inc = sol.flux_inc.max(1e-12);
let ro_pow = sol.r_o.norm_sqr() * sol.flux_ro;
let re_pow = sol.r_e.norm_sqr() * sol.flux_re;
let r_total_k = ((ro_pow + re_pow) / flux_inc).min(1.0);
stokes[k] = stokes[k].scale(r_total_k);
if geo.sin2_t_ch[k] <= 1.0 {
path_pdf[k] *= r_total_k.clamp(1e-4, 1.0 - 1e-4);
}
if k == ctx.hero_idx {
exact_p_o = Some(ro_pow / (ro_pow + re_pow).max(1e-12));
}
}
return (k_hat - 2.0 * k_hat.dot(normal) * normal, exact_p_o);
}
for k in 0..NUM_CHANNELS {
let n1k = geo.n1_ch[k];
let n2k = geo.n2_ch[k];
if geo.sin2_t_ch[k] > 1.0 {
let delta_k = tir_phase_delta(n1k, geo.cos_i, geo.sin_i);
let tir_matrix_k = MuellerMatrix::tir_retardation(delta_k);
stokes[k] = stokes[k].apply_matrix(&tir_matrix_k);
} else {
let cos_t_k = (1.0 - geo.sin2_t_ch[k]).max(0.0).sqrt();
let r_s_k = f32::mul_add(n2k, -cos_t_k, n1k * geo.cos_i)
/ f32::mul_add(n2k, cos_t_k, n1k * geo.cos_i);
let r_p_k = f32::mul_add(n1k, -cos_t_k, n2k * geo.cos_i)
/ f32::mul_add(n1k, cos_t_k, n2k * geo.cos_i);
let refl_matrix_k = MuellerMatrix::fresnel_reflection(r_s_k, r_p_k);
stokes[k] = stokes[k].apply_matrix(&refl_matrix_k);
let r_unpol_k = (0.5 * r_p_k.mul_add(r_p_k, r_s_k * r_s_k)).clamp(1e-4, 1.0 - 1e-4);
path_pdf[k] *= r_unpol_k;
}
}
(k_hat - 2.0 * k_hat.dot(normal) * normal, None)
}