got some transparency?

This commit is contained in:
Michael Zhang 2023-04-03 02:01:51 -05:00
parent d1d7b59081
commit 73bbd64cc0
Signed by: michael
GPG key ID: BDA47A31A3C8EE6B
11 changed files with 262 additions and 55 deletions

View file

@ -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]]

View file

@ -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"] }

View file

@ -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 $< $@

View 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

View 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

View file

@ -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<_>>>()?;

View file

@ -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

View file

@ -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()?)

View file

@ -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,

View file

@ -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()

View file

@ -100,7 +100,7 @@ impl Triangle {
time,
point,
normal,
exiting: todo!(),
exiting: false,
}))
}