Add iridescent material
This commit is contained in:
parent
d475c1ef04
commit
c26a4bece0
8 changed files with 276 additions and 34 deletions
3
.cargo/config.toml
Normal file
3
.cargo/config.toml
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
|
||||
[build]
|
||||
rustflags = ["-C raget-cpu=native"]
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -1,2 +1,3 @@
|
|||
target
|
||||
**.exr
|
||||
**.png
|
||||
|
|
|
|||
60
flake.nix
60
flake.nix
|
|
@ -6,22 +6,30 @@
|
|||
flake-utils.url = "github:numtide/flake-utils";
|
||||
};
|
||||
|
||||
outputs = { self, nixpkgs, flake-utils }:
|
||||
flake-utils.lib.eachDefaultSystem (system:
|
||||
outputs =
|
||||
{
|
||||
self,
|
||||
nixpkgs,
|
||||
flake-utils,
|
||||
}:
|
||||
flake-utils.lib.eachDefaultSystem (
|
||||
system:
|
||||
let
|
||||
pkgs = nixpkgs.legacyPackages.${system};
|
||||
# Read the file relative to the flake's root
|
||||
overrides = (builtins.fromTOML (builtins.readFile (self + "/rust-toolchain.toml")));
|
||||
libPath = with pkgs; lib.makeLibraryPath [
|
||||
# load external libraries that you need in your rust project here
|
||||
];
|
||||
libPath =
|
||||
with pkgs;
|
||||
lib.makeLibraryPath [
|
||||
# load external libraries that you need in your rust project here
|
||||
];
|
||||
in
|
||||
{
|
||||
devShells.default = pkgs.mkShell rec {
|
||||
nativeBuildInputs = [ pkgs.pkg-config ];
|
||||
|
||||
packages = with pkgs; [
|
||||
|
||||
|
||||
tev
|
||||
shaderc
|
||||
vulkan-headers
|
||||
|
|
@ -31,6 +39,7 @@
|
|||
python3
|
||||
];
|
||||
buildInputs = with pkgs; [
|
||||
fontconfig
|
||||
clang
|
||||
llvmPackages.bintools
|
||||
rustup
|
||||
|
|
@ -38,14 +47,14 @@
|
|||
|
||||
# VULKAN_SDK = "${vulkan-validation-layers}/share/vulkan/explicit_layer.d";
|
||||
|
||||
LD_LIBRARY_PATH="${pkgs.wayland}/lib:${pkgs.libxkbcommon}/lib:${pkgs.vulkan-loader}/lib:${pkgs.vulkan-validation-layers}/lib";
|
||||
LD_LIBRARY_PATH = "${pkgs.wayland}/lib:${pkgs.libxkbcommon}/lib:${pkgs.vulkan-loader}/lib:${pkgs.vulkan-validation-layers}/lib";
|
||||
VK_LAYER_PATH = "${pkgs.vulkan-validation-layers}/share/vulkan/explicit_layer.d";
|
||||
|
||||
RUSTC_VERSION = overrides.toolchain.channel;
|
||||
|
||||
|
||||
# https://github.com/rust-lang/rust-bindgen#environment-variables
|
||||
LIBCLANG_PATH = pkgs.lib.makeLibraryPath [ pkgs.llvmPackages_latest.libclang.lib ];
|
||||
|
||||
|
||||
shellHook = ''
|
||||
export PATH=$PATH:''${CARGO_HOME:-~/.cargo}/bin
|
||||
export PATH=$PATH:''${RUSTUP_HOME:-~/.rustup}/toolchains/$RUSTC_VERSION-x86_64-unknown-linux-gnu/bin/
|
||||
|
|
@ -53,26 +62,27 @@
|
|||
'';
|
||||
|
||||
# Add precompiled library to rustc search path
|
||||
RUSTFLAGS = (builtins.map (a: ''-L ${a}/lib'') [
|
||||
# add libraries here (e.g. pkgs.libvmi)
|
||||
]);
|
||||
|
||||
RUSTFLAGS = (
|
||||
builtins.map (a: ''-L ${a}/lib'') [
|
||||
# add libraries here (e.g. pkgs.libvmi)
|
||||
]
|
||||
);
|
||||
|
||||
# LD_LIBRARY_PATH = pkgs.lib.makeLibraryPath (buildInputs ++ nativeBuildInputs);
|
||||
|
||||
|
||||
# Add glibc, clang, glib, and other headers to bindgen search path
|
||||
BINDGEN_EXTRA_CLANG_ARGS =
|
||||
# Includes normal include path
|
||||
(builtins.map (a: ''-I"${a}/include"'') [
|
||||
# add dev libraries here (e.g. pkgs.libvmi.dev)
|
||||
pkgs.glibc.dev
|
||||
])
|
||||
# Includes with special directory paths
|
||||
++ [
|
||||
''-I"${pkgs.llvmPackages_latest.libclang.lib}/lib/clang/${pkgs.llvmPackages_latest.libclang.version}/include"''
|
||||
''-I"${pkgs.glib.dev}/include/glib-2.0"''
|
||||
''-I${pkgs.glib.out}/lib/glib-2.0/include/''
|
||||
];
|
||||
# Includes normal include path
|
||||
(builtins.map (a: ''-I"${a}/include"'') [
|
||||
# add dev libraries here (e.g. pkgs.libvmi.dev)
|
||||
pkgs.glibc.dev
|
||||
])
|
||||
# Includes with special directory paths
|
||||
++ [
|
||||
''-I"${pkgs.llvmPackages_latest.libclang.lib}/lib/clang/${pkgs.llvmPackages_latest.libclang.version}/include"''
|
||||
''-I"${pkgs.glib.dev}/include/glib-2.0"''
|
||||
''-I${pkgs.glib.out}/lib/glib-2.0/include/''
|
||||
];
|
||||
};
|
||||
}
|
||||
);
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ use rand::{rngs::SmallRng, SeedableRng};
|
|||
use ray_tracing_core::prelude::*;
|
||||
use ray_tracing_material::{
|
||||
diffuse::DiffuseMaterial,
|
||||
iridescent::Iridescent,
|
||||
microfacet::{BeckmannDistribution, Microfacet},
|
||||
mirror::Mirror,
|
||||
oren_nayar::OrenNayar,
|
||||
|
|
@ -35,6 +36,9 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||
let m = OrenNayar::new(0.5, color);
|
||||
generate_chart("oren-nayar.png", &m, 100, w_in)?;
|
||||
|
||||
let m = Iridescent::new(300.0, 300.0, 1.0, 1.5, 10);
|
||||
generate_chart("iridescent.png", &m, 2, w_in)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
|
|||
207
ray-tracing-material/src/iridescent.rs
Normal file
207
ray-tracing-material/src/iridescent.rs
Normal file
|
|
@ -0,0 +1,207 @@
|
|||
use core::f32;
|
||||
|
||||
use ray_tracing_core::prelude::*;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Iridescent {
|
||||
d1: f32,
|
||||
d2: f32,
|
||||
n1: f32,
|
||||
n2: f32,
|
||||
n: f32,
|
||||
}
|
||||
|
||||
impl Iridescent {
|
||||
pub fn new(d1: f32, d2: f32, n1: f32, n2: f32, n: u32) -> Self {
|
||||
Self {
|
||||
d1,
|
||||
d2,
|
||||
n1,
|
||||
n2,
|
||||
n: n as f32,
|
||||
}
|
||||
}
|
||||
|
||||
fn fresnel_reflection(&self, theta1: f32, theta2: f32, s_polaized: bool) -> f32 {
|
||||
if s_polaized {
|
||||
(self.n1 * theta1.cos() - self.n2 * theta2.cos())
|
||||
/ (self.n1 * theta1.cos() + self.n2 * theta2.cos())
|
||||
} else {
|
||||
(self.n2 * theta1.cos() - self.n1 * theta2.cos())
|
||||
/ (self.n2 * theta1.cos() + self.n1 * theta2.cos())
|
||||
}
|
||||
}
|
||||
|
||||
fn abs_r_1_squared(&self, l: f32, theta1: f32, theta2: f32, s_polaized: bool) -> f32 {
|
||||
let r12 = self.fresnel_reflection(theta1, theta2, s_polaized);
|
||||
|
||||
let phi = 2.0 * f32::consts::PI * self.n2 * self.d2 * theta2.cos() / l;
|
||||
|
||||
let num_real = r12 * (1.0 - f32::cos(-2.0 * phi));
|
||||
let num_imag = r12 * (1.0 - f32::sin(-2.0 * phi));
|
||||
|
||||
let denom_real = 1.0 - r12 * r12 * f32::cos(-2.0 * phi);
|
||||
let denom_imag = 1.0 - r12 * r12 * f32::sin(-2.0 * phi);
|
||||
|
||||
let real = (num_real * denom_real + num_imag * denom_imag)
|
||||
/ (denom_real * denom_real + denom_imag * denom_imag);
|
||||
|
||||
let imag = (num_imag * denom_real - num_real * denom_imag)
|
||||
/ (denom_real * denom_real + denom_imag * denom_imag);
|
||||
|
||||
real * real + imag * imag
|
||||
}
|
||||
|
||||
fn abs_C_squared(&self, l: f32, theta1: f32, theta2: f32, s_polaized: bool) -> f32 {
|
||||
let local_abs_r_1_squared = self.abs_r_1_squared(l, theta1, theta2, s_polaized);
|
||||
|
||||
local_abs_r_1_squared / (local_abs_r_1_squared + 1.0)
|
||||
}
|
||||
|
||||
fn cos_K_Delta(&self, l: f32, theta1: f32, theta2: f32, s_polaized: bool) -> f32 {
|
||||
let k1z = 2.0 * f32::consts::PI * self.n1 * theta1.cos() / l;
|
||||
let k2z = 2.0 * f32::consts::PI * self.n2 * theta2.cos() / l;
|
||||
|
||||
let omega = if s_polaized {
|
||||
k2z / k1z + k1z / k2z
|
||||
} else {
|
||||
(self.n1 * self.n1 * k2z) / (self.n2 * self.n2 * k1z)
|
||||
+ (self.n2 * self.n2 * k1z) / (self.n1 * self.n1 * k2z)
|
||||
};
|
||||
|
||||
f32::cos(k1z * self.d1) * f32::cos(k2z * self.d2)
|
||||
- 0.5 * omega * f32::sin(k1z * self.d1) * f32::sin(k2z * self.d2)
|
||||
}
|
||||
|
||||
fn reflectance(&self, l: f32, theta1: f32, s_polaized: bool) -> f32 {
|
||||
let theta2 = f32::asin(f32::sin(theta1) * self.n1 / self.n2);
|
||||
|
||||
let local_cos_k_delta = self.cos_K_Delta(l, theta1, theta2, s_polaized);
|
||||
|
||||
let local_c_squred = self.abs_C_squared(l, theta1, theta2, s_polaized);
|
||||
|
||||
if !local_c_squred.is_normal() || !local_cos_k_delta.is_normal() {
|
||||
dbg!((
|
||||
l,
|
||||
theta1,
|
||||
theta2,
|
||||
local_cos_k_delta,
|
||||
local_c_squred,
|
||||
s_polaized
|
||||
));
|
||||
}
|
||||
|
||||
if local_cos_k_delta.abs() <= 1.0 {
|
||||
let k_delta = f32::acos(local_cos_k_delta);
|
||||
|
||||
if !k_delta.is_normal() {
|
||||
dbg!((
|
||||
l,
|
||||
theta1,
|
||||
theta2,
|
||||
local_cos_k_delta,
|
||||
local_c_squred,
|
||||
s_polaized,
|
||||
k_delta,
|
||||
));
|
||||
}
|
||||
let u = f32::sin(k_delta) / f32::sin(self.n * k_delta);
|
||||
|
||||
local_c_squred / (local_c_squred + u * u)
|
||||
} else {
|
||||
let imk_delta = -f32::ln(f32::abs(
|
||||
local_cos_k_delta - f32::sqrt(local_cos_k_delta * local_cos_k_delta - 1.0),
|
||||
));
|
||||
|
||||
if !imk_delta.is_normal() {
|
||||
dbg!((
|
||||
l,
|
||||
theta1,
|
||||
theta2,
|
||||
local_cos_k_delta,
|
||||
local_c_squred,
|
||||
s_polaized,
|
||||
imk_delta,
|
||||
));
|
||||
}
|
||||
let u = f32::sinh(imk_delta) / f32::sinh(self.n * imk_delta);
|
||||
|
||||
local_c_squred / (local_c_squred + u * u)
|
||||
}
|
||||
}
|
||||
|
||||
fn averaged_reflectance(&self, l: f32, theta1: f32) -> f32 {
|
||||
0.5 * (self.reflectance(l, theta1, true) + self.reflectance(l, theta1, false))
|
||||
}
|
||||
|
||||
fn piecewise_gaussian(l: f32, mu: f32, tau1: f32, tau2: f32) -> f32 {
|
||||
if l < mu {
|
||||
let s = tau1 * (l - mu);
|
||||
f32::exp(-0.5 * s * s)
|
||||
} else {
|
||||
let s = tau2 * (l - mu);
|
||||
f32::exp(-0.5 * s * s)
|
||||
}
|
||||
}
|
||||
|
||||
fn color_matching_function(l: f32) -> Color {
|
||||
Color::new(
|
||||
1.056 * Self::piecewise_gaussian(l, 599.8, 0.0264, 0.0323)
|
||||
+ 0.362 * Self::piecewise_gaussian(l, 442.0, 0.0624, 0.0374)
|
||||
- 0.065 * Self::piecewise_gaussian(l, 501.1, 0.0490, 0.0382),
|
||||
0.821 * Self::piecewise_gaussian(l, 568.8, 0.0213, 0.0247)
|
||||
+ 0.286 * Self::piecewise_gaussian(l, 530.9, 0.0613, 0.0322),
|
||||
1.217 * Self::piecewise_gaussian(l, 437.0, 0.0845, 0.0278)
|
||||
+ 0.681 * Self::piecewise_gaussian(l, 459.0, 0.0385, 0.0725),
|
||||
)
|
||||
}
|
||||
|
||||
fn monte_carlo_reflectance<R: Rng>(&self, theta1: f32, rng: &mut R) -> Color {
|
||||
let range = 400.0..800.0;
|
||||
let size = range.end - range.start;
|
||||
|
||||
let l = rng.gen_range(range);
|
||||
|
||||
let r = self.averaged_reflectance(l, theta1);
|
||||
|
||||
if !r.is_normal() {
|
||||
dbg!((theta1, l, r));
|
||||
}
|
||||
|
||||
let color = Self::color_matching_function(l);
|
||||
|
||||
if !color.r().is_normal() || !color.g().is_normal() || !color.b().is_normal() {
|
||||
dbg!(color);
|
||||
}
|
||||
|
||||
color * r
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: Rng> Material<R> for Iridescent {
|
||||
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_in;
|
||||
let _ = w_out;
|
||||
let _ = rng;
|
||||
Color::black()
|
||||
}
|
||||
|
||||
fn sample(&self, w_in: Dir3, rng: &mut R) -> ray_tracing_core::material::SampleResult {
|
||||
let w_out = (2.0 * Dir3::up() * w_in.y()) - w_in;
|
||||
|
||||
let color = self.monte_carlo_reflectance(f32::acos(w_out.z()), rng);
|
||||
|
||||
ray_tracing_core::material::SampleResult::new(w_out, color, true)
|
||||
}
|
||||
|
||||
fn pdf(&self, w_in: Dir3, w_out: Dir3) -> Float {
|
||||
let _ = w_out;
|
||||
let _ = w_in;
|
||||
0.0
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
pub mod default;
|
||||
pub mod diffuse;
|
||||
pub mod iridescent;
|
||||
pub mod microfacet;
|
||||
pub mod mirror;
|
||||
pub mod oren_nayar;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,7 @@
|
|||
use ray_tracing_core::{prelude::*, scene::Scene};
|
||||
use ray_tracing_material::iridescent::Iridescent;
|
||||
use ray_tracing_material::microfacet::{BeckmannDistribution, Microfacet};
|
||||
use ray_tracing_material::mirror::Mirror;
|
||||
use std::collections::HashMap;
|
||||
use std::fmt::Debug;
|
||||
|
||||
|
|
@ -19,9 +22,21 @@ pub fn example_scenes<R: Rng + Debug + 'static>() -> HashMap<&'static str, Box<d
|
|||
let mut map: HashMap<&str, Box<dyn ExampleScene<R>>> = HashMap::new();
|
||||
map.insert("basic_cornel", Box::new(basic_cornell::BasicCornell::new()));
|
||||
map.insert("cornel2", Box::new(cornell2::Cornell2::new()));
|
||||
let color = Color::new(0.2, 0.2, 0.9);
|
||||
let material = Microfacet::new(BeckmannDistribution::new(0.01), color);
|
||||
map.insert(
|
||||
"stanford_dragon",
|
||||
Box::new(stanford_dragon::StanfordDragon::new()),
|
||||
"stanford_dragon_microfacet",
|
||||
Box::new(stanford_dragon::StanfordDragon::new(material)),
|
||||
);
|
||||
let material = Iridescent::new(300.0, 300.0, 1.0, 1.5, 10);
|
||||
map.insert(
|
||||
"stanford_dragon_iridescent",
|
||||
Box::new(stanford_dragon::StanfordDragon::new(material)),
|
||||
);
|
||||
let material = Mirror::new(Color::white());
|
||||
map.insert(
|
||||
"stanford_dragon_mirror",
|
||||
Box::new(stanford_dragon::StanfordDragon::new(material)),
|
||||
);
|
||||
map.insert(
|
||||
"stanford_dragon_as",
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ use ray_tracing_material::{
|
|||
microfacet::{BeckmannDistribution, Microfacet},
|
||||
oren_nayar::OrenNayar,
|
||||
};
|
||||
use std::cell::OnceCell;
|
||||
use std::cell::{Cell, OnceCell};
|
||||
|
||||
use crate::{
|
||||
parse_obj::ObjData,
|
||||
|
|
@ -13,19 +13,21 @@ use crate::{
|
|||
|
||||
use super::ExampleScene;
|
||||
|
||||
pub struct StanfordDragon<R: Rng> {
|
||||
pub struct StanfordDragon<R: Rng, M: Material<R>> {
|
||||
scene: OnceCell<TriangleBVH<R>>,
|
||||
material: Cell<Option<M>>,
|
||||
}
|
||||
|
||||
impl<R: Rng> StanfordDragon<R> {
|
||||
pub fn new() -> Self {
|
||||
impl<R: Rng, M: Material<R>> StanfordDragon<R, M> {
|
||||
pub fn new(material: M) -> Self {
|
||||
Self {
|
||||
scene: OnceCell::new(),
|
||||
material: Cell::new(Some(material)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: Rng + 'static> ExampleScene<R> for StanfordDragon<R> {
|
||||
impl<R: Rng + 'static, M: Material<R> + 'static> ExampleScene<R> for StanfordDragon<R, M> {
|
||||
fn get_scene(&self) -> Box<dyn Scene<R> + Sync> {
|
||||
let s = self.scene.get_or_init(|| {
|
||||
let obj = ObjData::new("ray-tracing-scene/obj/stanford_dragon.obj").unwrap();
|
||||
|
|
@ -35,9 +37,8 @@ impl<R: Rng + 'static> ExampleScene<R> for StanfordDragon<R> {
|
|||
.map(|t| Triangle::new(t.v, 0))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let color = Color::new(0.2, 0.2, 0.9);
|
||||
let materials = vec![
|
||||
BVHMaterial::new_material(Microfacet::new(BeckmannDistribution::new(0.01), color)),
|
||||
BVHMaterial::new_material(self.material.take().unwrap()),
|
||||
BVHMaterial::new_material(OrenNayar::new(0.5, Color::new(0.8, 0.8, 0.8))),
|
||||
BVHMaterial::new_material(OrenNayar::new(0.5, Color::new(0.9, 0.0, 0.0))),
|
||||
BVHMaterial::new_material(OrenNayar::new(0.5, Color::new(0.0, 0.9, 0.0))),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue