Working renderer.

This commit is contained in:
Bill Thiede 2018-07-21 19:24:30 -07:00
parent 5a549957c4
commit f69704b7b1
3 changed files with 534 additions and 60 deletions

190
Cargo.lock generated
View File

@ -1,11 +1,36 @@
[[package]]
name = "ansi_term"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "aobench"
version = "0.1.0"
dependencies = [
"log 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)",
"stderrlog 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
"structopt 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "atty"
version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)",
"termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "bitflags"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "cfg-if"
version = "0.1.4"
@ -21,6 +46,42 @@ dependencies = [
"time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "clap"
version = "2.32.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
"atty 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)",
"bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
"strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"textwrap 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "cloudabi"
version = "0.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "fuchsia-zircon"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
"fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "fuchsia-zircon-sys"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "lazy_static"
version = "1.0.2"
@ -52,11 +113,52 @@ name = "num-traits"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "proc-macro2"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "quote"
version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand"
version = "0.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
"fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_core 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand_core"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "redox_syscall"
version = "0.1.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "redox_termios"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "stderrlog"
version = "0.4.1"
@ -68,6 +170,40 @@ dependencies = [
"thread_local 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "strsim"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "structopt"
version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)",
"structopt-derive 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "structopt-derive"
version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.14.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "syn"
version = "0.14.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "termcolor"
version = "0.3.6"
@ -76,6 +212,24 @@ dependencies = [
"wincolor 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "termion"
version = "1.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)",
"redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
"redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "textwrap"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "thread_local"
version = "0.3.5"
@ -95,6 +249,16 @@ dependencies = [
"winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "unicode-width"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "unicode-xid"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "unreachable"
version = "1.0.0"
@ -103,6 +267,11 @@ dependencies = [
"void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "vec_map"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "void"
version = "1.0.2"
@ -136,19 +305,40 @@ dependencies = [
]
[metadata]
"checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
"checksum atty 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "2fc4a1aa4c24c0718a250f0681885c1af91419d242f29eb8f2ab28502d80dbd1"
"checksum bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d0c54bb8f454c567f21197eefcdbf5679d0bd99f2ddbe52e84c77061952e6789"
"checksum cfg-if 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "efe5c877e17a9c717a0bf3613b2709f723202c4e4675cc8f12926ded29bcb17e"
"checksum chrono 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "6962c635d530328acc53ac6a955e83093fedc91c5809dfac1fa60fa470830a37"
"checksum clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b957d88f4b6a63b9d70d5f454ac8011819c6efa7727858f458ab71c756ce2d3e"
"checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
"checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82"
"checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
"checksum lazy_static 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "fb497c35d362b6a331cfd94956a07fc2c78a4604cdbee844a81170386b996dd3"
"checksum libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)" = "b685088df2b950fccadf07a7187c8ef846a959c142338a48f9dc0b94517eb5f1"
"checksum log 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "61bd98ae7f7b754bc53dca7d44b604f733c6bba044ea6f41bc8d89272d8161d2"
"checksum num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "e83d528d2677f0518c570baf2b7abdcf0cd2d248860b68507bdcb3e91d4c0cea"
"checksum num-traits 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "630de1ef5cc79d0cdd78b7e33b81f083cbfe90de0f4b2b2f07f905867c70e9fe"
"checksum proc-macro2 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)" = "cccdc7557a98fe98453030f077df7f3a042052fae465bb61d2c2c41435cfd9b6"
"checksum quote 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e44651a0dc4cdd99f71c83b561e221f714912d11af1a4dff0631f923d53af035"
"checksum rand 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)" = "12397506224b2f93e6664ffc4f664b29be8208e5157d3d90b44f09b5fae470ea"
"checksum rand_core 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "edecf0f94da5551fc9b492093e30b041a891657db7940ee221f9d2f66e82eef2"
"checksum redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "c214e91d3ecf43e9a4e41e578973adeb14b474f2bee858742d127af75a0112b1"
"checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76"
"checksum stderrlog 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "61dc66b7ae72b65636dbf36326f9638fb3ba27871bb737a62e2c309b87d91b70"
"checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550"
"checksum structopt 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "d8e9ad6a11096cbecdcca0cc6aa403fdfdbaeda2fb3323a39c98e6a166a1e45a"
"checksum structopt-derive 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4cbce8ccdc62166bd594c14396a3242bf94c337a51dbfa9be1076dd74b3db2af"
"checksum syn 0.14.4 (registry+https://github.com/rust-lang/crates.io-index)" = "2beff8ebc3658f07512a413866875adddd20f4fd47b2a4e6c9da65cd281baaea"
"checksum termcolor 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "adc4587ead41bf016f11af03e55a624c06568b5a19db4e90fde573d805074f83"
"checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096"
"checksum textwrap 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "307686869c93e71f94da64286f9a9524c0f308a9e1c87a583de8e9c9039ad3f6"
"checksum thread_local 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "279ef31c19ededf577bfd12dfae728040a21f635b06a24cd670ff510edd38963"
"checksum time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "d825be0eb33fda1a7e68012d51e9c7f451dc1a69391e7fdc197060bb8c56667b"
"checksum unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "882386231c45df4700b275c7ff55b6f3698780a650026380e72dabe76fa46526"
"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"
"checksum unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56"
"checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a"
"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
"checksum winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "773ef9dcc5f24b7d850d0ff101e542ff24c3b090a9768e03ff889fdef41f00fd"
"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"

View File

@ -6,3 +6,5 @@ authors = ["Bill Thiede <rust@xinu.tv>"]
[dependencies]
log = "0.4"
stderrlog = "0.4.1"
structopt = "0.2.10"
rand = "0.5"

View File

@ -1,16 +1,119 @@
#[macro_use]
extern crate log;
extern crate rand;
extern crate stderrlog;
#[macro_use]
extern crate structopt;
use rand::Rng;
use std::clone::Clone;
use std::f64;
use std::fmt;
use std::fs::File;
use std::io::Write;
use std::ops::Add;
use std::ops::Mul;
use std::ops::Sub;
use structopt::StructOpt;
fn vdot(v0: &Vector, v1: &Vector) -> f64 {
v0.x * v1.x + v0.y * v1.y + v0.z * v1.z
}
fn vcross(v0: &Vector, v1: &Vector) -> Vector {
Vector {
x: v0.y * v1.z - v0.z * v1.y,
y: v0.z * v1.x - v0.x * v1.z,
z: v0.x * v1.y - v0.y * v1.x,
}
}
#[derive(Debug, Copy, Clone)]
struct Vector {
x: f64,
y: f64,
z: f64,
}
impl Vector {
fn zero() -> Vector {
Vector {
x: 0.,
y: 0.,
z: 0.,
}
}
fn new(x: f64, y: f64, z: f64) -> Vector {
Vector { x, y, z }
}
fn normalize(&mut self) {
let length = vdot(self, self).sqrt();
if length.abs() < 1.0e-17 {
return;
}
self.x = self.x / length;
self.y = self.y / length;
self.z = self.z / length;
}
}
impl fmt::Display for Vector {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "({:.4}, {:.4}, {:.4})", self.x, self.y, self.z)
}
}
impl Add for Vector {
type Output = Vector;
fn add(self, other: Vector) -> Vector {
Vector {
x: self.x + other.x,
y: self.y + other.y,
z: self.z + other.z,
}
}
}
impl Mul for Vector {
type Output = Vector;
fn mul(self, other: Vector) -> Vector {
Vector {
x: self.x * other.x,
y: self.y * other.y,
z: self.z * other.z,
}
}
}
impl Mul<f64> for Vector {
type Output = Vector;
fn mul(self, other: f64) -> Vector {
Vector {
x: self.x * other,
y: self.y * other,
z: self.z * other,
}
}
}
impl Sub for Vector {
type Output = Vector;
fn sub(self, other: Vector) -> Vector {
Vector {
x: self.x - other.x,
y: self.y - other.y,
z: self.z - other.z,
}
}
}
#[derive(Copy, Clone)]
struct ISect {
t: f64,
p: Vector,
@ -28,102 +131,281 @@ struct Plane {
n: Vector,
}
#[derive(Debug)]
struct Ray {
org: Vector,
dir: Vector,
}
impl fmt::Display for Ray {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "o: {} d: {}", self.org, self.dir)
}
}
fn ray_sphere_intersect(mut isect: ISect, ray: &Ray, sphere: &Sphere) -> ISect {
let rs = ray.org - sphere.center;
let b = vdot(&rs, &ray.dir);
let c = vdot(&rs, &rs) - sphere.radius * sphere.radius;
let d = b * b - c;
if d > 0.0 {
let t = -b - d.sqrt();
if t > 0.0 && t < isect.t {
isect.t = t;
isect.hit = true;
isect.p = ray.org + ray.dir * t;
isect.n = isect.p - sphere.center;
isect.n.normalize();
}
}
isect
}
fn ray_plane_intersect(mut isect: ISect, ray: &Ray, plane: &Plane) -> ISect {
let d = -vdot(&plane.p, &plane.n);
let v = vdot(&ray.dir, &plane.n);
if v.abs() < 1.0e-17 {
return isect;
}
let t = -(vdot(&ray.org, &plane.n) + d) / v;
if (t > 0.0) && (t < isect.t) {
isect.t = t;
isect.hit = true;
isect.p = ray.org + ray.dir * t;
isect.n = plane.n;
}
isect
}
struct World {
spheres: Vec<Sphere>,
plane: Plane,
width: usize,
height: usize,
n_subsamples: u32,
n_aosamples: u32,
n_subsamples: usize,
n_aosamples: usize,
}
img: Vec<u8>,
fn ortho_basis(n: &Vector) -> [Vector; 3] {
let mut basis: [Vector; 3] = [Vector::zero(), Vector::zero(), n.clone()];
if (n.x < 0.6) && (n.x > -0.6) {
basis[1].x = 1.0;
} else if (n.y < 0.6) && (n.y > -0.6) {
basis[1].y = 1.0;
} else if (n.z < 0.6) && (n.z > -0.6) {
basis[1].z = 1.0;
} else {
basis[1].x = 1.0;
}
basis[0] = vcross(&basis[1], &basis[2]);
basis[0].normalize();
basis[1] = vcross(&basis[2], &basis[0]);
basis[1].normalize();
basis
}
impl World {
fn render(&mut self) {
info!(
"Rendering world {}x{} w/ {} subsampling and {} ambient occlusion sampling",
self.width, self.height, self.n_subsamples, self.n_aosamples
);
for y in 0..self.height {
for x in 0..self.width {
self.img[(x + y * self.width) * 3 + 0] = (x % 256) as u8;
self.img[(x + y * self.width) * 3 + 1] = (y % 256) as u8;
self.img[(x + y * self.width) * 3 + 2] = ((x * y) % 256) as u8;
fn ambient_occlusion(&self, isect: ISect) -> Vector {
let n_theta: usize = self.n_aosamples;
let n_phi: usize = self.n_aosamples;
let eps: f64 = 0.0001;
let p = isect.p + isect.n * eps;
let basis = ortho_basis(&isect.n);
let mut occlusion: f64 = 0.0;
let mut rng = rand::thread_rng();
for _ in 0..n_theta {
for _ in 0..n_phi {
let theta: f64 = rng.gen::<f64>().sqrt();
let phi: f64 = rng.gen::<f64>() * 2.0 * f64::consts::PI;
let x = phi.cos() * theta;
let y = phi.sin() * theta;
let z = (1.0 - theta * theta).sqrt();
let rx = x * basis[0].x + y * basis[1].x + z * basis[2].x;
let ry = x * basis[0].y + y * basis[1].y + z * basis[2].y;
let rz = x * basis[0].z + y * basis[1].z + z * basis[2].z;
let ray = Ray {
org: p.clone(),
dir: Vector::new(rx, ry, rz),
};
let mut occ_isect = ISect {
t: 1.0e+17,
hit: false,
p: Vector::zero(),
n: Vector::zero(),
};
occ_isect = ray_sphere_intersect(occ_isect, &ray, &self.spheres[0]);
occ_isect = ray_sphere_intersect(occ_isect, &ray, &self.spheres[1]);
occ_isect = ray_sphere_intersect(occ_isect, &ray, &self.spheres[2]);
occ_isect = ray_plane_intersect(occ_isect, &ray, &self.plane);
if occ_isect.hit {
occlusion += 1.0;
}
}
}
}
fn save(&self, path: &str) -> std::io::Result<()> {
info!("Saving scene to {}", path);
let mut buf = File::create(path)?;
writeln!(&buf, "P6\n{} {}\n255", self.width, self.height)?;
buf.write_all(&self.img)
let n_theta = n_theta as f64;
let n_phi = n_phi as f64;
occlusion = (n_theta * n_phi - occlusion) / (n_theta * n_phi);
Vector::new(occlusion, occlusion, occlusion)
}
}
fn init_scene() -> World {
let w: usize = 512;
let h: usize = 512;
fn clamp(f: f64) -> u8 {
let i = (f * 255.5) as i32;
if i < 0 {
return 0;
}
if i > 255 {
return 255;
}
i as u8
}
impl World {
fn render(&mut self) -> Vec<u8> {
let w = self.width;
let h = self.height;
info!(
"Rendering world {}x{} w/ {} subsampling and {} ambient occlusion sampling",
w, h, self.n_subsamples, self.n_aosamples
);
let mut img = vec![0; w * h * 3];
let mut fimg = vec![0.0; w * h * 3];
for y in 0..h as usize {
for x in 0..w as usize {
for v in 0..self.n_subsamples {
for u in 0..self.n_subsamples {
let fx = x as f64;
let fy = y as f64;
let fw = w as f64;
let fh = h as f64;
let fu = u as f64;
let fv = v as f64;
let n_subsamples = self.n_subsamples as f64;
let px = (fx + (fu / n_subsamples) - (fw / 2.0)) / (fw / 2.);
let py = -(fy + (fv / n_subsamples) - (fh / 2.0)) / (fh / 2.);
let mut dir = Vector::new(px, py, -1.0);
dir.normalize();
let ray = Ray {
org: Vector::zero(),
dir,
};
let mut isect = ISect {
t: 1.0e+17,
hit: false,
p: Vector::zero(),
n: Vector::zero(),
};
isect = ray_sphere_intersect(isect, &ray, &self.spheres[0]);
isect = ray_sphere_intersect(isect, &ray, &self.spheres[1]);
isect = ray_sphere_intersect(isect, &ray, &self.spheres[2]);
isect = ray_plane_intersect(isect, &ray, &self.plane);
if isect.hit {
let col = self.ambient_occlusion(isect);
fimg[(x + y * w) * 3 + 0] += col.x;
fimg[(x + y * w) * 3 + 1] += col.y;
fimg[(x + y * w) * 3 + 2] += col.z;
}
}
}
let n2 = (self.n_subsamples * self.n_subsamples) as f64;
fimg[3 * (y * w + x) + 0] /= n2;
fimg[3 * (y * w + x) + 1] /= n2;
fimg[3 * (y * w + x) + 2] /= n2;
img[3 * (y * w + x) + 0] = clamp(fimg[3 * (y * w + x) + 0]);
img[3 * (y * w + x) + 1] = clamp(fimg[3 * (y * w + x) + 1]);
img[3 * (y * w + x) + 2] = clamp(fimg[3 * (y * w + x) + 2]);
}
}
img
}
}
fn save(width: usize, height: usize, img: &[u8], path: &str) -> std::io::Result<()> {
info!("Saving scene to {}", path);
let mut buf = File::create(path)?;
writeln!(&buf, "P6\n{} {}\n255", width, height)?;
buf.write_all(img)
}
fn init_scene(width: usize, height: usize, n_subsamples: usize, n_aosamples: usize) -> World {
World {
spheres: vec![
Sphere {
center: Vector {
x: -2.0,
y: 0.0,
z: -3.5,
},
center: Vector::new(-2.0, 0.0, -3.5),
radius: 0.5,
},
Sphere {
center: Vector {
x: -0.5,
y: 0.0,
z: -3.0,
},
center: Vector::new(-0.5, 0.0, -3.0),
radius: 0.5,
},
Sphere {
center: Vector {
x: 1.0,
y: 0.0,
z: -2.2,
},
center: Vector::new(1.0, 0.0, -2.2),
radius: 0.5,
},
],
plane: Plane {
p: Vector {
x: 0.0,
y: -0.5,
z: 0.0,
},
n: Vector {
x: 0.0,
y: 1.0,
z: 0.0,
},
p: Vector::new(0.0, -0.5, 0.0),
n: Vector::new(0.0, 1.0, 0.0),
},
width: w,
height: h,
n_subsamples: 2,
n_aosamples: 8,
img: vec![0; w * h * 3],
width,
height,
n_subsamples,
n_aosamples,
}
}
fn main() -> std::io::Result<()> {
stderrlog::new().verbosity(3).init().unwrap();
let mut w = init_scene();
w.render();
w.save("ao.ppm")
#[derive(StructOpt, Debug)]
#[structopt()]
struct Opt {
/// Verbose mode (-v, -vv, -vvv, etc)
#[structopt(short = "v", long = "verbose", parse(from_occurrences), default_value = "vvv")]
verbose: usize,
#[structopt(short = "w", long = "width", default_value = "512")]
width: usize,
#[structopt(short = "h", long = "height", default_value = "512")]
height: usize,
#[structopt(short = "s", long = "subsamples", default_value = "2")]
n_subsamples: usize,
#[structopt(short = "a", long = "aosamples", default_value = "8")]
n_aosamples: usize,
}
fn main() -> std::io::Result<()> {
let opt = Opt::from_args();
stderrlog::new()
.verbosity(opt.verbose)
.timestamp(stderrlog::Timestamp::Millisecond)
.init()
.unwrap();
let mut w = init_scene(opt.width, opt.height, opt.n_subsamples, opt.n_aosamples);
let img = w.render();
save(opt.width, opt.height, &img, "ao.ppm")
}