First renders with lighting. Yay!
The lighting is super crappy, and pretty much hacked in. Will need to redo this properly soon. However, this verifies that certain other parts of the code are (mostly) working properly.
This commit is contained in:
parent
3b85b60a62
commit
fd195576d1
|
@ -22,6 +22,9 @@ pub struct Assembly {
|
||||||
|
|
||||||
// Object accel
|
// Object accel
|
||||||
pub object_accel: BVH,
|
pub object_accel: BVH,
|
||||||
|
|
||||||
|
// Light accel
|
||||||
|
pub light_accel: Vec<Instance>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Boundable for Assembly {
|
impl Boundable for Assembly {
|
||||||
|
@ -126,20 +129,37 @@ impl AssemblyBuilder {
|
||||||
self.objects.shrink_to_fit();
|
self.objects.shrink_to_fit();
|
||||||
self.assemblies.shrink_to_fit();
|
self.assemblies.shrink_to_fit();
|
||||||
|
|
||||||
// Build object accel
|
// Calculate instance bounds, used for building object accel and
|
||||||
|
// (TODO) light accel.
|
||||||
let (bis, bbs) = self.instance_bounds();
|
let (bis, bbs) = self.instance_bounds();
|
||||||
|
|
||||||
|
// Build object accel
|
||||||
let object_accel = BVH::from_objects(&mut self.instances[..],
|
let object_accel = BVH::from_objects(&mut self.instances[..],
|
||||||
1,
|
1,
|
||||||
|inst| &bbs[bis[inst.id]..bis[inst.id + 1]]);
|
|inst| &bbs[bis[inst.id]..bis[inst.id + 1]]);
|
||||||
|
|
||||||
println!("Assembly BVH Depth: {}", object_accel.tree_depth());
|
println!("Assembly BVH Depth: {}", object_accel.tree_depth());
|
||||||
|
|
||||||
|
// Build light accel
|
||||||
|
// TODO: build light tree instead of stupid vec
|
||||||
|
let light_accel = {
|
||||||
|
let mut light_accel = Vec::new();
|
||||||
|
for inst in self.instances.iter() {
|
||||||
|
if let InstanceType::Object = inst.instance_type {
|
||||||
|
if let Object::Light(_) = self.objects[inst.data_index] {
|
||||||
|
light_accel.push(*inst);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
light_accel
|
||||||
|
};
|
||||||
|
|
||||||
Assembly {
|
Assembly {
|
||||||
instances: self.instances,
|
instances: self.instances,
|
||||||
xforms: self.xforms,
|
xforms: self.xforms,
|
||||||
objects: self.objects,
|
objects: self.objects,
|
||||||
assemblies: self.assemblies,
|
assemblies: self.assemblies,
|
||||||
object_accel: object_accel,
|
object_accel: object_accel,
|
||||||
|
light_accel: light_accel,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ use std::fmt::Debug;
|
||||||
|
|
||||||
pub use self::sphere_light::SphereLight;
|
pub use self::sphere_light::SphereLight;
|
||||||
|
|
||||||
use math::{Vector, Point};
|
use math::{Vector, Point, Matrix4x4};
|
||||||
use color::SpectralSample;
|
use color::SpectralSample;
|
||||||
use boundable::Boundable;
|
use boundable::Boundable;
|
||||||
|
|
||||||
|
@ -20,6 +20,7 @@ pub trait LightSource: Boundable + Debug + Sync {
|
||||||
/// Returns: The light arriving at the point arr, the vector to use for
|
/// Returns: The light arriving at the point arr, the vector to use for
|
||||||
/// shadow testing, and the pdf of the sample.
|
/// shadow testing, and the pdf of the sample.
|
||||||
fn sample(&self,
|
fn sample(&self,
|
||||||
|
space: &Matrix4x4,
|
||||||
arr: Point,
|
arr: Point,
|
||||||
u: f32,
|
u: f32,
|
||||||
v: f32,
|
v: f32,
|
||||||
|
@ -37,6 +38,7 @@ pub trait LightSource: Boundable + Debug + Sync {
|
||||||
/// source). No guarantees are made about the correctness of the return
|
/// source). No guarantees are made about the correctness of the return
|
||||||
/// value if they are not valid.
|
/// value if they are not valid.
|
||||||
fn sample_pdf(&self,
|
fn sample_pdf(&self,
|
||||||
|
space: &Matrix4x4,
|
||||||
arr: Point,
|
arr: Point,
|
||||||
sample_dir: Vector,
|
sample_dir: Vector,
|
||||||
sample_u: f32,
|
sample_u: f32,
|
||||||
|
@ -54,7 +56,14 @@ pub trait LightSource: Boundable + Debug + Sync {
|
||||||
/// - v: Random parameter V.
|
/// - v: Random parameter V.
|
||||||
/// - wavelength: The hero wavelength of light to sample at.
|
/// - wavelength: The hero wavelength of light to sample at.
|
||||||
/// - time: The time to sample at.
|
/// - time: The time to sample at.
|
||||||
fn outgoing(&self, dir: Vector, u: f32, v: f32, wavelength: f32, time: f32) -> SpectralSample;
|
fn outgoing(&self,
|
||||||
|
space: &Matrix4x4,
|
||||||
|
dir: Vector,
|
||||||
|
u: f32,
|
||||||
|
v: f32,
|
||||||
|
wavelength: f32,
|
||||||
|
time: f32)
|
||||||
|
-> SpectralSample;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use math::{Vector, Point, coordinate_system_from_vector};
|
use math::{Vector, Point, Matrix4x4, coordinate_system_from_vector};
|
||||||
use bbox::BBox;
|
use bbox::BBox;
|
||||||
use boundable::Boundable;
|
use boundable::Boundable;
|
||||||
use color::{XYZ, SpectralSample, Color};
|
use color::{XYZ, SpectralSample, Color};
|
||||||
|
@ -35,14 +35,16 @@ impl SphereLight {
|
||||||
|
|
||||||
impl LightSource for SphereLight {
|
impl LightSource for SphereLight {
|
||||||
fn sample(&self,
|
fn sample(&self,
|
||||||
|
space: &Matrix4x4,
|
||||||
arr: Point,
|
arr: Point,
|
||||||
u: f32,
|
u: f32,
|
||||||
v: f32,
|
v: f32,
|
||||||
wavelength: f32,
|
wavelength: f32,
|
||||||
time: f32)
|
time: f32)
|
||||||
-> (SpectralSample, Vector, f32) {
|
-> (SpectralSample, Vector, f32) {
|
||||||
|
// TODO: use transform space correctly
|
||||||
|
let pos = Point::new(0.0, 0.0, 0.0) * space.inverse();
|
||||||
// Calculate time interpolated values
|
// Calculate time interpolated values
|
||||||
let pos = Point::new(0.0, 0.0, 0.0); // Light position is always at origin
|
|
||||||
let radius: f64 = lerp_slice(&self.radii, time) as f64;
|
let radius: f64 = lerp_slice(&self.radii, time) as f64;
|
||||||
let col = lerp_slice(&self.colors, time);
|
let col = lerp_slice(&self.colors, time);
|
||||||
let surface_area_inv: f64 = 1.0 / (4.0 * PI_64 * radius * radius);
|
let surface_area_inv: f64 = 1.0 / (4.0 * PI_64 * radius * radius);
|
||||||
|
@ -104,6 +106,7 @@ impl LightSource for SphereLight {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sample_pdf(&self,
|
fn sample_pdf(&self,
|
||||||
|
space: &Matrix4x4,
|
||||||
arr: Point,
|
arr: Point,
|
||||||
sample_dir: Vector,
|
sample_dir: Vector,
|
||||||
sample_u: f32,
|
sample_u: f32,
|
||||||
|
@ -111,7 +114,8 @@ impl LightSource for SphereLight {
|
||||||
wavelength: f32,
|
wavelength: f32,
|
||||||
time: f32)
|
time: f32)
|
||||||
-> f32 {
|
-> f32 {
|
||||||
let pos = Point::new(0.0, 0.0, 0.0); // Light position is always at origin
|
// TODO: use transform space correctly
|
||||||
|
let pos = Point::new(0.0, 0.0, 0.0) * space.inverse();
|
||||||
let radius: f64 = lerp_slice(&self.radii, time) as f64;
|
let radius: f64 = lerp_slice(&self.radii, time) as f64;
|
||||||
|
|
||||||
let d2: f64 = (pos - arr).length2() as f64; // Distance from center of sphere squared
|
let d2: f64 = (pos - arr).length2() as f64; // Distance from center of sphere squared
|
||||||
|
@ -129,7 +133,15 @@ impl LightSource for SphereLight {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn outgoing(&self, dir: Vector, u: f32, v: f32, wavelength: f32, time: f32) -> SpectralSample {
|
fn outgoing(&self,
|
||||||
|
space: &Matrix4x4,
|
||||||
|
dir: Vector,
|
||||||
|
u: f32,
|
||||||
|
v: f32,
|
||||||
|
wavelength: f32,
|
||||||
|
time: f32)
|
||||||
|
-> SpectralSample {
|
||||||
|
// TODO: use transform space correctly
|
||||||
let radius = lerp_slice(&self.radii, time) as f64;
|
let radius = lerp_slice(&self.radii, time) as f64;
|
||||||
let col = lerp_slice(&self.colors, time);
|
let col = lerp_slice(&self.colors, time);
|
||||||
let surface_area = 4.0 * PI_64 * radius * radius;
|
let surface_area = 4.0 * PI_64 * radius * radius;
|
||||||
|
|
|
@ -7,7 +7,7 @@ use lerp::Lerp;
|
||||||
use float4::Float4;
|
use float4::Float4;
|
||||||
|
|
||||||
use super::{DotProduct, CrossProduct};
|
use super::{DotProduct, CrossProduct};
|
||||||
use super::Matrix4x4;
|
use super::{Matrix4x4, Vector};
|
||||||
|
|
||||||
/// A surface normal in 3d homogeneous space.
|
/// A surface normal in 3d homogeneous space.
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
@ -31,6 +31,10 @@ impl Normal {
|
||||||
pub fn normalized(&self) -> Normal {
|
pub fn normalized(&self) -> Normal {
|
||||||
*self / self.length()
|
*self / self.length()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn into_vector(self) -> Vector {
|
||||||
|
Vector::new(self.co[0], self.co[1], self.co[2])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ use lerp::Lerp;
|
||||||
use float4::Float4;
|
use float4::Float4;
|
||||||
|
|
||||||
use super::{DotProduct, CrossProduct};
|
use super::{DotProduct, CrossProduct};
|
||||||
use super::Matrix4x4;
|
use super::{Matrix4x4, Normal};
|
||||||
|
|
||||||
/// A direction vector in 3d homogeneous space.
|
/// A direction vector in 3d homogeneous space.
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
@ -31,6 +31,10 @@ impl Vector {
|
||||||
pub fn normalized(&self) -> Vector {
|
pub fn normalized(&self) -> Vector {
|
||||||
*self / self.length()
|
*self / self.length()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn into_normal(self) -> Normal {
|
||||||
|
Normal::new(self.co[0], self.co[1], self.co[2])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
100
src/renderer.rs
100
src/renderer.rs
|
@ -9,10 +9,12 @@ use scoped_threadpool::Pool;
|
||||||
use crossbeam::sync::MsQueue;
|
use crossbeam::sync::MsQueue;
|
||||||
|
|
||||||
use algorithm::partition_pair;
|
use algorithm::partition_pair;
|
||||||
|
use lerp::lerp_slice;
|
||||||
use ray::Ray;
|
use ray::Ray;
|
||||||
|
use assembly::Object;
|
||||||
use tracer::Tracer;
|
use tracer::Tracer;
|
||||||
use halton;
|
use halton;
|
||||||
use math::fast_logit;
|
use math::{Vector, Matrix4x4, dot, fast_logit};
|
||||||
use image::Image;
|
use image::Image;
|
||||||
use surface;
|
use surface;
|
||||||
use scene::Scene;
|
use scene::Scene;
|
||||||
|
@ -125,9 +127,10 @@ impl Renderer {
|
||||||
let isects = tracer.trace(&rays);
|
let isects = tracer.trace(&rays);
|
||||||
|
|
||||||
// Determine next rays to shoot based on result
|
// Determine next rays to shoot based on result
|
||||||
pi = partition_pair(&mut paths[..pi],
|
pi =
|
||||||
&mut rays[..pi],
|
partition_pair(&mut paths[..pi], &mut rays[..pi], |i, path, ray| {
|
||||||
|i, path, ray| path.next(&isects[i], &mut *ray));
|
path.next(&self.scene, &isects[i], &mut *ray)
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate color based on ray hits
|
// Calculate color based on ray hits
|
||||||
|
@ -185,6 +188,7 @@ pub struct LightPath {
|
||||||
round: u32,
|
round: u32,
|
||||||
time: f32,
|
time: f32,
|
||||||
wavelength: f32,
|
wavelength: f32,
|
||||||
|
light_attenuation: XYZ,
|
||||||
color: XYZ,
|
color: XYZ,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -201,9 +205,10 @@ impl LightPath {
|
||||||
pixel_co: pixel_co,
|
pixel_co: pixel_co,
|
||||||
lds_offset: lds_offset,
|
lds_offset: lds_offset,
|
||||||
dim_offset: 6,
|
dim_offset: 6,
|
||||||
round: 1,
|
round: 0,
|
||||||
time: time,
|
time: time,
|
||||||
wavelength: wavelength,
|
wavelength: wavelength,
|
||||||
|
light_attenuation: XYZ::new(1.0, 1.0, 1.0),
|
||||||
color: XYZ::new(0.0, 0.0, 0.0),
|
color: XYZ::new(0.0, 0.0, 0.0),
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -214,19 +219,82 @@ impl LightPath {
|
||||||
lens_uv.1))
|
lens_uv.1))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn next(&mut self, isect: &surface::SurfaceIntersection, ray: &mut Ray) -> bool {
|
fn next_lds_samp(&mut self) -> f32 {
|
||||||
if let &surface::SurfaceIntersection::Hit { t: _, pos: _, nor: _, local_space: _, uv } =
|
let s = halton::sample(self.dim_offset, self.lds_offset);
|
||||||
isect {
|
self.dim_offset += 1;
|
||||||
let rgbcol = (uv.0, uv.1, (1.0 - uv.0 - uv.1).max(0.0));
|
s
|
||||||
let xyz = XYZ::from_tuple(rec709e_to_xyz(rgbcol));
|
}
|
||||||
self.color += XYZ::from_spectral_sample(&xyz.to_spectral_sample(self.wavelength));
|
|
||||||
|
|
||||||
} else {
|
fn next(&mut self, scene: &Scene, isect: &surface::SurfaceIntersection, ray: &mut Ray) -> bool {
|
||||||
let xyz = XYZ::new(0.02, 0.02, 0.02);
|
match self.round {
|
||||||
self.color += XYZ::from_spectral_sample(&xyz.to_spectral_sample(self.wavelength));
|
// Result of camera rays, prepare light rays
|
||||||
|
0 => {
|
||||||
|
self.round += 1;
|
||||||
|
if let &surface::SurfaceIntersection::Hit { t: _,
|
||||||
|
pos: pos,
|
||||||
|
nor: nor,
|
||||||
|
local_space: _,
|
||||||
|
uv } = isect {
|
||||||
|
// Hit something! Do lighting!
|
||||||
|
if scene.root.light_accel.len() > 0 {
|
||||||
|
// Get the light and the mapping to its local space
|
||||||
|
let (light, space) = {
|
||||||
|
let l1 = &scene.root.objects[scene.root.light_accel[0].data_index];
|
||||||
|
let light = if let &Object::Light(ref light) = l1 {
|
||||||
|
light
|
||||||
|
} else {
|
||||||
|
panic!()
|
||||||
|
};
|
||||||
|
let space = if let Some((start, end)) = scene.root.light_accel[0]
|
||||||
|
.transform_indices {
|
||||||
|
lerp_slice(&scene.root.xforms[start..end], self.time)
|
||||||
|
} else {
|
||||||
|
Matrix4x4::new()
|
||||||
|
};
|
||||||
|
(light, space)
|
||||||
|
};
|
||||||
|
|
||||||
|
let lu = self.next_lds_samp();
|
||||||
|
let lv = self.next_lds_samp();
|
||||||
|
// TODO: store incident light info and pdf, and use them properly
|
||||||
|
let (_, shadow_vec, _) =
|
||||||
|
light.sample(&space, pos, lu, lv, self.wavelength, self.time);
|
||||||
|
|
||||||
|
let la = dot(nor.normalized().into_vector(), shadow_vec.normalized())
|
||||||
|
.max(0.0);
|
||||||
|
self.light_attenuation = XYZ::from_spectral_sample(&XYZ::new(la, la, la)
|
||||||
|
.to_spectral_sample(self.wavelength));
|
||||||
|
*ray = Ray::new(pos + shadow_vec.normalized() * 0.0001,
|
||||||
|
shadow_vec,
|
||||||
|
self.time,
|
||||||
|
true);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Didn't hit anything, so background color
|
||||||
|
let xyz = XYZ::new(0.02, 0.02, 0.02);
|
||||||
|
self.color +=
|
||||||
|
XYZ::from_spectral_sample(&xyz.to_spectral_sample(self.wavelength));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Result of light rays
|
||||||
|
1 => {
|
||||||
|
self.round += 1;
|
||||||
|
if let &surface::SurfaceIntersection::Miss = isect {
|
||||||
|
self.color += self.light_attenuation;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
_ => unimplemented!(),
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
|
|
||||||
use lerp::{lerp, lerp_slice, lerp_slice_with};
|
use lerp::{lerp, lerp_slice, lerp_slice_with};
|
||||||
use math::{Point, Normal, Matrix4x4};
|
use math::{Point, Normal, Matrix4x4, cross};
|
||||||
use ray::{Ray, AccelRay};
|
use ray::{Ray, AccelRay};
|
||||||
use triangle;
|
use triangle;
|
||||||
use bbox::BBox;
|
use bbox::BBox;
|
||||||
|
@ -85,7 +85,7 @@ impl Surface for TriangleMesh {
|
||||||
isects[r.id as usize] = SurfaceIntersection::Hit {
|
isects[r.id as usize] = SurfaceIntersection::Hit {
|
||||||
t: t,
|
t: t,
|
||||||
pos: wr.orig + (wr.dir * t),
|
pos: wr.orig + (wr.dir * t),
|
||||||
nor: Normal::new(0.0, 0.0, 0.0), // TODO
|
nor: cross(tri.0 - tri.1, tri.0 - tri.2).into_normal(),
|
||||||
local_space: mat_space,
|
local_space: mat_space,
|
||||||
uv: (tri_u, tri_v),
|
uv: (tri_u, tri_v),
|
||||||
};
|
};
|
||||||
|
|
|
@ -116,7 +116,9 @@ impl<'a> Tracer<'a> {
|
||||||
surface.intersect_rays(rays, wrays, &mut self.isects, self.xform_stack.top());
|
surface.intersect_rays(rays, wrays, &mut self.isects, self.xform_stack.top());
|
||||||
}
|
}
|
||||||
|
|
||||||
&Object::Light(_) => unimplemented!(),
|
&Object::Light(_) => {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user