Skip to content

Commit

Permalink
Correct light sampling
Browse files Browse the repository at this point in the history
  • Loading branch information
markusmoenig committed Mar 5, 2023
1 parent afc2fde commit 20a6286
Show file tree
Hide file tree
Showing 6 changed files with 78 additions and 9 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Binary file modified images/spheres.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
18 changes: 11 additions & 7 deletions renderer/src/analytical.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ impl Scene for AnalyticalScene {

fn new() -> Self {

let em = 5.0;
let em = 3.0;
let lights = vec![AnalyticalLight::spherical(F3::new(3.0, 2.0, 2.0), 1.0, F3::new(em, em, em))];

Self {
Expand All @@ -28,12 +28,12 @@ impl Scene for AnalyticalScene {
fn background(&self, ray: &Ray) -> F3 {
// Taken from https://raytracing.github.io/books/RayTracingInOneWeekend.html, a source of great knowledge
let t = 0.5 * (ray.direction.y + 1.0);
self.to_linear((1.0 - t) * F3::new(1.0, 1.0, 1.0) + t * F3::new(0.5, 0.7, 1.0)) * F3::new_x(0.8)
self.to_linear((1.0 - t) * F3::new(1.0, 1.0, 1.0) + t * F3::new(0.5, 0.7, 1.0)) * F3::new_x(0.5)
}


/// The closest hit, includes light sources.
fn closest_hit(&self, ray: &Ray, state: &mut State, _light: &mut LightSampleRec) -> bool {
fn closest_hit(&self, ray: &Ray, state: &mut State, light_sample: &mut LightSampleRec) -> bool {

let mut dist = F::MAX;
let mut hit = false;
Expand All @@ -54,7 +54,7 @@ impl Scene for AnalyticalScene {
//state.material.roughness = 1.0;

state.material.rgb = F3::new_x(1.0);//PTF3::new(0.815, 0.418501512, 0.00180012);
state.material.roughness = 0.02;
state.material.roughness = 0.05;
state.material.metallic = 1.0;
//state.material.spec_trans = 1.0;

Expand All @@ -80,9 +80,9 @@ impl Scene for AnalyticalScene {
state.normal = normal;

state.material.rgb = F3::new(1.0,0.186, 0.0);
//state.material.clearcoat = 1.0;
//state.material.clearcoat_gloss = 1.0;
state.material.roughness = 0.3;
state.material.clearcoat = 1.0;
state.material.clearcoat_gloss = 1.0;
state.material.roughness = 0.1;

// state.material.rgb = F3::new(0.9,0.9, 0.9);
// state.material.roughness = 0.2;
Expand Down Expand Up @@ -119,6 +119,10 @@ impl Scene for AnalyticalScene {
}
}

if self.sample_lights(ray, state, light_sample, &self.lights) {
hit = true;
}

hit
}

Expand Down
2 changes: 1 addition & 1 deletion rust-pathtracer/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "rust-pathtracer"
version = "0.2.0"
version = "0.2.1"
edition = "2021"
description = "A principled BSDF pathtracer with an abstracted backend. Perfect for rendering procedural content."
license = "MIT"
Expand Down
52 changes: 52 additions & 0 deletions rust-pathtracer/src/scene.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,56 @@ pub trait Scene : Sync + Send {
fn to_linear(&self, c: F3) -> F3 {
F3::new(c.x.powf(2.2), c.y.powf(2.2), c.z.powf(2.2))
}

fn sample_lights(&self, ray: &Ray, state: &mut State, light_sample: &mut LightSampleRec, lights: &Vec<AnalyticalLight>) -> bool {

// Based on https://www.scratchapixel.com/lessons/3d-basic-rendering/minimal-ray-tracer-rendering-simple-shapes/ray-sphere-intersection
fn sphere(ray: &Ray, center: F3, radius: F) -> Option<F> {
let l = center - ray.origin;
let tca = l.dot(&ray.direction);
let d2 = l.dot(&l) - tca * tca;
let radius2 = radius * radius;
if d2 > radius2 {
return None;
}
let thc = (radius2 - d2).sqrt();
let mut t0 = tca - thc;
let mut t1 = tca + thc;

if t0 > t1 {
std::mem::swap(&mut t0, &mut t1);
}

if t0 < 0.0 {
t0 = t1;
if t0 < 0.0 {
return None;
}
}

Some(t0)
}

let mut hit = false;
let mut dist = state.hit_dist;

for light in lights {
if light.light.light_type == LightType::Spherical {
if let Some(d) = sphere(ray, light.light.position, light.light.radius) {
if d < dist {
dist = d;
let hit_point = ray.at(&d);
let cos_theta = dot(&-ray.direction, &normalize(&(hit_point - light.light.position)));
light_sample.pdf = (dist * dist) / (light.light.area * cos_theta * 0.5);
light_sample.emission = light.light.emission;
state.is_emitter = true;
state.hit_dist = d;
hit = true;
}
}
}
}

hit
}
}
13 changes: 13 additions & 0 deletions rust-pathtracer/src/tracer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,19 @@ impl Tracer {

radiance += state.material.emission * throughput;

// Gather radiance from light and use scatterSample.pdf from previous bounce for MIS
if state.is_emitter {
let mut mis_weight = 1.0;

if state.depth > 0 {
mis_weight = self.power_heuristic(&scatter_sample.pdf, &light_sample.pdf);
}

radiance += mis_weight * light_sample.emission * throughput;

break;
}

radiance += self.direct_light(&ray, &state, true, &mut rng) * throughput;

// Sample BSDF for color and outgoing direction
Expand Down

0 comments on commit 20a6286

Please sign in to comment.