Add Oren-Nayar and fix bugs

This commit is contained in:
hal8174 2024-12-27 19:11:02 +01:00
parent 3a37a72f56
commit 745b7d2602
7 changed files with 137 additions and 28 deletions

View file

@ -1,4 +1,4 @@
use egui::{ahash::HashMap, Widget};
use egui::Widget;
use egui_winit_vulkano::{Gui, GuiConfig};
use ray_tracing_core::prelude::*;
use ray_tracing_scene::examples::example_scenes;

View file

@ -6,7 +6,12 @@ use plotters::{
};
use rand::{rngs::SmallRng, SeedableRng};
use ray_tracing_core::prelude::*;
use ray_tracing_material::{diffuse::DiffuseMaterial, mirror::Mirror};
use ray_tracing_material::{
diffuse::DiffuseMaterial,
microfacet::{BeckmannDistribution, Microfacet},
mirror::Mirror,
oren_nayar::OrenNayar,
};
use rayon::iter::{IntoParallelIterator, ParallelExtend, ParallelIterator};
fn main() -> Result<(), Box<dyn std::error::Error>> {
@ -20,6 +25,12 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
let m = DiffuseMaterial::new(color);
generate_chart("diffuse.png", &m, 100, w_in)?;
let m = Microfacet::new(BeckmannDistribution::new(0.5), color);
generate_chart("microfacet.png", &m, 100, w_in)?;
let m = OrenNayar::new(0.5, color);
generate_chart("oren-nayar.png", &m, 100, w_in)?;
Ok(())
}
@ -42,7 +53,6 @@ fn generate_chart<M: Material<SmallRng>>(
let sample_histogram = create_historgram_sampled(m, 400, 400, executions, w_in);
let max = Float::max(eval_histogram.max(), sample_histogram.max());
dbg!(max, eval_histogram.max(), sample_histogram.max());
let area = areas[0].titled("Evaled", ("sans-serif", 30))?;
plot_material(&area, eval_histogram, max)?;
@ -132,6 +142,7 @@ where
_ => unreachable!(),
};
assert!(f >= 0.0 && f <= max && f.is_finite(), "{f}");
let color = plotters::prelude::ViridisRGB::get_color_normalized(f, 0.0, max);
if dir.y() < 0.0 {
lower_area.draw_pixel((dir.x() as f64, dir.z() as f64), &color)?;
@ -263,6 +274,18 @@ fn create_historgram_sampled<M: Material<SmallRng>>(
.map_init(SmallRng::from_entropy, |rng, _| {
let sample = m.sample(w_in, rng);
assert!(
sample.color().r() >= 0.0
&& sample.color().r().is_finite()
&& sample.color().g() >= 0.0
&& sample.color().g().is_finite()
&& sample.color().b() >= 0.0
&& sample.color().b().is_finite(),
"w_in: {w_in:?}; w_out: {:?}; material: {m:?}; color: {:?}",
sample.w_out(),
sample.color()
);
(sample.w_out(), sample.color())
}),
);
@ -285,7 +308,17 @@ fn create_histogram_evaled<M: Material<SmallRng>>(
.map_init(SmallRng::from_entropy, |rng, _| {
let w_out = Dir3::sample_uniform_sphere(rng);
let color = m.eval(w_in, w_out, rng) * w_out.y() * 4.0 * FloatConsts::PI;
let color = m.eval(w_in, w_out, rng) * w_out.y().abs() * 4.0 * FloatConsts::PI;
assert!(
color.r() >= 0.0
&& color.r().is_finite()
&& color.g() >= 0.0
&& color.g().is_finite()
&& color.b() >= 0.0
&& color.b().is_finite(),
"w_in: {w_in:?}; w_out: {w_out:?}; material: {m:?}; color: {color:?}"
);
(w_out, color)
}),

View file

@ -14,7 +14,11 @@ impl DiffuseMaterial {
}
impl<R: Rng> Material<R> for DiffuseMaterial {
fn eval(&self, _w_in: Dir3, _w_out: Dir3, _rng: &mut R) -> Color {
self.albedo
fn eval(&self, _w_in: Dir3, w_out: Dir3, _rng: &mut R) -> Color {
if w_out.y() >= 0.0 {
self.albedo
} else {
Color::black()
}
}
}

View file

@ -2,3 +2,4 @@ pub mod default;
pub mod diffuse;
pub mod microfacet;
pub mod mirror;
pub mod oren_nayar;

View file

@ -2,34 +2,57 @@ use ray_tracing_core::{material::Material, prelude::*};
use std::fmt::Debug;
#[derive(Debug)]
struct Microfacet<D: MicrofacetDistribution + Debug> {
pub struct Microfacet<D: MicrofacetDistribution + Debug> {
dist: D,
color: Color,
}
fn fresnel(w_in: Dir3, n: Dir3, nu_rel: Float) -> Float {
todo!()
}
impl<R: Rng, D: MicrofacetDistribution + Debug + Sync> Material<R> for Microfacet<D> {
fn eval(
&self,
w_in: ray_tracing_core::prelude::Dir3,
w_out: ray_tracing_core::prelude::Dir3,
_rng: &mut R,
) -> ray_tracing_core::prelude::Color {
let w_h = (w_in + w_out).normalize();
let g = self.dist.g1(w_in, w_h) * self.dist.g1(w_out, w_h);
self.color * fresnel(w_in, w_h, 1.0) * g * self.dist.d(w_h) / (4.0 * w_in.y() * w_out.y())
impl<D: MicrofacetDistribution + Debug> Microfacet<D> {
pub fn new(dist: D, color: Color) -> Self {
Self { dist, color }
}
}
struct BeckmannDistribution {
fn fresnel_real(cos_theta_in: Float, nu1: Float, nu2: Float) -> Float {
let nu_rel = nu1 / nu2;
let cos_theta_tr = Float::sqrt(1.0 - nu_rel * nu_rel * (1.0 - cos_theta_in * cos_theta_in));
let rs = ((nu1 * cos_theta_in - nu2 * cos_theta_tr)
/ (nu1 * cos_theta_in + nu2 * cos_theta_tr))
.powi(2);
let rp = ((nu1 * cos_theta_tr - nu2 * cos_theta_in)
/ (nu1 * cos_theta_tr + nu2 * cos_theta_in))
.powi(2);
0.5 * (rs + rp)
}
impl<R: Rng, D: MicrofacetDistribution + Debug + Sync> Material<R> for Microfacet<D> {
fn eval(&self, w_in: Dir3, w_out: Dir3, _rng: &mut R) -> ray_tracing_core::prelude::Color {
if w_out.y() > 0.0 {
let w_h = (w_in + w_out).normalize();
let g = self.dist.g1(w_in, w_h) * self.dist.g1(w_out, w_h);
self.color * fresnel_real(Dir3::dot(w_in, w_h), 1.0, 1.3) * g * self.dist.d(w_h)
/ (4.0 * w_in.y() * w_out.y()).max(0.0)
} else {
Color::black()
}
}
}
#[derive(Debug)]
pub struct BeckmannDistribution {
alpha: Float,
}
impl BeckmannDistribution {
pub fn new(alpha: Float) -> Self {
Self { alpha }
}
}
impl MicrofacetDistribution for BeckmannDistribution {
fn g1(&self, w: Dir3, w_h: Dir3) -> Float {
if w.y() > 0.0 && Dir3::dot(w, w_h) > 0.0 {
@ -55,7 +78,7 @@ impl MicrofacetDistribution for BeckmannDistribution {
}
}
trait MicrofacetDistribution {
pub trait MicrofacetDistribution {
fn g1(&self, w: Dir3, w_h: Dir3) -> Float;
fn d(&self, w_h: Dir3) -> Float;

View file

@ -0,0 +1,39 @@
use ray_tracing_core::prelude::*;
#[derive(Debug)]
pub struct OrenNayar {
rho: Float,
color: Color,
}
impl OrenNayar {
pub fn new(rho: Float, color: Color) -> Self {
Self { rho, color }
}
}
impl<R: Rng> Material<R> for OrenNayar {
fn eval(&self, w_in: Dir3, w_out: Dir3, _rng: &mut R) -> Color {
if w_out.y() > 0.0 {
let cos_alpha = Float::min(w_in.y(), w_out.y());
let cos_beta = Float::max(w_in.y(), w_out.y());
let a = 1.0 - (self.rho * self.rho) / (2.0 * self.rho * self.rho + 0.33);
let b = 0.45 * self.rho * self.rho / (self.rho * self.rho + 0.09);
let sin_alpha = Float::sqrt(1.0 - cos_alpha * cos_alpha);
let tan_beta = Float::sqrt(1.0 - cos_beta * cos_beta) / cos_beta;
let w_in_tick = w_in - Dir3::up() * w_in.y();
let w_out_tick = w_out - Dir3::up() * w_out.y();
let cos_phi = Dir3::dot(w_in_tick, w_out_tick);
self.color
* FloatConsts::FRAC_1_PI
* (a + b * Float::max(0.0, cos_phi) * sin_alpha * tan_beta)
} else {
Color::black()
}
}
}

View file

@ -1,6 +1,10 @@
use rand::Rng;
use ray_tracing_core::{light::AreaLight, prelude::*, scene::Scene};
use ray_tracing_material::{diffuse::DiffuseMaterial, mirror::Mirror};
use ray_tracing_material::{
diffuse::DiffuseMaterial,
microfacet::{BeckmannDistribution, Microfacet},
oren_nayar::OrenNayar,
};
use std::fmt::Debug;
use crate::{
@ -12,6 +16,11 @@ use super::ExampleScene;
pub fn scene<R: Rng + Debug + 'static>() -> ExampleScene<R> {
let f = || {
let color = Color::new(0.2, 0.2, 0.9);
// let m = DiffuseMaterial::new(color);
// let m = Microfacet::new(BeckmannDistribution::new(0.5), color);
let m = OrenNayar::new(0.5, color);
let obj = ObjData::new("ray-tracing-scene/obj/stanford_dragon.obj").unwrap();
let mut triangles = obj
@ -20,7 +29,7 @@ pub fn scene<R: Rng + Debug + 'static>() -> ExampleScene<R> {
.collect::<Vec<_>>();
let materials = vec![
BVHMaterial::new_material(DiffuseMaterial::new(Color::new(0.2, 0.2, 0.9))),
BVHMaterial::new_material(m),
BVHMaterial::new_light(AreaLight::new(Color::white() * 30.0)),
];
@ -50,7 +59,7 @@ pub fn scene<R: Rng + Debug + 'static>() -> ExampleScene<R> {
ExampleScene {
scene: f,
camera_pos: Pos3::new(0.0, 0.0, -400.0),
camera_pos: Pos3::new(-150.0, 100.0, 250.0),
camera_look_at: Pos3::new(0.0, 0.0, 0.0),
camera_up: Dir3::up(),
horizontal_fov: 90.0_f32.to_radians(),