///|
/// Participating media (volume rendering) support.
/// Simulates fog, smoke, and volumetric scattering through ray marching.
pub(all) struct VolumeBox {
min_point : Vec3
max_point : Vec3
density : Double
albedo : Vec3
} derive(Debug)
pub fn VolumeBox::new(min_point~ : Vec3, max_point~ : Vec3, density~ : Double, albedo~ : Vec3) -> VolumeBox {
{ min_point, max_point, density, albedo }
}
fn constant_medium_aabb(vol : VolumeBox) -> AABB {
{ min: vol.min_point, max: vol.max_point }
}
pub fn ray_march_volume(
vol : VolumeBox,
r : Ray,
t_min : Double,
t_max : Double
) -> (Vec3, Double) {
let aabb = constant_medium_aabb(vol)
if !(aabb.hit(r, t_min=t_min, t_max=t_max)) {
return ({ x: 0.0, y: 0.0, z: 0.0 }, 1.0)
}
let inv_dir = { x: 1.0 / r.dir.x, y: 1.0 / r.dir.y, z: 1.0 / r.dir.z }
let t0 = (vol.min_point.x - r.orig.x) * inv_dir.x
let t1 = (vol.max_point.x - r.orig.x) * inv_dir.x
let mut t_entry = if inv_dir.x > 0.0 { t0 } else { t1 }
let mut t_exit = if inv_dir.x > 0.0 { t1 } else { t0 }
let ty0 = (vol.min_point.y - r.orig.y) * inv_dir.y
let ty1 = (vol.max_point.y - r.orig.y) * inv_dir.y
let tymin = if inv_dir.y > 0.0 { ty0 } else { ty1 }
let tymax = if inv_dir.y > 0.0 { ty1 } else { ty0 }
t_entry = t_entry.max(tymin)
t_exit = t_exit.min(tymax)
let tz0 = (vol.min_point.z - r.orig.z) * inv_dir.z
let tz1 = (vol.max_point.z - r.orig.z) * inv_dir.z
let tzmin = if inv_dir.z > 0.0 { tz0 } else { tz1 }
let tzmax = if inv_dir.z > 0.0 { tz1 } else { tz0 }
t_entry = t_entry.max(tzmin)
t_exit = t_exit.min(tzmax)
t_entry = t_entry.max(t_min)
t_exit = t_exit.min(t_max)
if t_entry >= t_exit {
return ({ x: 0.0, y: 0.0, z: 0.0 }, 1.0)
}
let dist_inside = t_exit - t_entry
let optical_depth = dist_inside * vol.density
let transmittance = @math.exp(-optical_depth)
let emitted = vol.albedo.mul_scalar(1.0 - transmittance)
(emitted, transmittance)
}
pub fn exponential_fog(
color : Vec3,
dist : Double,
fog_density : Double,
fog_color : Vec3
) -> Vec3 {
let transmittance = @math.exp(-dist * fog_density)
color.mul_scalar(transmittance) + fog_color.mul_scalar(1.0 - transmittance)
}
pub fn layered_fog(
color : Vec3,
pos_y : Double,
fog_base : Double,
fog_top : Double,
fog_density : Double,
fog_color : Vec3
) -> Vec3 {
let height_factor = ((pos_y - fog_base) / (fog_top - fog_base)).clamp(min=0.0, max=0.999)
let density = fog_density * (1.0 - height_factor)
let transmittance = @math.exp(-density)
color.mul_scalar(transmittance) + fog_color.mul_scalar(1.0 - transmittance)
}
pub fn beer_lambert_absorption(color : Vec3, dist : Double, density : Vec3) -> Vec3 {
{
x: color.x * @math.exp(-dist * density.x),
y: color.y * @math.exp(-dist * density.y),
z: color.z * @math.exp(-dist * density.z),
}
}