319 lines
8 KiB
Rust
319 lines
8 KiB
Rust
use crate::core::bxdf::BSDF;
|
|
use crate::core::material::MaterialTrait;
|
|
use crate::core::medium::{Medium, MediumInterface};
|
|
use crate::core::pbrt::Float;
|
|
use crate::geometry::{Normal3f, Point2f, Point3f, Point3fi, Ray, Vector3f, VectorLike};
|
|
use crate::lights::Light;
|
|
use crate::shapes::ShapeTrait;
|
|
|
|
use std::any::Any;
|
|
use std::sync::Arc;
|
|
|
|
#[derive(Default, Clone, Debug)]
|
|
pub struct InteractionData {
|
|
pub pi: Point3fi,
|
|
pub n: Normal3f,
|
|
pub time: Float,
|
|
pub wo: Vector3f,
|
|
pub medium_interface: Option<Arc<MediumInterface>>,
|
|
pub medium: Option<Arc<Medium>>,
|
|
}
|
|
|
|
pub trait Interaction: Send + Sync {
|
|
fn get_common(&self) -> &InteractionData;
|
|
fn get_common_mut(&mut self) -> &mut InteractionData;
|
|
fn as_any(&self) -> &dyn Any;
|
|
|
|
fn p(&self) -> Point3f {
|
|
self.get_common().pi.into()
|
|
}
|
|
fn pi(&self) -> Point3fi {
|
|
self.get_common().pi
|
|
}
|
|
fn time(&self) -> Float {
|
|
self.get_common().time
|
|
}
|
|
fn wo(&self) -> Vector3f {
|
|
self.get_common().wo
|
|
}
|
|
|
|
fn n(&self) -> Normal3f {
|
|
self.get_common().n
|
|
}
|
|
|
|
fn is_surface_interaction(&self) -> bool {
|
|
self.as_any().is::<SurfaceInteraction>()
|
|
}
|
|
|
|
fn is_medium_interaction(&self) -> bool {
|
|
self.as_any().is::<MediumInteraction>()
|
|
}
|
|
|
|
fn get_medium(&self, w: Vector3f) -> Option<Arc<Medium>>;
|
|
|
|
fn spawn_ray(&self, d: Vector3f) -> Ray;
|
|
|
|
fn spawn_ray_to_point(&self, p2: Point3f) -> Ray {
|
|
let origin = self.p();
|
|
let direction = p2 - origin;
|
|
Ray {
|
|
o: origin,
|
|
d: direction,
|
|
time: self.time(),
|
|
medium: self.get_medium(direction),
|
|
differential: None,
|
|
}
|
|
}
|
|
|
|
fn spawn_ray_to_interaction(&self, other: &dyn Interaction) -> Ray {
|
|
self.spawn_ray_to_point(other.p())
|
|
}
|
|
|
|
fn offset_ray_vector(&self, w: Vector3f) -> Point3f {
|
|
Ray::offset_origin(&self.pi(), &self.n(), &w)
|
|
}
|
|
|
|
fn offset_ray_point(&self, pt: Point3f) -> Point3f {
|
|
self.offset_ray_vector(pt - self.p())
|
|
}
|
|
}
|
|
|
|
#[derive(Default, Clone, Debug)]
|
|
pub struct ShadingGeometry {
|
|
pub n: Normal3f,
|
|
pub dpdu: Vector3f,
|
|
pub dpdv: Vector3f,
|
|
pub dndu: Normal3f,
|
|
pub dndv: Normal3f,
|
|
}
|
|
|
|
#[derive(Default, Debug, Clone)]
|
|
pub struct SurfaceInteraction {
|
|
pub common: InteractionData,
|
|
pub uv: Point2f,
|
|
pub dpdu: Vector3f,
|
|
pub dpdv: Vector3f,
|
|
pub dndu: Normal3f,
|
|
pub dndv: Normal3f,
|
|
pub shading: ShadingGeometry,
|
|
pub medium_interface: Option<Arc<MediumInterface>>,
|
|
pub face_index: usize,
|
|
pub area_light: Option<Arc<Light>>,
|
|
pub material: Option<Arc<dyn MaterialTrait>>,
|
|
pub dpdx: Vector3f,
|
|
pub dpdy: Vector3f,
|
|
pub dudx: Float,
|
|
pub dvdx: Float,
|
|
pub dudy: Float,
|
|
pub dvdy: Float,
|
|
pub shape: Option<Arc<dyn ShapeTrait>>,
|
|
pub bsdf: Option<BSDF>,
|
|
}
|
|
|
|
impl SurfaceInteraction {
|
|
pub fn set_intersection_properties(
|
|
&mut self,
|
|
mtl: Arc<dyn MaterialTrait>,
|
|
area: Arc<Light>,
|
|
prim_medium_interface: Option<Arc<MediumInterface>>,
|
|
ray_medium: Arc<Medium>,
|
|
) {
|
|
self.material = Some(mtl);
|
|
self.area_light = Some(area);
|
|
if prim_medium_interface.as_ref().map_or(false, |mi| mi.is_medium_transition()) {
|
|
self.common.medium_interface = prim_medium_interface;
|
|
} else {
|
|
self.common.medium = Some(ray_medium);
|
|
}
|
|
}
|
|
}
|
|
|
|
pub struct PhaseFunction;
|
|
|
|
pub struct MediumInteraction {
|
|
pub common: InteractionData,
|
|
pub medium: Arc<Medium>,
|
|
pub phase: PhaseFunction,
|
|
}
|
|
|
|
impl Interaction for SurfaceInteraction {
|
|
fn get_common(&self) -> &InteractionData {
|
|
&self.common
|
|
}
|
|
|
|
fn get_common_mut(&mut self) -> &mut InteractionData {
|
|
&mut self.common
|
|
}
|
|
|
|
fn as_any(&self) -> &dyn Any {
|
|
self
|
|
}
|
|
|
|
fn get_medium(&self, w: Vector3f) -> Option<Arc<Medium>> {
|
|
self.medium_interface.as_ref().and_then(|interface| {
|
|
if self.n().dot(w.into()) > 0.0 {
|
|
interface.outside.clone()
|
|
} else {
|
|
interface.inside.clone()
|
|
}
|
|
})
|
|
}
|
|
|
|
fn spawn_ray(&self, d: Vector3f) -> Ray {
|
|
let mut ray = Ray::spawn(&self.pi(), &self.n(), self.time(), d);
|
|
ray.medium = self.get_medium(d);
|
|
ray
|
|
}
|
|
|
|
fn spawn_ray_to_point(&self, p2: Point3f) -> Ray {
|
|
let mut ray = Ray::spawn_to_point(&self.pi(), &self.n(), self.time(), p2);
|
|
ray.medium = self.get_medium(ray.d);
|
|
ray
|
|
}
|
|
|
|
fn spawn_ray_to_interaction(&self, other: &dyn Interaction) -> Ray {
|
|
// Check if the other interaction is a surface to use the robust spawn method
|
|
if let Some(si_to) = other.as_any().downcast_ref::<SurfaceInteraction>() {
|
|
let mut ray = Ray::spawn_to_interaction(
|
|
&self.pi(),
|
|
&self.n(),
|
|
self.time(),
|
|
&si_to.pi(),
|
|
&si_to.n(),
|
|
);
|
|
ray.medium = self.get_medium(ray.d);
|
|
ray
|
|
} else {
|
|
// Fallback for non-surface interactions
|
|
self.spawn_ray_to_point(other.p())
|
|
}
|
|
}
|
|
}
|
|
|
|
impl SurfaceInteraction {
|
|
pub fn new(
|
|
pi: Point3fi,
|
|
uv: Point2f,
|
|
wo: Vector3f,
|
|
dpdu: Vector3f,
|
|
dpdv: Vector3f,
|
|
dndu: Normal3f,
|
|
dndv: Normal3f,
|
|
time: Float,
|
|
flip: bool,
|
|
) -> Self {
|
|
let mut n = Normal3f::from(dpdu.cross(dpdv).normalize());
|
|
let mut shading_n = n;
|
|
|
|
if flip {
|
|
n *= -1.0;
|
|
shading_n *= -1.0;
|
|
}
|
|
|
|
Self {
|
|
common: InteractionData { pi, n, time, wo, medium_interface: None, medium: None },
|
|
uv,
|
|
dpdu,
|
|
dpdv,
|
|
dndu,
|
|
dndv,
|
|
shading: ShadingGeometry {
|
|
n: shading_n,
|
|
dpdu,
|
|
dpdv,
|
|
dndu,
|
|
dndv,
|
|
},
|
|
medium_interface: None,
|
|
material: None,
|
|
face_index: 0,
|
|
area_light: None,
|
|
dpdx: Vector3f::zero(),
|
|
dpdy: Vector3f::zero(),
|
|
dudx: 0.0,
|
|
dudy: 0.0,
|
|
dvdx: 0.0,
|
|
dvdy: 0.0,
|
|
shape: None,
|
|
bsdf: None,
|
|
}
|
|
}
|
|
|
|
pub fn new_with_face(
|
|
pi: Point3fi,
|
|
uv: Point2f,
|
|
wo: Vector3f,
|
|
dpdu: Vector3f,
|
|
dpdv: Vector3f,
|
|
dndu: Normal3f,
|
|
dndv: Normal3f,
|
|
time: Float,
|
|
flip: bool,
|
|
face_index: usize,
|
|
) -> Self {
|
|
let mut si = Self::new(pi, uv, wo, dpdu, dpdv, dndu, dndv, time, flip);
|
|
si.face_index = face_index;
|
|
si
|
|
}
|
|
|
|
pub fn set_shading_geometry(
|
|
&mut self,
|
|
ns: Normal3f,
|
|
dpdus: Vector3f,
|
|
dpdvs: Vector3f,
|
|
dndus: Normal3f,
|
|
dndvs: Normal3f,
|
|
orientation: bool,
|
|
) {
|
|
self.shading.n = ns;
|
|
if orientation {
|
|
self.common.n = self.n().face_forward(self.shading.n.into());
|
|
}
|
|
self.shading.dpdu = dpdus;
|
|
self.shading.dpdv = dpdvs;
|
|
self.shading.dndu = dndus.into();
|
|
self.shading.dndv = dndvs.into();
|
|
}
|
|
|
|
pub fn new_simple(pi: Point3fi, n: Normal3f, uv: Point2f) -> Self {
|
|
let mut si = Self::default();
|
|
si.common = InteractionData {
|
|
pi,
|
|
n,
|
|
time: 0.,
|
|
wo: Vector3f::zero(),
|
|
medium_interface: None,
|
|
medium: None,
|
|
};
|
|
si.uv = uv;
|
|
si
|
|
}
|
|
}
|
|
|
|
impl Interaction for MediumInteraction {
|
|
fn get_common(&self) -> &InteractionData {
|
|
&self.common
|
|
}
|
|
|
|
fn get_common_mut(&mut self) -> &mut InteractionData {
|
|
&mut self.common
|
|
}
|
|
|
|
fn as_any(&self) -> &dyn Any {
|
|
self
|
|
}
|
|
|
|
fn get_medium(&self, _w: Vector3f) -> Option<Arc<Medium>> {
|
|
Some(self.medium.clone())
|
|
}
|
|
|
|
fn spawn_ray(&self, d: Vector3f) -> Ray {
|
|
Ray {
|
|
o: self.p(),
|
|
d,
|
|
time: self.time(),
|
|
medium: Some(self.medium.clone()),
|
|
differential: None,
|
|
}
|
|
}
|
|
}
|