WIP: pathtrace soup

This commit is contained in:
Bruno BELANYI 2020-04-01 23:13:02 +02:00
parent 8afc5ba382
commit c5867bca84
8 changed files with 146 additions and 97 deletions

View File

@ -2,6 +2,7 @@
use super::core::LinearColor;
use super::{Point, Vector};
use beevee::ray::Ray;
use nalgebra::Unit;
/// Represent a light in the scene being rendered.
@ -16,6 +17,11 @@ pub trait SpatialLight: Light {
fn to_source(&self, origin: &Point) -> (Unit<Vector>, f32);
}
/// Represent a light from which rays can be sampled
pub trait SampleLight: SpatialLight {
fn sample_ray(&self) -> Ray;
}
mod ambient_light;
pub use ambient_light::*;

View File

@ -1,4 +1,4 @@
use super::{Light, SpatialLight};
use super::{Light, SampleLight, SpatialLight};
use crate::core::LinearColor;
use crate::{Point, Vector};
use beevee::ray::Ray;
@ -31,7 +31,23 @@ impl PointLight {
pub fn new(position: Point, color: LinearColor) -> Self {
PointLight { position, color }
}
}
impl Light for PointLight {
fn illumination(&self, point: &Point) -> LinearColor {
let dist = (self.position - point).norm();
self.color.clone() / dist
}
}
impl SpatialLight for PointLight {
fn to_source(&self, point: &Point) -> (Unit<Vector>, f32) {
let delt = self.position - point;
let dist = delt.norm();
(Unit::new_normalize(delt), dist)
}
}
impl SampleLight for PointLight {
/// Uniformly sample a ray from the point-light in a random direction.
///
/// # Examles
@ -47,7 +63,7 @@ impl PointLight {
/// );
/// let sampled = dir_light.sample_ray();
/// ```
pub fn sample_ray(&self) -> Ray {
fn sample_ray(&self) -> Ray {
let mut rng = rand::thread_rng();
// Sample sphere uniformly
// See <https://mathworld.wolfram.com/SpherePointPicking.html>
@ -63,21 +79,6 @@ impl PointLight {
}
}
impl Light for PointLight {
fn illumination(&self, point: &Point) -> LinearColor {
let dist = (self.position - point).norm();
self.color.clone() / dist
}
}
impl SpatialLight for PointLight {
fn to_source(&self, point: &Point) -> (Unit<Vector>, f32) {
let delt = self.position - point;
let dist = delt.norm();
(Unit::new_normalize(delt), dist)
}
}
#[cfg(test)]
mod test {
use super::*;

View File

@ -1,4 +1,4 @@
use super::{Light, SpatialLight};
use super::{Light, SampleLight, SpatialLight};
use crate::core::LinearColor;
use crate::{Point, Vector};
use beevee::ray::Ray;
@ -47,7 +47,28 @@ impl SpotLight {
color,
)
}
}
impl Light for SpotLight {
fn illumination(&self, point: &Point) -> LinearColor {
let delt = point - self.position;
let cos = self.direction.dot(&delt.normalize());
if cos >= self.cosine_value {
self.color.clone() / delt.norm_squared()
} else {
LinearColor::black()
}
}
}
impl SpatialLight for SpotLight {
fn to_source(&self, point: &Point) -> (Unit<Vector>, f32) {
let delt = self.position - point;
let dist = delt.norm();
(Unit::new_normalize(delt), dist)
}
}
impl SampleLight for SpotLight {
/// Uniformly sample a ray from the spot-light in a random direction.
///
/// # Examles
@ -65,7 +86,7 @@ impl SpotLight {
/// );
/// let sampled = spot_light.sample_ray();
/// ```
pub fn sample_ray(&self) -> Ray {
fn sample_ray(&self) -> Ray {
let mut rng = rand::thread_rng();
// Sample cap at Z-pole uniformly
// See <https://math.stackexchange.com/questions/56784>
@ -94,26 +115,6 @@ impl SpotLight {
}
}
impl Light for SpotLight {
fn illumination(&self, point: &Point) -> LinearColor {
let delt = point - self.position;
let cos = self.direction.dot(&delt.normalize());
if cos >= self.cosine_value {
self.color.clone() / delt.norm_squared()
} else {
LinearColor::black()
}
}
}
impl SpatialLight for SpotLight {
fn to_source(&self, point: &Point) -> (Unit<Vector>, f32) {
let delt = self.position - point;
let dist = delt.norm();
(Unit::new_normalize(delt), dist)
}
}
#[derive(Debug, Deserialize)]
struct SerializedSpotLight {
position: Point,

View File

@ -0,0 +1,37 @@
use crate::core::{LightProperties, LinearColor};
use crate::light::SampleLight;
use crate::Point;
pub struct LightPathPoint {
pub point: Point,
pub luminance: LinearColor,
pub properties: LightProperties,
}
impl LightPathPoint {
pub fn new(point: Point, luminance: LinearColor, properties: LightProperties) -> Self {
LightPathPoint {
point,
luminance,
properties,
}
}
}
pub struct LightPath<'a> {
pub origin: &'a dyn SampleLight,
pub points: Vec<LightPathPoint>,
}
impl<'a> LightPath<'a> {
pub fn new(origin: &'a dyn SampleLight) -> Self {
LightPath {
origin,
points: Vec::new(),
}
}
pub fn push_point(&mut self, new_point: LightPathPoint) {
self.points.push(new_point)
}
}

View File

@ -1,4 +1,4 @@
mod path;
mod light_path;
mod pathtracer;
pub use self::pathtracer::*;

View File

@ -1,44 +0,0 @@
use crate::core::LightProperties;
use crate::{Point, Vector};
use nalgebra::Unit;
pub struct PathPoint {
pub point: Point,
pub incident: Unit<Vector>,
pub normal: Unit<Vector>,
pub properties: LightProperties,
}
impl PathPoint {
pub fn new(
point: Point,
incident: Unit<Vector>,
normal: Unit<Vector>,
properties: LightProperties,
) -> Self {
PathPoint {
point,
incident,
normal,
properties,
}
}
}
pub struct Path {
pub origin: Point,
pub points: Vec<PathPoint>,
}
impl Path {
pub fn new(origin: Point) -> Self {
Path {
origin,
points: Vec::new(),
}
}
pub fn push_point(&mut self, new_point: PathPoint) {
self.points.push(new_point)
}
}

View File

@ -1,9 +1,17 @@
use super::super::utils::*;
use super::super::Renderer;
use super::path::*;
use super::light_path::{LightPath, LightPathPoint};
use crate::core::{LightProperties, LinearColor};
use crate::light::SampleLight;
use crate::material::Material;
use crate::scene::object::Object;
use crate::scene::Scene;
use crate::shape::Shape;
use crate::{Point, Vector};
use beevee::ray::Ray;
use image::RgbImage;
use nalgebra::Unit;
use rand::Rng;
/// Render the [`Scene`] using Bidirectional-Pathtracing
///
@ -29,17 +37,57 @@ impl Pathtracer {
todo!()
}
fn construct_path(&self, point: Point, direction: Unit<Vector>) -> Path {
let mut res = Path::new(point);
for _ in 0..self.scene.reflection_limit {
// FIXME:
// * cast_ray: if no intersection, return the empty path
// * look-up information at intersection
// * append to path
// * start again with new origin
fn cast_ray(&self, ray: Ray) -> Option<(f32, &Object)> {
self.scene.bvh.walk(&ray, &self.scene.objects)
}
fn construct_light_path(&self) -> LightPath {
let mut rng = rand::thread_rng();
let num_lights = self.scene.lights.points.len() + self.scene.lights.spots.len();
let index = rng.gen_range(0, num_lights);
let sample_light: &dyn SampleLight = if index < self.scene.lights.points.len() {
&self.scene.lights.points[index]
} else {
&self.scene.lights.spots[index - self.scene.lights.points.len()]
};
let mut ray = sample_light.sample_ray();
let mut res = LightPath::new(sample_light);
if let Some((dist, obj)) = self.cast_ray(ray) {
let hit_pos = ray.origin + ray.direction.as_ref() * dist;
let texel = obj.shape.project_texel(&hit_pos);
let new_point = LightPathPoint::new(
hit_pos,
sample_light.illumination(&hit_pos),
obj.material.properties(texel),
);
res.push_point(new_point);
ray = todo!(); // Sample new direction
} else {
return res;
};
for _ in 1..self.scene.reflection_limit {
if let Some((dist, obj)) = self.cast_ray(ray) {
let new_point = todo!();
res.push_point(new_point);
} else {
break;
}
}
res
}
fn illuminate(
&self,
point: Point,
properties: LightProperties,
path: LightPath,
) -> LinearColor {
path.points.iter().map(|p| p.luminance.clone()).sum()
}
}
impl Renderer for Pathtracer {

View File

@ -8,13 +8,13 @@ use std::iter::Iterator;
/// A struct centralizing the light computation logic.
pub struct LightAggregate {
#[serde(default)]
ambients: Vec<AmbientLight>,
pub(crate) ambients: Vec<AmbientLight>,
#[serde(default)]
directionals: Vec<DirectionalLight>,
pub(crate) directionals: Vec<DirectionalLight>,
#[serde(default)]
points: Vec<PointLight>,
pub(crate) points: Vec<PointLight>,
#[serde(default)]
spots: Vec<SpotLight>,
pub(crate) spots: Vec<SpotLight>,
}
impl LightAggregate {