use crate::math::{halton, reflect, saturate, Ray};
use crate::render::camera::Camera;
use crate::render::framebuffer::Framebuffer;
use crate::render::scene::Scene;
use glam::{Vec2, Vec3};
use rayon::prelude::*;
const MAX_BOUNCES: u32 = 2;
const T_MIN: f32 = 1e-3;
const T_MAX: f32 = 1e3;
pub const MIN_SPP: u32 = 1;
pub const MAX_SPP: u32 = 16;
pub fn render(scene: &Scene, camera: &Camera, fb: &mut Framebuffer, samples_per_pixel: u32) {
let w = fb.width;
let h = fb.height;
let spp = samples_per_pixel.clamp(MIN_SPP, MAX_SPP);
let offsets: Vec<Vec2> = if spp == 1 {
vec![Vec2::splat(0.5)]
} else {
(1..=spp).map(|i| Vec2::new(halton(i, 2), halton(i, 3))).collect()
};
let inv_spp = 1.0 / spp as f32;
fb.pixels
.par_chunks_mut(w as usize)
.enumerate()
.for_each(|(y, row)| {
for x in 0..w {
let mut acc = Vec3::ZERO;
for &j in &offsets {
let ray = camera.ray(x, y as u16, w, h, j);
acc += trace(scene, &ray, MAX_BOUNCES);
}
row[x as usize] = acc * inv_spp;
}
});
}
fn trace(scene: &Scene, ray: &Ray, depth: u32) -> Vec3 {
let Some(hit) = scene.hit(ray, T_MIN, T_MAX) else {
return scene.sky(ray.dir);
};
let mat = &scene.materials[hit.material];
let albedo = mat.sample_albedo(hit.point);
let mut color = scene.ambient * albedo;
for light in &scene.lights {
let to_light_vec = light.position - hit.point;
let dist = to_light_vec.length();
let l = to_light_vec / dist;
let shadow_ray = Ray { origin: hit.point + hit.normal * 1e-3, dir: l };
let in_shadow = scene
.hit(&shadow_ray, T_MIN, dist - 1e-3)
.is_some();
if in_shadow {
continue;
}
let atten = light.intensity / (1.0 + 0.05 * dist * dist);
let ndotl = saturate(hit.normal.dot(l));
let diffuse = albedo * ndotl;
let view = -ray.dir;
let half = (l + view).normalize();
let ndoth = saturate(hit.normal.dot(half));
let spec = ndoth.powf(mat.shininess) * mat.specular;
color += (diffuse + Vec3::splat(spec)) * light.color * atten;
}
if depth > 0 && mat.reflectivity > 0.0 {
let refl_dir = reflect(ray.dir, hit.normal).normalize();
let refl_ray = Ray {
origin: hit.point + hit.normal * 1e-3,
dir: refl_dir,
};
let refl_color = trace(scene, &refl_ray, depth - 1);
color = color.lerp(refl_color, mat.reflectivity);
}
Vec3::new(color.x.powf(1.0 / 2.2), color.y.powf(1.0 / 2.2), color.z.powf(1.0 / 2.2))
}