use super::*;
pub struct Vertex<'a> {
pub h: Hit<'a>,
pub gathered: Color,
pub pdf_fwd: Float,
pub pdf_bck: Float,
}
impl<'a> Vertex<'a> {
pub fn camera(xo: Point, gathered: Color) -> Self {
let h = Hit::new(
0.0,
&Material::Blank,
Direction::NEG_X,
xo,
Vec3::ZERO,
Normal::X,
Normal::X,
Vec2::X,
).unwrap();
Self {
h,
gathered,
pdf_bck: 1.0,
pdf_fwd: 0.0,
}
}
pub fn light(mut h: Hit<'a>, light: &'a dyn Sampleable, gathered: Color, pdf_bck: Float) -> Self {
h.light = Some(light);
Self {
h,
gathered,
pdf_bck,
pdf_fwd: 1.0 / light.area(),
}
}
pub fn surface(
h: Hit<'a>,
gathered: Color,
pdf_fwd: Float,
prev: &Vertex,
) -> Self {
let pdf_fwd = if h.material.is_delta() {
0.0
} else {
let xo = prev.h.p;
let xi = h.p;
let wi = (xi - xo).normalize();
let ng = h.ng;
pdf_fwd * wi.dot(ng).abs() / xi.distance_squared(xo)
};
Self {
h,
gathered,
pdf_fwd,
pdf_bck: 0.0,
}
}
fn material(&self) -> &Material {
self.h.material
}
pub fn is_surface(&self) -> bool {
!matches!(self.material(), Material::Blank | Material::Volumetric(..))
}
pub fn is_light(&self) -> bool {
self.h.is_light()
}
pub fn is_delta(&self) -> bool {
self.material().is_delta()
}
pub fn emittance(&self) -> Color {
self.material().emit(&self.h)
}
pub fn shading_cosine(&self, wi: Direction, ns: Normal) -> Float {
if self.is_surface() {
self.material().shading_cosine(wi, ns)
} else {
1.0
}
}
pub fn bsdf(&self, prev: &Vertex, next: &Vertex, mode: Transport) -> Color {
let wo = (self.h.p - prev.h.p).normalize();
let wi = (next.h.p - self.h.p).normalize();
self.material().bsdf_f(wo, wi, mode, &self.h)
}
pub fn solid_angle_to_area(&self, pdf: Float, next: &Vertex) -> Float {
let xo = self.h.p;
let xi = next.h.p;
let wi = (xi - xo).normalize();
if next.is_surface() {
let ng = next.h.ng;
pdf * wi.dot(ng).abs() / xi.distance_squared(xo)
} else {
pdf / xi.distance_squared(xo)
}
}
pub fn g(&self, v: &Vertex, scene: &Scene) -> Color {
let xo = self.h.p;
let xi = v.h.p;
let no = self.h.ns;
let ni = v.h.ns;
let wi = (xi - xo).normalize();
let mut g = 1.0 / xo.distance_squared(xi);
if self.is_surface() {
g *= no.dot(wi).abs();
}
if v.is_surface() {
g *= ni.dot(wi).abs();
}
g * scene.transmittance(xo.distance(xi))
}
pub fn pdf_area(&self, prev: &Vertex, next: &Vertex, mode: Transport) -> Float {
let ho = &self.h;
let xo = prev.h.p;
let xi = ho.p;
let wo = xi - xo;
let ro = Ray::new(xo, wo);
let xii = next.h.p;
let wi = xii - xi;
let ri = Ray::new(xi, wi);
let wi = ri.dir;
let angle_pdf = match self.material().bsdf_pdf(ho, &ro) {
None => 0.0,
Some(pdf) => pdf.value_for(&ri, matches!(mode, Transport::Importance))
};
let ng = next.h.ng;
angle_pdf * wi.dot(ng).abs() / xi.distance_squared(xii)
}
pub fn pdf_light_origin(&self) -> Float {
self.h.light.map_or(0.0, |light| 1.0 / light.area())
}
pub fn pdf_light_leaving(&self, next: &Vertex) -> Float {
if let Some(light) = self.h.light {
let xo = self.h.p;
let xi = next.h.p;
let wi = xi - xo;
let ri = Ray::new(xo, wi);
let wi = ri.dir;
let ng = self.h.ng;
let (_, pdf_dir) = light.sample_leaving_pdf(&ri, ng);
let ng = next.h.ng;
pdf_dir * wi.dot(ng).abs() / xo.distance_squared(xi)
} else {
0.0
}
}
}