got some transparency?
This commit is contained in:
parent
d1d7b59081
commit
73bbd64cc0
11 changed files with 262 additions and 55 deletions
36
assignment-1d/Cargo.lock
generated
36
assignment-1d/Cargo.lock
generated
|
@ -311,6 +311,12 @@ dependencies = [
|
|||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc"
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
|
@ -677,6 +683,12 @@ version = "1.0.11"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5583e89e108996506031660fe09baa5011b9dd0341b89029313006d1fb508d70"
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09"
|
||||
|
||||
[[package]]
|
||||
name = "safe_arch"
|
||||
version = "0.6.0"
|
||||
|
@ -698,6 +710,17 @@ version = "1.0.152"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb"
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.89"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "020ff22c755c2ed3f8cf162dbb41a7268d934702f3ed3631656ea597e08fc3db"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sharded-slab"
|
||||
version = "0.1.4"
|
||||
|
@ -806,6 +829,16 @@ dependencies = [
|
|||
"tracing-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-serde"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc6b213177105856957181934e4920de57730fc69bf42c37ee5bb664d406d9e1"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"tracing-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-subscriber"
|
||||
version = "0.3.16"
|
||||
|
@ -813,11 +846,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "a6176eae26dd70d0c919749377897b54a9276bd7061339665dd68777926b5a70"
|
||||
dependencies = [
|
||||
"nu-ansi-term",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sharded-slab",
|
||||
"smallvec",
|
||||
"thread_local",
|
||||
"tracing-core",
|
||||
"tracing-log",
|
||||
"tracing-serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
|
@ -32,4 +32,4 @@ ordered-float = "3.4.0"
|
|||
rand = "0.8.5"
|
||||
rayon = "1.6.1"
|
||||
tracing = "0.1.37"
|
||||
tracing-subscriber = "0.3.16"
|
||||
tracing-subscriber = { version = "0.3.16", features = ["json"] }
|
||||
|
|
|
@ -3,18 +3,18 @@
|
|||
.PRECIOUS: $(EXAMPLES_PPM)
|
||||
|
||||
DEBUG :=
|
||||
RELEASE_FLAG := --release
|
||||
|
||||
ifeq ($(DEBUG),1)
|
||||
RELEASE_FLAG :=
|
||||
endif
|
||||
|
||||
CARGO_FLAGS := --release
|
||||
RAYTRACER_FLAGS :=
|
||||
DOCKER := docker
|
||||
ZIP := zip
|
||||
PANDOC := pandoc
|
||||
CONVERT := convert
|
||||
|
||||
ifeq ($(DEBUG),1)
|
||||
RAYTRACER_FLAGS += -vvvv
|
||||
else
|
||||
endif
|
||||
|
||||
HANDIN := ./hw1d.michael.zhang.zip
|
||||
BINARY := ./raytracer1d
|
||||
SOURCES := Cargo.toml $(shell find -name "*.rs")
|
||||
|
@ -42,7 +42,7 @@ $(HANDIN): $(BINARY) Makefile Cargo.toml Cargo.lock README.md $(EXAMPLES_PNG) $(
|
|||
$(ZIP) -r $@ src examples $^
|
||||
|
||||
examples/%.ppm: examples/%.txt $(SOURCES)
|
||||
cargo run $(RELEASE_FLAG) -- -o $@ $(RAYTRACER_FLAGS) $<
|
||||
cargo run $(CARGO_FLAGS) -- -o $@ $(RAYTRACER_FLAGS) $<
|
||||
|
||||
examples/%.png: examples/%.ppm
|
||||
convert $< $@
|
||||
|
|
45
assignment-1d/examples/soft-shadow-demo.txt
Normal file
45
assignment-1d/examples/soft-shadow-demo.txt
Normal file
|
@ -0,0 +1,45 @@
|
|||
imsize 640 480
|
||||
eye 0 0 15
|
||||
viewdir 0 0 -1
|
||||
hfov 60
|
||||
updir 0 1 0
|
||||
bkgcolor 0.5 0.5 0.5
|
||||
|
||||
depthcueing 0.5 0.5 0.5 1 0.4 60 0
|
||||
|
||||
light 10 10 -10 1 1 1 1
|
||||
|
||||
mtlcolor 0.5 1 0.5 1 1 1 0.2 1 0.1 5 0.5 1
|
||||
sphere 4.5 4.5 -20 4.5
|
||||
sphere -4.5 -4.5 -20 4.5
|
||||
|
||||
mtlcolor 1 0.5 0.5 1 1 1 0.2 0.8 0 5 0.8 1
|
||||
sphere -10 0 -30 4
|
||||
sphere -20 0 -30 4
|
||||
sphere -30 0 -30 4
|
||||
sphere -40 0 -30 4
|
||||
sphere 0 0 -30 4
|
||||
sphere 10 0 -30 4
|
||||
sphere 20 0 -30 4
|
||||
sphere 30 0 -30 4
|
||||
sphere 40 0 -30 4
|
||||
|
||||
sphere -10 -10 -30 4
|
||||
sphere -20 -10 -30 4
|
||||
sphere -30 -10 -30 4
|
||||
sphere -40 -10 -30 4
|
||||
sphere 0 -10 -30 4
|
||||
sphere 10 -10 -30 4
|
||||
sphere 20 -10 -30 4
|
||||
sphere 30 -10 -30 4
|
||||
sphere 40 -10 -30 4
|
||||
|
||||
sphere -10 10 -30 4
|
||||
sphere -20 10 -30 4
|
||||
sphere -30 10 -30 4
|
||||
sphere -40 10 -30 4
|
||||
sphere 0 10 -30 4
|
||||
sphere 10 10 -30 4
|
||||
sphere 20 10 -30 4
|
||||
sphere 30 10 -30 4
|
||||
sphere 40 10 -30 4
|
11
assignment-1d/examples/transparency-distance.txt
Normal file
11
assignment-1d/examples/transparency-distance.txt
Normal file
|
@ -0,0 +1,11 @@
|
|||
eye 0 0 10
|
||||
viewdir 0 0 -1
|
||||
updir 0 1 0
|
||||
hfov 60
|
||||
imsize 512 512
|
||||
bkgcolor 0.5 0.7 0.9 1
|
||||
light 0 -1 0 0 1 1 1
|
||||
|
||||
mtlcolor 0.7 0.2 0.7 1 1 1 0 0.05 0.1 80 0.5 1.5
|
||||
sphere 1 1 -6 3
|
||||
sphere -1 -1 1 3
|
|
@ -7,9 +7,14 @@ use std::path::PathBuf;
|
|||
use anyhow::Result;
|
||||
use assignment_1d::{image::Image, ray::Ray, scene::Scene};
|
||||
|
||||
use clap::Parser;
|
||||
use clap::{ArgAction, Parser};
|
||||
|
||||
use rayon::prelude::{IntoParallelIterator, ParallelIterator};
|
||||
use tracing::metadata::LevelFilter;
|
||||
use tracing_subscriber::{
|
||||
fmt::Layer, prelude::__tracing_subscriber_SubscriberExt,
|
||||
util::SubscriberInitExt,
|
||||
};
|
||||
|
||||
/// Simple raytracer with Blinn-Phong illumination and shadowing.
|
||||
#[derive(Parser)]
|
||||
|
@ -31,16 +36,33 @@ struct Opt {
|
|||
/// Override distance from eye
|
||||
#[clap(long = "distance", default_value = "1.0")]
|
||||
distance: f64,
|
||||
|
||||
/// Verbosity
|
||||
#[clap(short, long, action = ArgAction::Count)]
|
||||
verbosity: u8,
|
||||
}
|
||||
|
||||
fn main() -> Result<()> {
|
||||
let opt = Opt::parse();
|
||||
|
||||
let level_filter = match opt.verbosity {
|
||||
0 => LevelFilter::ERROR,
|
||||
1 => LevelFilter::WARN,
|
||||
2 => LevelFilter::INFO,
|
||||
3 => LevelFilter::DEBUG,
|
||||
_ => LevelFilter::TRACE,
|
||||
};
|
||||
|
||||
// Set up logging
|
||||
tracing_subscriber::fmt()
|
||||
let layer = Layer::default()
|
||||
.json()
|
||||
.with_target(false)
|
||||
.with_timer(tracing_subscriber::fmt::time::uptime())
|
||||
.with_level(true)
|
||||
.with_level(true);
|
||||
|
||||
tracing_subscriber::registry()
|
||||
.with(layer)
|
||||
.with(level_filter)
|
||||
.init();
|
||||
|
||||
// Rename the output file if it's not provided
|
||||
|
@ -68,6 +90,9 @@ fn main() -> Result<()> {
|
|||
// Loop through every single pixel of the output file
|
||||
let pixels = pixels_iter
|
||||
.map(|(px, py)| {
|
||||
let span = trace_span!("main_loop", px = px, py = py);
|
||||
let _enter = span.enter();
|
||||
|
||||
let pixel_in_space = translate_pixel(px, py);
|
||||
|
||||
let ray_start = if scene.parallel_projection {
|
||||
|
@ -84,6 +109,7 @@ fn main() -> Result<()> {
|
|||
|
||||
let ray = Ray::from_endpoints(ray_start, pixel_in_space);
|
||||
|
||||
// let res= rayon::spawn(|| scene.trace_single_ray(ray, 0));
|
||||
scene.trace_single_ray(ray, 0)
|
||||
})
|
||||
.collect::<Result<Vec<_>>>()?;
|
||||
|
|
|
@ -19,6 +19,8 @@ use super::{
|
|||
// TODO: Is this a good constant?
|
||||
const JITTER_CONST: f64 = 0.05;
|
||||
|
||||
const ZERO_COLOR: Color = Color::new(0.0, 0.0, 0.0);
|
||||
|
||||
impl Scene {
|
||||
/// Determine the color that should be used to fill this pixel.
|
||||
///
|
||||
|
@ -35,6 +37,10 @@ impl Scene {
|
|||
intersection_context: IntersectionContext,
|
||||
depth: usize,
|
||||
) -> Result<Color> {
|
||||
let span =
|
||||
trace_span!("compute_pixel_color", intersection = ?intersection_context);
|
||||
let _enter = span.enter();
|
||||
|
||||
let material = match self.materials.get(object.material_idx) {
|
||||
Some(v) => v,
|
||||
None => bail!("Material index not found."),
|
||||
|
@ -138,49 +144,21 @@ impl Scene {
|
|||
};
|
||||
|
||||
let (eta_i, eta_t) = match intersection_context.exiting {
|
||||
true => (material.eta, 1.0),
|
||||
false => (1.0, material.eta),
|
||||
// true => (material.eta, 1.0),
|
||||
_ => (1.0, material.eta),
|
||||
};
|
||||
|
||||
let transparency = if eta_t < 1.0 {
|
||||
Vector::default()
|
||||
ZERO_COLOR
|
||||
} else {
|
||||
let n = intersection_context.normal.normalize();
|
||||
let i = incident_ray.direction.normalize();
|
||||
|
||||
assert!(eta_t != 0.0, "wtf eta_t is 0");
|
||||
|
||||
// Slide 69
|
||||
let cos_theta_i = dot(i, n);
|
||||
let sin_theta_i = (1.0 - cos_theta_i.powi(2)).sqrt();
|
||||
let sin_theta_t = (eta_i / eta_t) * sin_theta_i;
|
||||
let cos_theta_t = (1.0 - sin_theta_t.powi(2)).sqrt();
|
||||
|
||||
let fresnel_coefficient =
|
||||
self.compute_fresnel_coefficient(&material, i, n);
|
||||
|
||||
// Calculate refraction direction
|
||||
let a = (-n).normalize() * cos_theta_t;
|
||||
let s_direction = cos_theta_i * n - i;
|
||||
let m_unit = s_direction.normalize();
|
||||
let b = m_unit * sin_theta_t;
|
||||
let t = a + b;
|
||||
|
||||
// Jitter a bit to reduce acne
|
||||
// TODO: Is this a good constant?
|
||||
let origin = intersection_context.point;
|
||||
let origin = origin + JITTER_CONST * t;
|
||||
|
||||
let ray = Ray::new(origin, t);
|
||||
assert!(
|
||||
!ray.has_nan(),
|
||||
"ray is nan WTF {cos_theta_i} {sin_theta_i} ({}) {sin_theta_t} {cos_theta_t} | normal: {:?}",
|
||||
eta_i / eta_t,
|
||||
intersection_context.normal,
|
||||
);
|
||||
let t_lambda = self.trace_single_ray(ray, depth + 1)?;
|
||||
|
||||
(1.0 - fresnel_coefficient) * (1.0 - material.alpha) * t_lambda
|
||||
self.compute_transparency(
|
||||
&intersection_context,
|
||||
incident_ray,
|
||||
material,
|
||||
eta_i,
|
||||
eta_t,
|
||||
depth,
|
||||
)?
|
||||
};
|
||||
|
||||
// This is the result of the Phong illumination equation.
|
||||
|
@ -349,11 +327,22 @@ impl Scene {
|
|||
incident_ray: Vector,
|
||||
normal: Vector,
|
||||
) -> f64 {
|
||||
let cos_theta_i = dot(incident_ray, normal);
|
||||
let mut cos_theta_i = dot(incident_ray, normal);
|
||||
|
||||
if cos_theta_i < 0.0 {
|
||||
cos_theta_i = dot(incident_ray, -normal);
|
||||
}
|
||||
|
||||
let f0 = ((material.eta - 1.0) / (material.eta + 1.0)).powi(2);
|
||||
let fr = f0 * 1.0 + (1.0 - f0) * (1.0 - cos_theta_i).powi(5);
|
||||
|
||||
if fr < 0.0 || fr > 1.0 {
|
||||
warn!(
|
||||
eta = material.eta,
|
||||
cos_theta_i, f0, fr, "fresnel coefficient outside of 0 - 1"
|
||||
);
|
||||
}
|
||||
|
||||
fr
|
||||
}
|
||||
|
||||
|
@ -370,6 +359,89 @@ impl Scene {
|
|||
|
||||
r
|
||||
}
|
||||
|
||||
fn compute_transparency(
|
||||
&self,
|
||||
intersection_context: &IntersectionContext,
|
||||
incident_ray: Ray,
|
||||
material: &Material,
|
||||
eta_i: f64,
|
||||
eta_t: f64,
|
||||
depth: usize,
|
||||
) -> Result<Color> {
|
||||
let span =
|
||||
trace_span!("compute_transparency", eta_i = eta_i, eta_t = eta_t);
|
||||
let _enter = span.enter();
|
||||
|
||||
let mut n = intersection_context.normal.normalize();
|
||||
if intersection_context.exiting {
|
||||
n = -n;
|
||||
}
|
||||
|
||||
let i = incident_ray.direction.normalize();
|
||||
|
||||
assert!(eta_t != 0.0, "wtf eta_t is 0");
|
||||
|
||||
// Slide 69
|
||||
let mut cos_theta_i = dot(i, n);
|
||||
|
||||
if cos_theta_i > 1.0 {
|
||||
// warn!("[{depth}] cos_theta_i = {cos_theta_i}");
|
||||
cos_theta_i = 1.0;
|
||||
}
|
||||
|
||||
let sin_theta_i = (1.0 - cos_theta_i.powi(2)).sqrt();
|
||||
|
||||
// Total internal reflection check
|
||||
if sin_theta_i * eta_i > eta_t {
|
||||
warn!(
|
||||
sin_theta_i,
|
||||
eta_i, eta_t, "Total internal reflection check failed."
|
||||
);
|
||||
return Ok(ZERO_COLOR);
|
||||
}
|
||||
|
||||
let sin_theta_t = (eta_i / eta_t) * sin_theta_i;
|
||||
let cos_theta_t = (1.0 - sin_theta_t.powi(2)).sqrt();
|
||||
|
||||
let fresnel_coefficient = self.compute_fresnel_coefficient(&material, i, n);
|
||||
|
||||
// Calculate refraction direction
|
||||
let a = (-n).normalize() * cos_theta_t;
|
||||
let s_direction = cos_theta_i * n - i;
|
||||
let m_unit = s_direction.normalize();
|
||||
let b = m_unit * sin_theta_t;
|
||||
let t = a + b;
|
||||
|
||||
// Jitter a bit to reduce acne
|
||||
// TODO: Is this a good constant?
|
||||
let origin = intersection_context.point;
|
||||
let origin = origin + JITTER_CONST * t;
|
||||
|
||||
let ray = Ray::new(origin, t);
|
||||
if ray.has_nan() {
|
||||
warn!("WTF");
|
||||
}
|
||||
/* assert!(
|
||||
!ray.has_nan(),
|
||||
"ray is nan WTF {cos_theta_i} {sin_theta_i} ({}) {sin_theta_t} {cos_theta_t} | normal: {:?}",
|
||||
eta_i / eta_t,
|
||||
intersection_context.normal,
|
||||
); */
|
||||
let t_lambda = self.trace_single_ray(ray, depth + 1)?;
|
||||
|
||||
let value = (1.0 - fresnel_coefficient) * (1.0 - material.alpha) * t_lambda;
|
||||
debug!(
|
||||
depth,
|
||||
fresnel_coefficient,
|
||||
alpha = material.alpha,
|
||||
?t_lambda,
|
||||
?value,
|
||||
"computing final value"
|
||||
);
|
||||
|
||||
Ok(value)
|
||||
}
|
||||
}
|
||||
|
||||
/// Information about an intersection
|
||||
|
|
|
@ -61,12 +61,12 @@ impl Scene {
|
|||
macro_rules! r {
|
||||
($ty:ty) => {
|
||||
<$ty>::construct(&mut parts, ())
|
||||
.with_context(|| format!("Could not parse {}", stringify!($ty)))?
|
||||
.with_context(|| format!("Could not parse {} ({}:{})", stringify!($ty), file!(), line!()))?
|
||||
};
|
||||
|
||||
($ty:ty, $($ex:expr),* $(,)?) => {
|
||||
<$ty>::construct(&mut parts, $($ex,)*)
|
||||
.with_context(|| format!("Could not parse {}", stringify!($ty)))?
|
||||
.with_context(|| format!("Could not parse {} ({}:{})", stringify!($ty), file!(), line!()))?
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -327,7 +327,12 @@ macro_rules! impl_construct {
|
|||
{
|
||||
let item = match it.next() {
|
||||
Some(v) => v,
|
||||
None => bail!("Ran out of items"),
|
||||
None => bail!(
|
||||
"Ran out of items for {} ({}:{})",
|
||||
stringify!($ty),
|
||||
file!(),
|
||||
line!()
|
||||
),
|
||||
};
|
||||
|
||||
Ok(item.parse()?)
|
||||
|
|
|
@ -36,6 +36,10 @@ impl Sphere {
|
|||
|
||||
let discriminant = b * b - 4.0 * a * c;
|
||||
|
||||
if discriminant.is_nan() {
|
||||
warn!("WTF NAN");
|
||||
}
|
||||
|
||||
let time = match discriminant {
|
||||
// Discriminant < 0, means the equation has no solutions.
|
||||
d if d < 0.0 => None,
|
||||
|
@ -80,6 +84,11 @@ impl Sphere {
|
|||
dx.powi(2) + dy.powi(2) + dz.powi(2) < self.radius.powi(2)
|
||||
};
|
||||
|
||||
/* let normal = match exiting {
|
||||
true => -normal,
|
||||
false => normal,
|
||||
}; */
|
||||
|
||||
Ok(Some(IntersectionContext {
|
||||
time,
|
||||
point,
|
||||
|
|
|
@ -12,6 +12,9 @@ impl Scene {
|
|||
return Ok(Color::new(0.0, 0.0, 0.0));
|
||||
}
|
||||
|
||||
let span = trace_span!("trace_ray", ray = ?ray, depth = depth);
|
||||
let _enter = span.enter();
|
||||
|
||||
let intersections = self
|
||||
.objects
|
||||
.iter()
|
||||
|
|
|
@ -100,7 +100,7 @@ impl Triangle {
|
|||
time,
|
||||
point,
|
||||
normal,
|
||||
exiting: todo!(),
|
||||
exiting: false,
|
||||
}))
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue