full color!

This commit is contained in:
numzero 2025-11-25 15:13:46 +03:00
parent ca460bc0d9
commit 13fb710dc7
2 changed files with 51 additions and 25 deletions

View File

@ -234,30 +234,45 @@ impl Core {
const BASE: Sphere = Sphere { const BASE: Sphere = Sphere {
position: vec3(0., 0., -BASE_R), position: vec3(0., 0., -BASE_R),
radius: BASE_R, radius: BASE_R,
color: vec3(0.1, 0.5, 0.0),
}; };
fn sphere(pos: Vec3) -> Sphere { fn sphere(pos: Vec3, color: Vec3) -> Sphere {
Sphere { Sphere {
position: pos, position: pos,
radius: BASE_POS.distance(pos) - BASE_R, radius: BASE_POS.distance(pos) - BASE_R,
color,
} }
} }
let scene = Scene { let scene = Scene {
objects: vec![ objects: vec![
BASE, BASE,
sphere(vec3(0., 0., 0.1)), sphere(vec3(0., 0., 0.1), vec3(1., 0.3, 0.0)),
sphere(vec3(0.3, 0., 0.1)), sphere(vec3(0.3, 0., 0.1), vec3(0.3, 1., 0.0)),
sphere(vec3(0.1, 0.3, 0.1)), sphere(vec3(0.1, 0.3, 0.1), vec3(0.3, 0.0, 1.)),
], ],
}; };
#[derive(Debug, Clone, Copy)]
struct LightHit<'a> {
incident: Ray,
normal: Vec3,
light: Vec3,
object: &'a Sphere,
}
let mut prng = rand_pcg::Pcg64::new(42, 0); let mut prng = rand_pcg::Pcg64::new(42, 0);
let source_rays: Vec<Ray> = (0..10240).map(|_| source.make_ray(&mut prng)).collect(); let source_rays: Vec<Ray> = (0..10240).map(|_| source.make_ray(&mut prng)).collect();
let camera_rays: Vec<Ray> = (0..10240).map(|_| camera.make_ray(&mut prng)).collect(); let camera_rays: Vec<Ray> = (0..10240).map(|_| camera.make_ray(&mut prng)).collect();
let mut source_ray_display: Vec<Vertex> = Vec::with_capacity(source_rays.len()); let mut source_ray_display: Vec<Vertex> = Vec::with_capacity(source_rays.len());
let mut hits: Vec<Hit> = Vec::with_capacity(source_rays.len()); let mut hits: Vec<LightHit> = Vec::with_capacity(source_rays.len());
for ray in source_rays { for ray in source_rays {
let light = Vec3::splat(1.);
if let Some(hit) = scene.trace_ray(ray) { if let Some(hit) = scene.trace_ray(ray) {
hits.push(hit); hits.push(LightHit {
incident: hit.incident,
normal: hit.normal,
light,
object: hit.object,
});
if args.show_hit_emission { if args.show_hit_emission {
source_ray_display.extend([ source_ray_display.extend([
Vertex { Vertex {
@ -274,7 +289,7 @@ impl Core {
source_ray_display.extend([ source_ray_display.extend([
Vertex { Vertex {
pos: hit.incident.base - 0.02 * hit.incident.dir, pos: hit.incident.base - 0.02 * hit.incident.dir,
color: vec3(0., 0., 1.), color: light,
}, },
Vertex { Vertex {
pos: hit.incident.base, pos: hit.incident.base,
@ -300,20 +315,26 @@ impl Core {
if args.reflections > 0 { if args.reflections > 0 {
let mut hits1 = hits.clone(); let mut hits1 = hits.clone();
for _ in 0..args.reflections { for _ in 0..args.reflections {
let mut hits2: Vec<Hit> = Vec::with_capacity(hits1.len()); let mut hits2: Vec<LightHit> = Vec::with_capacity(hits1.len());
for hit in &hits1 { for hit in &hits1 {
let reflector = Lambertian; let reflector = Lambertian;
let reflected = reflector.reflect(&mut prng, hit.normal, hit.incident.dir); let reflected = reflector.reflect(&mut prng, hit.normal, hit.incident.dir);
let light = hit.light * hit.object.color;
let ray = Ray::new(hit.incident.base, reflected); let ray = Ray::new(hit.incident.base, reflected);
let Some(hit2) = scene.trace_ray(ray) else { let Some(hit2) = scene.trace_ray(ray) else {
continue; continue;
}; };
hits2.push(hit2); hits2.push(LightHit {
incident: hit2.incident,
normal: hit2.normal,
light: light,
object: hit2.object,
});
if args.show_indirect_hit { if args.show_indirect_hit {
source_ray_display.extend([ source_ray_display.extend([
Vertex { Vertex {
pos: hit2.incident.base - 0.02 * hit2.incident.dir, pos: hit2.incident.base - 0.02 * hit2.incident.dir,
color: vec3(1., 0., 1.), color: light,
}, },
Vertex { Vertex {
pos: hit2.incident.base, pos: hit2.incident.base,
@ -330,8 +351,8 @@ impl Core {
let sigma2 = args.accum_sigma.powi(2); let sigma2 = args.accum_sigma.powi(2);
let accum_normalizator = (2. * PI * sigma2).recip(); let accum_normalizator = (2. * PI * sigma2).recip();
let hits = &hits; let hits = &hits;
move |hit: Hit| -> f32 { move |hit: Hit| -> Vec3 {
let mut total_cd = 0.0f32; let mut total_cd = Vec3::splat(0.);
for light_hit in hits { for light_hit in hits {
let d2 = hit.incident.base.distance_squared(light_hit.incident.base); let d2 = hit.incident.base.distance_squared(light_hit.incident.base);
if d2 > 9. * sigma2 { if d2 > 9. * sigma2 {
@ -344,16 +365,18 @@ impl Core {
assert!(normal.is_normalized()); assert!(normal.is_normalized());
assert!(hit.incident.dir.is_normalized()); assert!(hit.incident.dir.is_normalized());
let reflector = Lambertian; let reflector = Lambertian;
let in_lm = 1.0; let in_lm = light_hit.light;
let out_cd = let out_cd = in_lm
in_lm * reflector.brdf(normal, light_hit.incident.dir, -hit.incident.dir); * reflector.brdf(normal, light_hit.incident.dir, -hit.incident.dir)
* hit.object.color;
let weight = accum_normalizator * (-0.5 * d2 / sigma2).exp(); let weight = accum_normalizator * (-0.5 * d2 / sigma2).exp();
total_cd += weight * out_cd; total_cd += weight * out_cd;
} }
total_cd * args.accum_scale total_cd * args.accum_scale
} }
}; };
let colormap = |light: f32| { let colormap = |light: Vec3| {
let light = light.dot(Vec3::splat(1. / 3.));
let brightness = 3. * (1. - (1. + light).recip()); let brightness = 3. * (1. - (1. + light).recip());
vec3(brightness, brightness - 1., brightness - 2.) vec3(brightness, brightness - 1., brightness - 2.)
.clamp(Vec3::splat(0.), Vec3::splat(1.)) .clamp(Vec3::splat(0.), Vec3::splat(1.))
@ -397,11 +420,11 @@ impl Core {
if !dir.is_finite() { if !dir.is_finite() {
dir = camera.direction(); dir = camera.direction();
} }
let light = light_at(Hit { let color = light_at(Hit {
incident: Ray::new(pos, dir), incident: Ray::new(pos, dir),
normal, normal,
object: obj,
}); });
let color = Vec3::splat(light);
render::faces::Vertex { pos, color } render::faces::Vertex { pos, color }
}) })
.collect::<Vec<_>>(), .collect::<Vec<_>>(),

View File

@ -76,6 +76,7 @@ impl Source {
pub struct Sphere { pub struct Sphere {
pub position: Vec3, pub position: Vec3,
pub radius: f32, pub radius: f32,
pub color: Vec3,
} }
struct Hit1 { struct Hit1 {
@ -113,26 +114,28 @@ pub struct Scene {
} }
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub struct Hit { pub struct Hit<'a> {
pub incident: Ray, pub incident: Ray,
pub normal: Vec3, pub normal: Vec3,
pub object: &'a Sphere,
} }
impl Scene { impl Scene {
pub fn trace_ray(&self, ray: Ray) -> Option<Hit> { pub fn trace_ray(&self, ray: Ray) -> Option<Hit<'_>> {
const EPS: f32 = -1e-3; const EPS: f32 = -1e-3;
let hit = self let hit = self
.objects .objects
.iter() .iter()
.filter_map(|obj| obj.trace_ray(ray)) .filter_map(|obj| obj.trace_ray(ray).map(|hit| (obj, hit)))
.filter(|h| h.dist >= EPS) .filter(|(_obj, h)| h.dist >= EPS)
.min_by(|a, b| f32::total_cmp(&a.dist, &b.dist))?; .min_by(|a, b| f32::total_cmp(&a.1.dist, &b.1.dist))?;
Some(Hit { Some(Hit {
incident: Ray { incident: Ray {
base: hit.pos, base: hit.1.pos,
dir: ray.dir, dir: ray.dir,
}, },
normal: hit.normal, normal: hit.1.normal,
object: hit.0,
}) })
} }
} }