Extract renderer into own packege
This commit is contained in:
parent
b3fdef8837
commit
c517a836ee
24 changed files with 332 additions and 157 deletions
|
|
@ -1,5 +1,4 @@
|
|||
use crate::prelude::*;
|
||||
use rand::Rng;
|
||||
|
||||
pub trait Camera<R: Rng> {
|
||||
fn forward(&self, x: u32, y: u32, rng: &mut R) -> Ray;
|
||||
|
|
|
|||
|
|
@ -16,5 +16,6 @@ pub mod prelude {
|
|||
pub use crate::material::Material;
|
||||
pub use crate::math::*;
|
||||
pub use crate::ray::Ray;
|
||||
pub use rand::Rng;
|
||||
pub use std::f32::consts as FloatConsts;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
use crate::prelude::*;
|
||||
use rand::Rng;
|
||||
|
||||
pub trait Light<R: Rng>: Sync {
|
||||
fn emit(&self, w_in: Dir3, rng: &mut R) -> Color;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,31 @@
|
|||
use crate::prelude::*;
|
||||
use rand::Rng;
|
||||
|
||||
/// All calculations for the material are done a tangent space of the intersection.
|
||||
pub trait Material<R: Rng>: Sync {
|
||||
fn eval(&self, w_in: Dir3, w_out: Dir3, rng: &mut R) -> Color;
|
||||
|
||||
fn sample(&self, w_in: Dir3, rng: &mut R) -> SampleResult {
|
||||
let w_out = Dir3::sample_cosine_hemisphere(rng);
|
||||
|
||||
SampleResult::new(w_out, self.eval(w_in, w_out, rng))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SampleResult {
|
||||
w_out: Dir3,
|
||||
color: Color,
|
||||
}
|
||||
|
||||
impl SampleResult {
|
||||
pub fn new(w_out: Dir3, color: Color) -> Self {
|
||||
Self { w_out, color }
|
||||
}
|
||||
|
||||
pub fn w_out(&self) -> Dir3 {
|
||||
self.w_out
|
||||
}
|
||||
|
||||
pub fn color(&self) -> Color {
|
||||
self.color
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
use crate::prelude::*;
|
||||
use rand::Rng;
|
||||
use std::ops::{Add, Div, Mul, Neg, Sub};
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
|
|
@ -62,15 +61,39 @@ impl Dir3 {
|
|||
)
|
||||
}
|
||||
|
||||
pub fn generate_uniform_hemisphere<R: Rng>(rng: &mut R) -> Self {
|
||||
pub fn sample_uniform_hemisphere<R: Rng>(rng: &mut R) -> Self {
|
||||
let theta = Float::acos(1.0 - rng.gen::<Float>());
|
||||
let phi = 2.0 * FloatConsts::PI * rng.gen::<Float>();
|
||||
let r = Float::sin(theta);
|
||||
Dir3::new(Float::sin(phi) * r, Float::cos(theta), Float::cos(phi) * r)
|
||||
}
|
||||
|
||||
pub fn generate_cosine_hemisphere<R: Rng>(rng: &mut R) -> Self {
|
||||
todo!()
|
||||
pub fn sample_cosine_hemisphere<R: Rng>(rng: &mut R) -> Self {
|
||||
let mut d = Self::sample_disc(rng);
|
||||
d.y = Float::sqrt(Float::max(0.0, 1.0 - d.x * d.x - d.z * d.z));
|
||||
d
|
||||
}
|
||||
|
||||
/// https://www.pbr-book.org/3ed-2018/Monte_Carlo_Integration/2D_Sampling_with_Multidimensional_Transformations#ConcentricSampleDisk
|
||||
pub fn sample_disc<R: Rng>(rng: &mut R) -> Self {
|
||||
let x = 2.0 * rng.gen::<Float>() - 1.0;
|
||||
let y = 2.0 * rng.gen::<Float>() - 1.0;
|
||||
|
||||
if x == 0.0 && y == 0.0 {
|
||||
return Self::zero();
|
||||
}
|
||||
|
||||
let r;
|
||||
let theta;
|
||||
if x.abs() > y.abs() {
|
||||
r = x;
|
||||
theta = FloatConsts::FRAC_PI_4 * (y / x);
|
||||
} else {
|
||||
r = y;
|
||||
theta = FloatConsts::FRAC_PI_2 - FloatConsts::FRAC_PI_4 * (x / y);
|
||||
}
|
||||
|
||||
r * Dir3::new(Float::cos(theta), 0.0, Float::sin(theta))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,138 +1,7 @@
|
|||
use std::marker::PhantomData;
|
||||
|
||||
use crate::{camera::Camera, prelude::*, scene::Scene};
|
||||
use rand::Rng;
|
||||
use crate::prelude::*;
|
||||
|
||||
pub trait ClassicalRenderer<R: Rng> {
|
||||
fn render_pixel(&self, x: u32, y: u32, rng: &mut R) -> Color;
|
||||
fn width(&self) -> u32;
|
||||
fn height(&self) -> u32;
|
||||
}
|
||||
|
||||
pub struct PathTracer<S, C, R>
|
||||
where
|
||||
S: Scene<R>,
|
||||
C: Camera<R>,
|
||||
R: Rng,
|
||||
{
|
||||
scene: S,
|
||||
camera: C,
|
||||
rng: PhantomData<R>,
|
||||
}
|
||||
|
||||
impl<S, C, R> PathTracer<S, C, R>
|
||||
where
|
||||
S: Scene<R>,
|
||||
C: Camera<R>,
|
||||
R: Rng,
|
||||
{
|
||||
pub fn new(scene: S, camera: C) -> Self {
|
||||
Self {
|
||||
scene,
|
||||
camera,
|
||||
rng: PhantomData {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<S, C, R> ClassicalRenderer<R> for PathTracer<S, C, R>
|
||||
where
|
||||
S: Scene<R>,
|
||||
C: Camera<R>,
|
||||
R: Rng,
|
||||
{
|
||||
fn render_pixel(&self, x: u32, y: u32, rng: &mut R) -> Color {
|
||||
let mut sum = Color::black();
|
||||
let mut alpha = Color::white();
|
||||
|
||||
let mut r = self.camera.forward(x, y, rng);
|
||||
|
||||
let mut count = 0;
|
||||
|
||||
while let Some(i) = self.scene.intersect(r, 0.001, Float::INFINITY) {
|
||||
let frame = i.tangent_frame();
|
||||
let w_in = frame.to_frame(-r.dir());
|
||||
let w_out = Dir3::generate_uniform_hemisphere(rng);
|
||||
|
||||
if let Some(light) = i.light() {
|
||||
sum += alpha * light.emit(w_in, rng) * w_out.y();
|
||||
}
|
||||
|
||||
if let Some(material) = i.material() {
|
||||
alpha *= material.eval(w_in, w_out, rng) * w_out.y();
|
||||
} else {
|
||||
return sum;
|
||||
}
|
||||
|
||||
r = Ray::new(r.at(i.t()), frame.to_world(w_out), r.time());
|
||||
count += 1;
|
||||
if count > 4 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
sum
|
||||
}
|
||||
|
||||
fn width(&self) -> u32 {
|
||||
self.camera.width()
|
||||
}
|
||||
|
||||
fn height(&self) -> u32 {
|
||||
self.camera.height()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DepthRenderer<S, C, R>
|
||||
where
|
||||
S: Scene<R>,
|
||||
C: Camera<R>,
|
||||
R: Rng,
|
||||
{
|
||||
scene: S,
|
||||
camera: C,
|
||||
rng: PhantomData<R>,
|
||||
}
|
||||
|
||||
impl<S, C, R> DepthRenderer<S, C, R>
|
||||
where
|
||||
S: Scene<R>,
|
||||
C: Camera<R>,
|
||||
R: Rng,
|
||||
{
|
||||
pub fn new(scene: S, camera: C) -> Self {
|
||||
Self {
|
||||
scene,
|
||||
camera,
|
||||
rng: PhantomData {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<S, C, R> ClassicalRenderer<R> for DepthRenderer<S, C, R>
|
||||
where
|
||||
S: Scene<R>,
|
||||
C: Camera<R>,
|
||||
R: Rng,
|
||||
{
|
||||
fn render_pixel(&self, x: u32, y: u32, rng: &mut R) -> Color {
|
||||
let r = self.camera.forward(x, y, rng);
|
||||
|
||||
if let Some(i) = self.scene.intersect(r, 0.0, Float::INFINITY) {
|
||||
// Color::gray(1.0 / i.t())
|
||||
let c = 0.5 * (i.normal() + Dir3::new(1.0, 1.0, 1.0));
|
||||
// let c = i.normal();
|
||||
Color::new(c.x, c.y, c.z)
|
||||
} else {
|
||||
Color::black()
|
||||
}
|
||||
}
|
||||
|
||||
fn width(&self) -> u32 {
|
||||
self.camera.width()
|
||||
}
|
||||
|
||||
fn height(&self) -> u32 {
|
||||
self.camera.height()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
use crate::prelude::*;
|
||||
use rand::Rng;
|
||||
|
||||
pub trait Scene<R: Rng> {
|
||||
fn intersect(&self, ray: Ray, min: Float, max: Float) -> Option<Intersection<'_, R>>;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue