use glam::Vec3;
use super::{
fan::FanGeometry,
lighting::ExitLighting,
ray_trace::{RayFate, trace_wavelength},
visibility::ray_is_visibly_returned,
};
use crate::optics::raytracer::{Ray, intersect_polyhedron_soa};
pub(super) struct ApertureSampleContext<'a> {
pub(super) plane_soa: &'a crate::simd::PlanesSoA32,
pub(super) nd: f32,
pub(super) n_f: f32,
pub(super) n_c: f32,
pub(super) cam_forward: Vec3,
pub(super) cam_right: Vec3,
pub(super) cam_up: Vec3,
pub(super) fan: FanGeometry,
pub(super) lighting: ExitLighting<'a>,
}
pub(super) enum RayClassification {
EntryBlocked,
Windowed,
Extinct,
Returned(Option<(f32, f32)>),
}
pub(super) fn classify_aperture_sample(
ctx: &ApertureSampleContext,
u: f32,
v: f32,
dx_sub: f32,
dz_sub: f32,
) -> Option<RayClassification> {
let ray_dir = (ctx.cam_forward + dx_sub * ctx.cam_right + dz_sub * ctx.cam_up).normalize();
let ray_origin = ctx
.fan
.origin(ctx.cam_forward, ctx.cam_right, ctx.cam_up, u, v);
let ray = Ray {
origin: ray_origin,
dir: ray_dir,
};
let hit = intersect_polyhedron_soa(ray, ctx.plane_soa);
let hit_rec = hit?;
let hit_point_entry = ray.origin + hit_rec.t * ray.dir;
let n_entry = hit_rec.normal;
let cos_i = (-ray.dir).dot(n_entry).clamp(0.0, 1.0);
match trace_wavelength(
hit_point_entry,
ray.dir,
n_entry,
cos_i,
ctx.plane_soa,
ctx.nd,
) {
RayFate::EntryBlocked => Some(RayClassification::EntryBlocked),
RayFate::Leaked => Some(RayClassification::Windowed),
RayFate::ExitedUpward(exit_d) => {
let exit_dir = exit_d.dir;
let transmittance = exit_d.transmittance;
if !ray_is_visibly_returned(exit_dir, ctx.cam_forward, &ctx.lighting) {
return Some(RayClassification::Extinct);
}
let fate_f = trace_wavelength(
hit_point_entry,
ray.dir,
n_entry,
cos_i,
ctx.plane_soa,
ctx.n_f,
);
let fate_c = trace_wavelength(
hit_point_entry,
ray.dir,
n_entry,
cos_i,
ctx.plane_soa,
ctx.n_c,
);
let Some(fire) = (match (fate_f, fate_c) {
(RayFate::ExitedUpward(exit_f), RayFate::ExitedUpward(exit_c)) => {
if exit_f.facet_idx != exit_c.facet_idx || exit_f.bounces != exit_c.bounces {
None
} else {
let transmittance_f = exit_f.transmittance;
let transmittance_c = exit_c.transmittance;
let exit_cos_f = exit_f.exit_cos_theta;
let exit_cos_c = exit_c.exit_cos_theta;
let cos_sep = exit_f.dir.dot(exit_c.dir).clamp(-1.0, 1.0);
let angle_deg = cos_sep.acos().to_degrees();
let energy_weight = transmittance * transmittance_f * transmittance_c;
let radiance_weight = exit_cos_f.max(0.0) * exit_cos_c.max(0.0);
let weight = energy_weight * radiance_weight;
Some((angle_deg, weight))
}
}
_ => None,
}) else {
return Some(RayClassification::Returned(None));
};
Some(RayClassification::Returned(Some(fire)))
}
RayFate::Absorbed => Some(RayClassification::Extinct),
}
}