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:
Nathan Vegdahl 2016-06-27 23:16:39 -07:00
parent 3b85b60a62
commit fd195576d1
9 changed files with 149 additions and 30 deletions

View File

@ -22,6 +22,9 @@ pub struct Assembly {
// Object accel
pub object_accel: BVH,
// Light accel
pub light_accel: Vec<Instance>,
}
impl Boundable for Assembly {
@ -126,20 +129,37 @@ impl AssemblyBuilder {
self.objects.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();
// Build object accel
let object_accel = BVH::from_objects(&mut self.instances[..],
1,
|inst| &bbs[bis[inst.id]..bis[inst.id + 1]]);
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 {
instances: self.instances,
xforms: self.xforms,
objects: self.objects,
assemblies: self.assemblies,
object_accel: object_accel,
light_accel: light_accel,
}
}

View File

@ -4,7 +4,7 @@ use std::fmt::Debug;
pub use self::sphere_light::SphereLight;
use math::{Vector, Point};
use math::{Vector, Point, Matrix4x4};
use color::SpectralSample;
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
/// shadow testing, and the pdf of the sample.
fn sample(&self,
space: &Matrix4x4,
arr: Point,
u: f32,
v: f32,
@ -37,6 +38,7 @@ pub trait LightSource: Boundable + Debug + Sync {
/// source). No guarantees are made about the correctness of the return
/// value if they are not valid.
fn sample_pdf(&self,
space: &Matrix4x4,
arr: Point,
sample_dir: Vector,
sample_u: f32,
@ -54,7 +56,14 @@ pub trait LightSource: Boundable + Debug + Sync {
/// - v: Random parameter V.
/// - wavelength: The hero wavelength of light 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;

View File

@ -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 boundable::Boundable;
use color::{XYZ, SpectralSample, Color};
@ -35,14 +35,16 @@ impl SphereLight {
impl LightSource for SphereLight {
fn sample(&self,
space: &Matrix4x4,
arr: Point,
u: f32,
v: f32,
wavelength: f32,
time: 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
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 col = lerp_slice(&self.colors, time);
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,
space: &Matrix4x4,
arr: Point,
sample_dir: Vector,
sample_u: f32,
@ -111,7 +114,8 @@ impl LightSource for SphereLight {
wavelength: f32,
time: 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 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 col = lerp_slice(&self.colors, time);
let surface_area = 4.0 * PI_64 * radius * radius;

View File

@ -7,7 +7,7 @@ use lerp::Lerp;
use float4::Float4;
use super::{DotProduct, CrossProduct};
use super::Matrix4x4;
use super::{Matrix4x4, Vector};
/// A surface normal in 3d homogeneous space.
#[derive(Debug, Copy, Clone)]
@ -31,6 +31,10 @@ impl Normal {
pub fn normalized(&self) -> Normal {
*self / self.length()
}
pub fn into_vector(self) -> Vector {
Vector::new(self.co[0], self.co[1], self.co[2])
}
}

View File

@ -7,7 +7,7 @@ use lerp::Lerp;
use float4::Float4;
use super::{DotProduct, CrossProduct};
use super::Matrix4x4;
use super::{Matrix4x4, Normal};
/// A direction vector in 3d homogeneous space.
#[derive(Debug, Copy, Clone)]
@ -31,6 +31,10 @@ impl Vector {
pub fn normalized(&self) -> Vector {
*self / self.length()
}
pub fn into_normal(self) -> Normal {
Normal::new(self.co[0], self.co[1], self.co[2])
}
}

View File

@ -9,10 +9,12 @@ use scoped_threadpool::Pool;
use crossbeam::sync::MsQueue;
use algorithm::partition_pair;
use lerp::lerp_slice;
use ray::Ray;
use assembly::Object;
use tracer::Tracer;
use halton;
use math::fast_logit;
use math::{Vector, Matrix4x4, dot, fast_logit};
use image::Image;
use surface;
use scene::Scene;
@ -125,9 +127,10 @@ impl Renderer {
let isects = tracer.trace(&rays);
// Determine next rays to shoot based on result
pi = partition_pair(&mut paths[..pi],
&mut rays[..pi],
|i, path, ray| path.next(&isects[i], &mut *ray));
pi =
partition_pair(&mut paths[..pi], &mut rays[..pi], |i, path, ray| {
path.next(&self.scene, &isects[i], &mut *ray)
});
}
// Calculate color based on ray hits
@ -185,6 +188,7 @@ pub struct LightPath {
round: u32,
time: f32,
wavelength: f32,
light_attenuation: XYZ,
color: XYZ,
}
@ -201,9 +205,10 @@ impl LightPath {
pixel_co: pixel_co,
lds_offset: lds_offset,
dim_offset: 6,
round: 1,
round: 0,
time: time,
wavelength: wavelength,
light_attenuation: XYZ::new(1.0, 1.0, 1.0),
color: XYZ::new(0.0, 0.0, 0.0),
},
@ -214,20 +219,83 @@ impl LightPath {
lens_uv.1))
}
fn next(&mut self, isect: &surface::SurfaceIntersection, ray: &mut Ray) -> bool {
if let &surface::SurfaceIntersection::Hit { t: _, pos: _, nor: _, local_space: _, uv } =
isect {
let rgbcol = (uv.0, uv.1, (1.0 - uv.0 - uv.1).max(0.0));
let xyz = XYZ::from_tuple(rec709e_to_xyz(rgbcol));
self.color += XYZ::from_spectral_sample(&xyz.to_spectral_sample(self.wavelength));
} else {
let xyz = XYZ::new(0.02, 0.02, 0.02);
self.color += XYZ::from_spectral_sample(&xyz.to_spectral_sample(self.wavelength));
fn next_lds_samp(&mut self) -> f32 {
let s = halton::sample(self.dim_offset, self.lds_offset);
self.dim_offset += 1;
s
}
fn next(&mut self, scene: &Scene, isect: &surface::SurfaceIntersection, ray: &mut Ray) -> bool {
match self.round {
// 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!(),
}
}
}

View File

@ -1,7 +1,7 @@
#![allow(dead_code)]
use lerp::{lerp, lerp_slice, lerp_slice_with};
use math::{Point, Normal, Matrix4x4};
use math::{Point, Normal, Matrix4x4, cross};
use ray::{Ray, AccelRay};
use triangle;
use bbox::BBox;
@ -85,7 +85,7 @@ impl Surface for TriangleMesh {
isects[r.id as usize] = SurfaceIntersection::Hit {
t: 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,
uv: (tri_u, tri_v),
};

View File

@ -116,7 +116,9 @@ impl<'a> Tracer<'a> {
surface.intersect_rays(rays, wrays, &mut self.isects, self.xform_stack.top());
}
&Object::Light(_) => unimplemented!(),
&Object::Light(_) => {
// TODO
}
}
}
}

View File

@ -1,3 +1,3 @@
- Move to spectral rendering.
//- Move to spectral rendering.
- Implement basic direct lighting w/ spherical lights.
- Unit tests for scene parsing.