Soft shadows
This commit is contained in:
parent
abe4aa6115
commit
b915006848
6 changed files with 193 additions and 17 deletions
61
assignment-1b/Cargo.lock
generated
61
assignment-1b/Cargo.lock
generated
|
@ -22,11 +22,13 @@ name = "assignment-1b"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
"base64",
|
||||||
"clap",
|
"clap",
|
||||||
"derivative",
|
"derivative",
|
||||||
"nalgebra",
|
"nalgebra",
|
||||||
"num",
|
"num",
|
||||||
"ordered-float",
|
"ordered-float",
|
||||||
|
"rand",
|
||||||
"rayon",
|
"rayon",
|
||||||
"tracing",
|
"tracing",
|
||||||
]
|
]
|
||||||
|
@ -37,6 +39,12 @@ version = "1.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "base64"
|
||||||
|
version = "0.21.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
version = "1.3.2"
|
version = "1.3.2"
|
||||||
|
@ -179,6 +187,17 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "getrandom"
|
||||||
|
version = "0.2.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"wasi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "heck"
|
name = "heck"
|
||||||
version = "0.4.1"
|
version = "0.4.1"
|
||||||
|
@ -395,6 +414,12 @@ version = "0.2.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116"
|
checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ppv-lite86"
|
||||||
|
version = "0.2.17"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro-error"
|
name = "proc-macro-error"
|
||||||
version = "1.0.4"
|
version = "1.0.4"
|
||||||
|
@ -437,6 +462,36 @@ dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand"
|
||||||
|
version = "0.8.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"rand_chacha",
|
||||||
|
"rand_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_chacha"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
||||||
|
dependencies = [
|
||||||
|
"ppv-lite86",
|
||||||
|
"rand_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_core"
|
||||||
|
version = "0.6.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rawpointer"
|
name = "rawpointer"
|
||||||
version = "0.2.1"
|
version = "0.2.1"
|
||||||
|
@ -589,6 +644,12 @@ version = "0.9.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasi"
|
||||||
|
version = "0.11.0+wasi-snapshot-preview1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wide"
|
name = "wide"
|
||||||
version = "0.7.5"
|
version = "0.7.5"
|
||||||
|
|
|
@ -14,10 +14,12 @@ path = "src/main.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0.68"
|
anyhow = "1.0.68"
|
||||||
|
base64 = "0.21.0"
|
||||||
clap = { version = "4.1.4", features = ["cargo", "derive"] }
|
clap = { version = "4.1.4", features = ["cargo", "derive"] }
|
||||||
derivative = "2.2.0"
|
derivative = "2.2.0"
|
||||||
nalgebra = "0.32.1"
|
nalgebra = "0.32.1"
|
||||||
num = { version = "0.4.0", features = ["serde"] }
|
num = { version = "0.4.0", features = ["serde"] }
|
||||||
ordered-float = "3.4.0"
|
ordered-float = "3.4.0"
|
||||||
|
rand = "0.8.5"
|
||||||
rayon = "1.6.1"
|
rayon = "1.6.1"
|
||||||
tracing = "0.1.37"
|
tracing = "0.1.37"
|
||||||
|
|
|
@ -19,12 +19,14 @@ EXAMPLES_PNG := $(patsubst %.txt,%.png,$(EXAMPLES))
|
||||||
all: $(HANDIN)
|
all: $(HANDIN)
|
||||||
|
|
||||||
$(BINARY): $(SOURCES)
|
$(BINARY): $(SOURCES)
|
||||||
|
mkdir -p target/docker
|
||||||
$(DOCKER) run \
|
$(DOCKER) run \
|
||||||
--rm \
|
--rm \
|
||||||
-v "$(shell pwd)":/usr/src/myapp \
|
-v "$(shell pwd)":/usr/src/myapp \
|
||||||
-v cargo-registry:/usr/local/cargo \
|
-v cargo-registry:/usr/local/cargo \
|
||||||
--user "$(shell id -u)":"$(shell id -g)" \
|
--user "$(shell id -u)":"$(shell id -g)" \
|
||||||
-w /usr/src/myapp \
|
-w /usr/src/myapp \
|
||||||
|
-e CARGO_TARGET_DIR=/usr/src/myapp/target/docker \
|
||||||
rust \
|
rust \
|
||||||
cargo build --release
|
cargo build --release
|
||||||
mv target/release/raytracer1b $@
|
mv target/release/raytracer1b $@
|
||||||
|
|
44
assignment-1b/examples/soft-shadow-demo.txt
Normal file
44
assignment-1b/examples/soft-shadow-demo.txt
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
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 40 20
|
||||||
|
|
||||||
|
light 10 10 -10 1 1 1 1
|
||||||
|
|
||||||
|
mtlcolor 0.5 1 0.5 1 1 1 0.2 0.4 0 10
|
||||||
|
sphere 5 5 -20 5
|
||||||
|
sphere -5 -5 -20 5
|
||||||
|
|
||||||
|
mtlcolor 1 0.5 0.5 1 1 1 0.2 0.4 0 10
|
||||||
|
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
|
|
@ -1,11 +1,16 @@
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::{ensure, Result};
|
||||||
use assignment_1b::image::Image;
|
use assignment_1b::image::Image;
|
||||||
use assignment_1b::ray::Ray;
|
use assignment_1b::ray::Ray;
|
||||||
use assignment_1b::scene::Scene;
|
use assignment_1b::scene::Scene;
|
||||||
|
use base64::{engine::general_purpose::STANDARD as BASE64, Engine};
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
|
use rand::{
|
||||||
|
rngs::{StdRng, ThreadRng},
|
||||||
|
thread_rng, Rng, RngCore, SeedableRng,
|
||||||
|
};
|
||||||
use rayon::prelude::{IntoParallelIterator, ParallelIterator};
|
use rayon::prelude::{IntoParallelIterator, ParallelIterator};
|
||||||
|
|
||||||
/// Simple raycaster.
|
/// Simple raycaster.
|
||||||
|
|
|
@ -1,14 +1,17 @@
|
||||||
use anyhow::Result;
|
use std::iter;
|
||||||
|
|
||||||
use nalgebra::Vector3;
|
use nalgebra::Vector3;
|
||||||
use ordered_float::NotNan;
|
use ordered_float::NotNan;
|
||||||
|
use rand::Rng;
|
||||||
use rayon::prelude::{
|
use rayon::prelude::{
|
||||||
IndexedParallelIterator, IntoParallelRefIterator, ParallelIterator,
|
IndexedParallelIterator, IntoParallelIterator, IntoParallelRefIterator,
|
||||||
|
ParallelIterator,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{image::Color, ray::Ray};
|
use crate::{image::Color, ray::Ray};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
data::{DepthCueing, Light, LightKind},
|
data::{DepthCueing, Light, LightKind, Object},
|
||||||
Scene,
|
Scene,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -59,14 +62,14 @@ impl Scene {
|
||||||
.max(0.0)
|
.max(0.0)
|
||||||
.powf(material.exponent);
|
.powf(material.exponent);
|
||||||
|
|
||||||
let shadow_flag =
|
let shadow_coefficient = self.compute_shadow_coefficient(
|
||||||
match self.in_shadow_of(obj_idx, intersection_context.point, light) {
|
obj_idx,
|
||||||
true => 0.0,
|
intersection_context.point,
|
||||||
false => 1.0,
|
light,
|
||||||
};
|
);
|
||||||
|
|
||||||
let diffuse_and_specular = diffuse_component + specular_component;
|
let diffuse_and_specular = diffuse_component + specular_component;
|
||||||
shadow_flag * light.color.component_mul(&diffuse_and_specular)
|
shadow_coefficient * light.color.component_mul(&diffuse_and_specular)
|
||||||
})
|
})
|
||||||
.sum();
|
.sum();
|
||||||
|
|
||||||
|
@ -103,12 +106,12 @@ impl Scene {
|
||||||
|
|
||||||
/// Perform another ray casting to see if there are any objects obstructing
|
/// Perform another ray casting to see if there are any objects obstructing
|
||||||
/// the light source to this particular point
|
/// the light source to this particular point
|
||||||
pub fn in_shadow_of(
|
pub fn compute_shadow_coefficient(
|
||||||
&self,
|
&self,
|
||||||
obj_idx: usize,
|
obj_idx: usize,
|
||||||
point: Vector3<f64>,
|
point: Vector3<f64>,
|
||||||
light: &Light,
|
light: &Light,
|
||||||
) -> bool {
|
) -> f64 {
|
||||||
let light_direction = light.direction_from(point);
|
let light_direction = light.direction_from(point);
|
||||||
let ray = Ray {
|
let ray = Ray {
|
||||||
origin: point,
|
origin: point,
|
||||||
|
@ -124,6 +127,7 @@ impl Scene {
|
||||||
.filter(|(i, _)| *i != obj_idx);
|
.filter(|(i, _)| *i != obj_idx);
|
||||||
|
|
||||||
// Get the list of intersections with all the other objects in the scene
|
// Get the list of intersections with all the other objects in the scene
|
||||||
|
// This list will be a set of opacities
|
||||||
let intersections = other_objects
|
let intersections = other_objects
|
||||||
.filter_map(|(_, object)| {
|
.filter_map(|(_, object)| {
|
||||||
let intersection_context = match object.kind.intersects_ray_at(&ray) {
|
let intersection_context = match object.kind.intersects_ray_at(&ray) {
|
||||||
|
@ -133,35 +137,93 @@ impl Scene {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}?;
|
}?;
|
||||||
|
let intersection_time = *intersection_context.time;
|
||||||
|
|
||||||
match light.kind {
|
match light.kind {
|
||||||
// In the case of point lights, we must check to see if both t > 0 and
|
// In the case of point lights, we must check to see if both t > 0 and
|
||||||
// t is less than the time it took to even get to the light.
|
// t is less than the time it took to even get to the light.
|
||||||
LightKind::Point { location } => {
|
LightKind::Point { location } => {
|
||||||
let light_time = (location - ray.origin).norm();
|
let light_time = (location - ray.origin).norm();
|
||||||
let intersection_time = *intersection_context.time;
|
|
||||||
|
|
||||||
if intersection_time <= 0.0 || intersection_time >= light_time {
|
if intersection_time <= 0.0 || intersection_time >= light_time {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
Some(intersection_context)
|
let soft_shadow_coefficient =
|
||||||
|
self.compute_soft_shadow_coefficient(location, point, object);
|
||||||
|
Some(soft_shadow_coefficient)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// In the case of directional lights, only t > 0 needs to be checked,
|
// In the case of directional lights, only t > 0 needs to be checked,
|
||||||
// otherwise
|
// otherwise
|
||||||
LightKind::Directional { .. } => {
|
LightKind::Directional { .. } => {
|
||||||
if *intersection_context.time <= 0.0 {
|
if intersection_time <= 0.0 {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
Some(intersection_context)
|
Some(0.0) // complete obstruction
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
!intersections.is_empty()
|
let average =
|
||||||
|
intersections.iter().cloned().sum::<f64>() / intersections.len() as f64;
|
||||||
|
|
||||||
|
match intersections.is_empty() {
|
||||||
|
true => 1.0,
|
||||||
|
false => average,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compute_soft_shadow_coefficient(
|
||||||
|
&self,
|
||||||
|
light_location: Vector3<f64>,
|
||||||
|
original_intersection_point: Vector3<f64>,
|
||||||
|
object: &Object,
|
||||||
|
) -> f64 {
|
||||||
|
// Soft shadows: jitter some rays here to somewhere close to the
|
||||||
|
// actual location as well, and measure the proportion
|
||||||
|
// of them that intersect any objects
|
||||||
|
const JITTER_RADIUS: f64 = 1.0;
|
||||||
|
const JITTER_RAYS: usize = 75;
|
||||||
|
|
||||||
|
let mut rng = rand::thread_rng();
|
||||||
|
let locations = iter::repeat_with(|| {
|
||||||
|
let x = rng.gen_range(0.0..JITTER_RADIUS);
|
||||||
|
let y = rng.gen_range(0.0..JITTER_RADIUS);
|
||||||
|
let z = rng.gen_range(0.0..JITTER_RADIUS);
|
||||||
|
let delta = Vector3::new(x, y, z);
|
||||||
|
light_location + delta
|
||||||
|
})
|
||||||
|
.take(JITTER_RAYS)
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
let num_obstructed_rays = locations
|
||||||
|
.into_par_iter()
|
||||||
|
.filter(|location| {
|
||||||
|
let direction = (location - original_intersection_point).normalize();
|
||||||
|
let ray = Ray {
|
||||||
|
origin: original_intersection_point,
|
||||||
|
direction,
|
||||||
|
};
|
||||||
|
|
||||||
|
let intersection_context = match object.kind.intersects_ray_at(&ray) {
|
||||||
|
Ok(Some(v)) => v,
|
||||||
|
Ok(None) => return false,
|
||||||
|
Err(err) => {
|
||||||
|
error!("Error while performing shadow casting: {err}");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let light_time = (location - ray.origin).norm();
|
||||||
|
let intersection_time = *intersection_context.time;
|
||||||
|
|
||||||
|
0.0 < intersection_time && intersection_time < light_time
|
||||||
|
})
|
||||||
|
.count();
|
||||||
|
|
||||||
|
(JITTER_RAYS - num_obstructed_rays) as f64 / JITTER_RAYS as f64
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue