Skip to content

Commit 6d431e5

Browse files
committed
Added integrators trait
1 parent 8512e74 commit 6d431e5

File tree

8 files changed

+244
-1185
lines changed

8 files changed

+244
-1185
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
use crate::integrators::*;
2+
use rt_core::*;
3+
4+
pub struct MisIntegrator;
5+
6+
impl Integrator for MisIntegrator {
7+
fn get_colour<A: AccelerationStructure<Object = P, Material = M>, P: Primitive, M: Scatter>(
8+
ray: &mut Ray,
9+
bvh: &A,
10+
) -> (Vec3, u64) {
11+
let (mut throughput, mut output) = (Vec3::one(), Vec3::zero());
12+
let mut ray_count = 0;
13+
14+
let mut wo;
15+
let mut hit;
16+
let mut mat;
17+
let (surface_intersection, _index) = bvh.check_hit(ray);
18+
19+
(hit, mat) = (surface_intersection.hit, surface_intersection.material);
20+
21+
wo = ray.direction;
22+
23+
let emission = mat.get_emission(&hit, wo);
24+
25+
let exit = mat.scatter_ray(&mut ray.clone(), &hit);
26+
27+
output += emission;
28+
29+
if exit {
30+
return (output, ray_count);
31+
}
32+
33+
let mut depth = 1;
34+
35+
while depth < MAX_DEPTH {
36+
// light sampling
37+
let sample_lights = sample_lights(bvh, &hit);
38+
ray_count += 1;
39+
if let Some((l_wi, le, l_pdf)) = sample_lights {
40+
let m_pdf = mat.scattering_pdf(&hit, wo, l_wi);
41+
let mis_weight = power_heuristic(l_pdf, m_pdf);
42+
output += throughput * mat.eval(&hit, wo, l_wi) * mis_weight * le / l_pdf;
43+
}
44+
45+
// material sampling and bounce
46+
let exit = mat.scatter_ray(ray, &hit);
47+
if exit {
48+
break;
49+
}
50+
let m_wi = ray.direction;
51+
52+
let (intersection, index) = bvh.check_hit(ray);
53+
54+
let m_pdf = mat.scattering_pdf(&hit, wo, m_wi);
55+
let le = intersection.material.get_emission(&hit, m_wi);
56+
throughput *= mat.eval_over_scattering_pdf(&hit, wo, m_wi);
57+
if le != Vec3::zero() {
58+
if (bvh.get_samplable().contains(&index) && !mat.is_delta())
59+
|| (index == usize::MAX && bvh.sky().can_sample())
60+
{
61+
let l_pdf = bvh.get_pdf_from_index(&hit, &intersection.hit, m_wi, index);
62+
let mis_weight = power_heuristic(m_pdf, l_pdf);
63+
output += throughput * le * mis_weight;
64+
} else {
65+
output += throughput * le;
66+
}
67+
}
68+
69+
if intersection.material.is_light() {
70+
break;
71+
}
72+
73+
if depth > RUSSIAN_ROULETTE_THRESHOLD {
74+
let p = throughput.component_max();
75+
let mut rng = SmallRng::from_rng(thread_rng()).unwrap();
76+
if rng.gen::<Float>() > p {
77+
break;
78+
}
79+
throughput /= p;
80+
}
81+
82+
wo = m_wi;
83+
hit = intersection.hit;
84+
mat = intersection.material;
85+
86+
depth += 1;
87+
}
88+
if output.contains_nan() || !output.is_finite() {
89+
return (Vec3::zero(), ray_count);
90+
}
91+
(output, ray_count)
92+
}
93+
}
94+
95+
fn sample_lights<A: AccelerationStructure<Object = P, Material = M>, P: Primitive, M: Scatter>(
96+
bvh: &A,
97+
hit: &Hit,
98+
) -> Option<(Vec3, Vec3, Float)> {
99+
//l_wi, le, l_pdf
100+
let sky = bvh.sky();
101+
let samplable_len = bvh.get_samplable().len();
102+
let sky_can_sample = sky.can_sample();
103+
104+
let sample_sky = |pdf_multiplier: Float| {
105+
let l_wi = sky.sample();
106+
let ray = Ray::new(hit.point + 0.0001 * hit.normal, l_wi, 0.0);
107+
108+
let (sa, index) = bvh.check_hit(&ray);
109+
if index == usize::MAX {
110+
let le = sa.material.get_emission(hit, l_wi);
111+
let l_pdf = sky.pdf(l_wi);
112+
return Some((l_wi, le, l_pdf * pdf_multiplier));
113+
}
114+
None
115+
};
116+
117+
let sample_light = |pdf_multiplier: Float, index: usize| {
118+
let index = bvh.get_samplable()[index];
119+
let light = bvh.get_object(index).unwrap();
120+
121+
let l_wi = light.sample_visible_from_point(hit.point);
122+
123+
if let Some(si) =
124+
bvh.check_hit_index(&Ray::new(hit.point + 0.0001 * hit.normal, l_wi, 0.0), index)
125+
{
126+
let l_pdf = light.scattering_pdf(hit.point, l_wi, &si.hit);
127+
if l_pdf > 0.0 {
128+
let le = si.material.get_emission(&si.hit, l_wi);
129+
return Some((l_wi, le, l_pdf * pdf_multiplier));
130+
}
131+
}
132+
None
133+
};
134+
135+
match (samplable_len, sky_can_sample) {
136+
(0, false) => None,
137+
(0, true) => sample_sky(1.0),
138+
(_, false) => {
139+
let multipler = 1.0 / samplable_len as Float;
140+
let light_index = SmallRng::from_rng(thread_rng())
141+
.unwrap()
142+
.gen_range(0..samplable_len);
143+
sample_light(multipler, light_index)
144+
}
145+
(_, true) => {
146+
let multipler = 1.0 / (samplable_len + 1) as Float;
147+
let light_index = SmallRng::from_rng(thread_rng())
148+
.unwrap()
149+
.gen_range(0..=samplable_len);
150+
if light_index == samplable_len {
151+
sample_sky(multipler)
152+
} else {
153+
sample_light(multipler, light_index)
154+
}
155+
}
156+
}
157+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
use crate::rt_core::*;
2+
use rand::rngs::SmallRng;
3+
use rand::thread_rng;
4+
use rand::Rng;
5+
use rand::SeedableRng;
6+
7+
const MAX_DEPTH: u32 = 50;
8+
const RUSSIAN_ROULETTE_THRESHOLD: u32 = 3;
9+
10+
pub mod mis;
11+
pub use mis::*;
12+
13+
pub trait Integrator {
14+
fn get_colour<A: AccelerationStructure<Object = P, Material = M>, P: Primitive, M: Scatter>(
15+
ray: &mut Ray,
16+
bvh: &A,
17+
) -> (Vec3, u64);
18+
}
19+
20+
pub struct NaiveIntegrator;
21+
22+
impl Integrator for NaiveIntegrator {
23+
fn get_colour<A: AccelerationStructure<Object = P, Material = M>, P: Primitive, M: Scatter>(
24+
ray: &mut Ray,
25+
bvh: &A,
26+
) -> (Vec3, u64) {
27+
let (mut throughput, mut output) = (Vec3::one(), Vec3::zero());
28+
let mut depth = 0;
29+
let mut ray_count = 0;
30+
31+
while depth < MAX_DEPTH {
32+
let hit_info = bvh.check_hit(ray);
33+
34+
ray_count += 1;
35+
36+
let (surface_intersection, _index) = hit_info;
37+
let (hit, mat) = (&surface_intersection.hit, &surface_intersection.material);
38+
39+
let wo = ray.direction;
40+
41+
let emission = mat.get_emission(hit, wo);
42+
43+
let exit = mat.scatter_ray(ray, hit);
44+
45+
if depth == 0 {
46+
output += emission;
47+
if exit {
48+
break;
49+
}
50+
}
51+
52+
if exit {
53+
output += throughput * emission;
54+
break;
55+
}
56+
57+
if !mat.is_delta() {
58+
throughput *= mat.eval_over_scattering_pdf(hit, wo, ray.direction);
59+
} else {
60+
throughput *= mat.eval(hit, wo, ray.direction);
61+
}
62+
63+
if depth > RUSSIAN_ROULETTE_THRESHOLD {
64+
let p = throughput.component_max();
65+
let mut rng = SmallRng::from_rng(thread_rng()).unwrap();
66+
if rng.gen::<Float>() > p {
67+
break;
68+
}
69+
throughput /= p;
70+
}
71+
72+
depth += 1;
73+
}
74+
if output.contains_nan() || !output.is_finite() {
75+
return (Vec3::zero(), ray_count);
76+
}
77+
(output, ray_count)
78+
}
79+
}

crates/implementations/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
mod acceleration;
22
mod camera;
3+
mod integrators;
34
mod materials;
45
mod primitives;
56
mod samplers;

crates/implementations/src/samplers/random_sampler.rs

+6-4
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use crate::integrators::*;
12
use crate::*;
23
use rand::Rng;
34
use rayon::prelude::*;
@@ -59,11 +60,12 @@ impl Sampler for RandomSampler {
5960

6061
let mut ray = camera.get_ray(u, v); // remember to add le DOF
6162
let result = match render_options.render_method {
62-
RenderMethod::Naive => {
63-
Ray::get_colour_naive(&mut ray, acceleration_structure)
64-
}
63+
RenderMethod::Naive => NaiveIntegrator::get_colour(
64+
&mut ray,
65+
acceleration_structure,
66+
),
6567
RenderMethod::MIS => {
66-
Ray::get_colour(&mut ray, acceleration_structure)
68+
MisIntegrator::get_colour(&mut ray, acceleration_structure)
6769
}
6870
};
6971

0 commit comments

Comments
 (0)