aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJulian T <julian@jtle.dk>2021-02-03 17:29:27 +0100
committerJulian T <julian@jtle.dk>2021-02-03 17:29:27 +0100
commit977b0e4152433b2a68e2b97fe5fe2c0ff6fb20d8 (patch)
tree9c1eccef2ede2507e7d29a0daf44d56c76036415
parent8296be848319eecd43f94900d4d12414ec189166 (diff)
Can render a simple sphere, without shading
-rw-r--r--Cargo.lock332
-rw-r--r--Cargo.toml1
-rw-r--r--src/camera/camera.rs9
-rw-r--r--src/camera/film.rs40
-rw-r--r--src/camera/mod.rs1
-rw-r--r--src/core/spectrum.rs6
-rw-r--r--src/core/vector2.rs7
-rw-r--r--src/lib.rs3
-rw-r--r--src/main.rs38
-rw-r--r--src/render.rs51
10 files changed, 465 insertions, 23 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 169dc89..a6626c8 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1,5 +1,337 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
[[package]]
+name = "adler"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ee2a4ec343196209d6594e19543ae87a39f96d5534d7174822a3ad825dd6ed7e"
+
+[[package]]
+name = "adler32"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234"
+
+[[package]]
+name = "autocfg"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
+
+[[package]]
+name = "bitflags"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
+
+[[package]]
+name = "bytemuck"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a4bad0c5981acc24bc09e532f35160f952e35422603f0563cd7a73c2c2e65a0"
+
+[[package]]
+name = "byteorder"
+version = "1.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ae44d1a3d5a19df61dd0c8beb138458ac2a53a7ac09eba97d55592540004306b"
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "color_quant"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
+
+[[package]]
+name = "const_fn"
+version = "0.4.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "28b9d6de7f49e22cf97ad17fc4036ece69300032f45f78f30b4a4482cdc3f4a6"
+
+[[package]]
+name = "crc32fast"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "crossbeam-channel"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dca26ee1f8d361640700bde38b2c37d8c22b3ce2d360e1fc1c74ea4b0aa7d775"
+dependencies = [
+ "cfg-if",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-deque"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "94af6efb46fef72616855b036a624cf27ba656ffc9be1b9a3c931cfc7749a9a9"
+dependencies = [
+ "cfg-if",
+ "crossbeam-epoch",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-epoch"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1aaa739f95311c2c7887a76863f500026092fb1dce0161dab577e559ef3569d"
+dependencies = [
+ "cfg-if",
+ "const_fn",
+ "crossbeam-utils",
+ "lazy_static",
+ "memoffset",
+ "scopeguard",
+]
+
+[[package]]
+name = "crossbeam-utils"
+version = "0.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "02d96d1e189ef58269ebe5b97953da3274d83a93af647c2ddd6f9dab28cedb8d"
+dependencies = [
+ "autocfg",
+ "cfg-if",
+ "lazy_static",
+]
+
+[[package]]
+name = "deflate"
+version = "0.8.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "73770f8e1fe7d64df17ca66ad28994a0a623ea497fa69486e14984e715c5d174"
+dependencies = [
+ "adler32",
+ "byteorder",
+]
+
+[[package]]
+name = "either"
+version = "1.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
+
+[[package]]
+name = "gif"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "02efba560f227847cb41463a7395c514d127d4f74fff12ef0137fff1b84b96c4"
+dependencies = [
+ "color_quant",
+ "weezl",
+]
+
+[[package]]
+name = "hermit-abi"
+version = "0.1.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "image"
+version = "0.23.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7ce04077ead78e39ae8610ad26216aed811996b043d47beed5090db674f9e9b5"
+dependencies = [
+ "bytemuck",
+ "byteorder",
+ "color_quant",
+ "gif",
+ "jpeg-decoder",
+ "num-iter",
+ "num-rational",
+ "num-traits",
+ "png",
+ "scoped_threadpool",
+ "tiff",
+]
+
+[[package]]
+name = "jpeg-decoder"
+version = "0.1.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "229d53d58899083193af11e15917b5640cd40b29ff475a1fe4ef725deb02d0f2"
+dependencies = [
+ "rayon",
+]
+
+[[package]]
+name = "lazy_static"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
+
+[[package]]
+name = "libc"
+version = "0.2.85"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7ccac4b00700875e6a07c6cde370d44d32fa01c5a65cdd2fca6858c479d28bb3"
+
+[[package]]
+name = "memoffset"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "157b4208e3059a8f9e78d559edc658e13df41410cb3ae03979c83130067fdd87"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "miniz_oxide"
+version = "0.3.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "791daaae1ed6889560f8c4359194f56648355540573244a5448a83ba1ecc7435"
+dependencies = [
+ "adler32",
+]
+
+[[package]]
+name = "miniz_oxide"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0f2d26ec3309788e423cfbf68ad1800f061638098d76a83681af979dc4eda19d"
+dependencies = [
+ "adler",
+ "autocfg",
+]
+
+[[package]]
+name = "num-integer"
+version = "0.1.44"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db"
+dependencies = [
+ "autocfg",
+ "num-traits",
+]
+
+[[package]]
+name = "num-iter"
+version = "0.1.42"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b2021c8337a54d21aca0d59a92577a029af9431cb59b909b03252b9c164fad59"
+dependencies = [
+ "autocfg",
+ "num-integer",
+ "num-traits",
+]
+
+[[package]]
+name = "num-rational"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "12ac428b1cb17fce6f731001d307d351ec70a6d202fc2e60f7d4c5e42d8f4f07"
+dependencies = [
+ "autocfg",
+ "num-integer",
+ "num-traits",
+]
+
+[[package]]
+name = "num-traits"
+version = "0.2.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "num_cpus"
+version = "1.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3"
+dependencies = [
+ "hermit-abi",
+ "libc",
+]
+
+[[package]]
name = "pathtrace"
version = "0.1.0"
+dependencies = [
+ "image",
+]
+
+[[package]]
+name = "png"
+version = "0.16.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3c3287920cb847dee3de33d301c463fba14dda99db24214ddf93f83d3021f4c6"
+dependencies = [
+ "bitflags",
+ "crc32fast",
+ "deflate",
+ "miniz_oxide 0.3.7",
+]
+
+[[package]]
+name = "rayon"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b0d8e0819fadc20c74ea8373106ead0600e3a67ef1fe8da56e39b9ae7275674"
+dependencies = [
+ "autocfg",
+ "crossbeam-deque",
+ "either",
+ "rayon-core",
+]
+
+[[package]]
+name = "rayon-core"
+version = "1.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9ab346ac5921dc62ffa9f89b7a773907511cdfa5490c572ae9be1be33e8afa4a"
+dependencies = [
+ "crossbeam-channel",
+ "crossbeam-deque",
+ "crossbeam-utils",
+ "lazy_static",
+ "num_cpus",
+]
+
+[[package]]
+name = "scoped_threadpool"
+version = "0.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1d51f5df5af43ab3f1360b429fa5e0152ac5ce8c0bd6485cae490332e96846a8"
+
+[[package]]
+name = "scopeguard"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
+
+[[package]]
+name = "tiff"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a53f4706d65497df0c4349241deddf35f84cee19c87ed86ea8ca590f4464437"
+dependencies = [
+ "jpeg-decoder",
+ "miniz_oxide 0.4.3",
+ "weezl",
+]
+
+[[package]]
+name = "weezl"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4a32b378380f4e9869b22f0b5177c68a5519f03b3454fde0b291455ddbae266c"
diff --git a/Cargo.toml b/Cargo.toml
index 621e7e3..63e31c6 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -7,3 +7,4 @@ edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
+image = "0.23.12"
diff --git a/src/camera/camera.rs b/src/camera/camera.rs
index 69fd4e0..7822e92 100644
--- a/src/camera/camera.rs
+++ b/src/camera/camera.rs
@@ -7,13 +7,13 @@
//!
//! ```
//! use pathtrace::camera::Camera;
-//! use pathtrace::core::{Vector3f, Vector2f};
+//! use pathtrace::core::{Vector3f, Vector2f, Vector2i};
//!
//! let cam = Camera::new(
//! Vector3f::new(10.0),
//! Vector3f::new(0.0),
//! Vector3f::new_xyz(0.0, 1.0, 0.0),
-//! 90.0, Vector2f::new(10.0),
+//! 90.0, Vector2i::new(10.0),
//! );
//!
//! let (r, _) = cam.generate_ray(&Vector2f::new(5.0));
@@ -27,7 +27,7 @@
//!
//! ```
use crate::Float;
-use crate::core::{Vector3f, Vector2f, Ray};
+use crate::core::{Vector3f, Vector2f, Vector2i, Ray};
/// A simple perspective camera
pub struct Camera {
@@ -45,7 +45,8 @@ impl Camera {
///
/// The field of view specifies how wide the image should be.
/// Currently must be between [0; 180[.
- pub fn new(origin: Vector3f, target: Vector3f, up: Vector3f, fov: Float, screensize: Vector2f) -> Camera {
+ pub fn new(origin: Vector3f, target: Vector3f, up: Vector3f, fov: Float, screensize: Vector2i) -> Camera {
+ let screensize = Vector2f::from(screensize);
// Calculate translation vectors
let forward = (target - origin).norm();
let right = up.cross(&origin).norm();
diff --git a/src/camera/film.rs b/src/camera/film.rs
index 30fd2fe..3586374 100644
--- a/src/camera/film.rs
+++ b/src/camera/film.rs
@@ -1,5 +1,6 @@
use crate::core::*;
use crate::Float;
+use image::{RgbImage, Rgb};
/// Contains the necesary values when doing calculations
///
@@ -14,7 +15,7 @@ pub struct Pixel {
pub struct Film {
size: Vector2i,
- drawing_bound: Bound2i,
+ pub frame: Bound2i,
pixels: Vec<Pixel>,
}
@@ -24,14 +25,12 @@ pub struct Film {
/// This means that multiple threads can work on the same area and commit their changed when they
/// are done.
pub struct FilmTile {
- bounds: Bound2i,
- size: Vector2i,
+ pub bounds: Bound2i,
+ pub size: Vector2i,
pixels: Vec<Pixel>,
}
-//const HalfPixel = Vector2f::new(0.5);
-
impl Pixel {
fn new() -> Pixel {
Pixel {
@@ -44,6 +43,15 @@ impl Pixel {
self.rgb += &(c * weight);
self.samples += 1;
}
+
+ fn finalize_rgb(&self) -> [u8; 3] {
+ let (r, g, b) = self.rgb.to_rgb(255.0);
+ [
+ r as u8,
+ g as u8,
+ b as u8,
+ ]
+ }
}
impl std::ops::AddAssign<&Self> for Pixel {
@@ -58,7 +66,7 @@ impl Film {
let area = size.x * size.y;
Film {
size,
- drawing_bound: Bound2i::new(&Vector2i::new(0), &size),
+ frame: Bound2i::new(&Vector2i::new(0), &size),
pixels: vec![Pixel::new(); area as usize],
}
}
@@ -79,18 +87,34 @@ impl Film {
pub fn commit_tile(&mut self, tile: &FilmTile) {
let offset = tile.bounds.min;
- for y in 0 ..= tile.size.y {
+ for y in 0 .. tile.size.y {
let rowindex = (offset.y + y) * self.size.x;
let prowindex = y * tile.size.x;
- for x in 0 ..= tile.size.x {
+ for x in 0 .. tile.size.x {
let index = offset.x + x + rowindex;
let pindex: i32 = x + prowindex;
self.pixels[index as usize] += &tile.pixels[pindex as usize];
}
}
+ }
+
+ pub fn finalize_image(&self) -> RgbImage {
+ let mut img = RgbImage::new(self.size.x as u32, self.size.y as u32);
+
+ for y in 0..self.size.y {
+ let index = y * self.size.x;
+ for x in 0..self.size.x {
+ img.put_pixel(
+ x as u32,
+ y as u32,
+ Rgb(self.pixels[(index + x) as usize].finalize_rgb()),
+ );
+ }
+ }
+ img
}
}
diff --git a/src/camera/mod.rs b/src/camera/mod.rs
index b7c982b..1391cea 100644
--- a/src/camera/mod.rs
+++ b/src/camera/mod.rs
@@ -10,3 +10,4 @@ pub mod film;
pub mod camera;
pub use camera::Camera;
+pub use film::Film;
diff --git a/src/core/spectrum.rs b/src/core/spectrum.rs
index c72a251..fb82a9e 100644
--- a/src/core/spectrum.rs
+++ b/src/core/spectrum.rs
@@ -12,9 +12,13 @@ pub struct Spectrum {
}
impl Spectrum {
- fn new_rgb(r: Float, g: Float, b: Float) -> Spectrum {
+ pub fn new_rgb(r: Float, g: Float, b: Float) -> Spectrum {
Spectrum { c: [r, g, b] }
}
+
+ pub fn to_rgb(&self, scale: Float) -> (Float, Float, Float) {
+ (self.c[0] * scale, self.c[1] * scale, self.c[2] * scale)
+ }
}
impl std::ops::Mul<Float> for &Spectrum {
diff --git a/src/core/vector2.rs b/src/core/vector2.rs
index 5afa0f2..3aadb46 100644
--- a/src/core/vector2.rs
+++ b/src/core/vector2.rs
@@ -3,6 +3,7 @@
//! This is implemented generictly with types that fit in the Number trait
use crate::{Float, Number};
use std::ops::{Sub, Add};
+use std::fmt;
#[derive(Clone, Copy)]
pub struct Vector2<T: Number> {
@@ -43,6 +44,12 @@ impl<T: Number> Add for Vector2<T> {
}
}
+impl<T: Number> fmt::Display for Vector2<T> {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ f.write_fmt(format_args!("[{}, {}]", self.x, self.y))
+ }
+}
+
impl Vector2f {
pub fn ceil(&self) -> Self {
Self::new_xy(
diff --git a/src/lib.rs b/src/lib.rs
index c3025b3..50d0435 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,6 +1,7 @@
pub mod core;
pub mod camera;
-mod scene;
+pub mod render;
+pub mod scene;
use std::ops::{Add, Sub, Mul, DivAssign};
use std::cmp;
diff --git a/src/main.rs b/src/main.rs
index 22998dd..05d6617 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,17 +1,37 @@
-use pathtrace::camera::Camera;
-use pathtrace::core::{Vector3f, Vector2f};
-
+use pathtrace::camera::{Camera, Film};
+use pathtrace::scene::Scene;
+use pathtrace::scene::shapes::Sphere;
+use pathtrace::core::{Vector2i, Vector3f};
+use pathtrace::render::{RenderContext, RenderTask};
fn main() {
+ let res = Vector2i::new_xy(500, 500);
let cam = Camera::new(
- Vector3f::new(10.0),
- Vector3f::new(0.0),
- Vector3f::new_xyz(0.0, 1.0, 0.0),
- 90.0, Vector2f::new(10.0),
+ Vector3f::new_xyz(10.0, 0.0, 0.0),
+ Vector3f::new(0.0),
+ Vector3f::new_xyz(0.0, 0.1, 0.0),
+ 90.0, res,
+ );
+
+ let mut scn = Scene::new();
+ scn.add_shape(
+ Box::new(Sphere::new(4.0, Vector3f::new(0.0))),
);
- let (r, _) = cam.generate_ray(Vector2f::new(5.0));
+ let ctx = RenderContext { cam: &cam, scn: &scn };
+
+ let mut film = Film::new(res);
+ let tile = film.get_tile(&film.frame);
+
+ let mut task = RenderTask::new(Box::new(tile), 1);
+ task.render(&ctx);
+
+ film.commit_tile(&task.tile);
+
+ let image = film.finalize_image();
+ if let Err(e) = image.save("test.png") {
+ println!("Failed to save {}", e);
+ }
- println!("r: {}, norm: {}", r.direction, r.direction.norm());
}
diff --git a/src/render.rs b/src/render.rs
new file mode 100644
index 0000000..dcda672
--- /dev/null
+++ b/src/render.rs
@@ -0,0 +1,51 @@
+//! Implements the main render loop
+//!
+//! This is not a final design
+use crate::camera::film::FilmTile;
+use crate::camera::Camera;
+use crate::scene::Scene;
+
+use crate::core::{Vector2f, Spectrum};
+use crate::Float;
+
+const HALF_PIXEL: Vector2f = Vector2f {x: 0.5, y: 0.5 };
+
+pub struct RenderTask {
+ pub tile: Box<FilmTile>,
+ samples: u32,
+}
+
+pub struct RenderContext<'a> {
+ pub scn: &'a Scene,
+ pub cam: &'a Camera,
+}
+
+impl RenderTask {
+ pub fn new(tile: Box<FilmTile>, samples: u32) -> Self {
+ Self { tile, samples }
+ }
+
+ fn render_at(&self, ctx: &RenderContext, x: i32, y: i32) -> Spectrum {
+ // Create a ray
+ let (r, _) = ctx.cam.generate_ray(&Vector2f::new_xy(x as Float, y as Float));
+
+ // Trace ray
+ if let Some(_) = ctx.scn.intersect(r) {
+ return Spectrum::new_rgb(0.5, 0.5, 0.0);
+ }
+
+ Spectrum::new_rgb(0.0, 0.0, 0.0)
+
+ }
+
+ pub fn render(&mut self, ctx: &RenderContext) {
+ let b = self.tile.bounds.clone();
+ for x in b.min.x .. b.max.x {
+ for y in b.min.y .. b.max.y {
+ let p = Vector2f::new_xy(x as Float, y as Float);
+
+ self.tile.add_sample(&p, self.render_at(ctx, x, y))
+ }
+ }
+ }
+}