reorganize core systems
no change in functionality, but preparing for stuff in the near future like more accurately modeling film sensors, filtering, color science, etc.
This commit is contained in:
parent
a30b29a130
commit
094f4947dc
9 changed files with 210 additions and 56 deletions
7
Cargo.lock
generated
7
Cargo.lock
generated
|
@ -107,6 +107,7 @@ dependencies = [
|
||||||
"enum_dispatch",
|
"enum_dispatch",
|
||||||
"fasthash",
|
"fasthash",
|
||||||
"glam",
|
"glam",
|
||||||
|
"range2d",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -257,6 +258,12 @@ version = "0.4.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc"
|
checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "range2d"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7169c4e8549f72214a10fe6a64e24bf5e2d75ca9378d0ac7934afb9acbdfc313"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rdrand"
|
name = "rdrand"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
|
|
|
@ -11,6 +11,7 @@ bytemuck = { version = "1.22.0", optional = true }
|
||||||
enum_dispatch = "0.3.13"
|
enum_dispatch = "0.3.13"
|
||||||
fasthash = "0.4.0"
|
fasthash = "0.4.0"
|
||||||
glam = "0.30.2"
|
glam = "0.30.2"
|
||||||
|
range2d = "0.2.0"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
fast-math = ["glam/fast-math"]
|
fast-math = ["glam/fast-math"]
|
||||||
|
|
|
@ -1,4 +1,8 @@
|
||||||
use crate::geometry::{Point, Ray, Transform, Vector};
|
use crate::{
|
||||||
|
color::Rgba,
|
||||||
|
geometry::{Point, Point2, Ray, Transform, Transform2, Vector, point2, ray, vec3},
|
||||||
|
sampling::PixelSample,
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Camera {
|
pub struct Camera {
|
||||||
|
@ -16,9 +20,52 @@ impl Camera {
|
||||||
Self { transform }
|
Self { transform }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ray(&self, x: f32, y: f32) -> Ray {
|
pub fn ray(&self, p_film: Point2) -> Ray {
|
||||||
let origin = self.transform * Point::ORIGIN;
|
ray(
|
||||||
let direction = self.transform * Vector::new(x, y, 1.0);
|
self.transform * Point::ORIGIN,
|
||||||
Ray::new(origin, direction)
|
self.transform * vec3(p_film.x, p_film.y, 1.0),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Film {
|
||||||
|
width: u32,
|
||||||
|
height: u32,
|
||||||
|
transform: Transform2,
|
||||||
|
data: Box<[Rgba]>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Film {
|
||||||
|
pub fn new(width: u32, height: u32) -> Self {
|
||||||
|
let size = width as usize * height as usize;
|
||||||
|
|
||||||
|
let data = vec![Rgba::BLACK; size].into_boxed_slice();
|
||||||
|
|
||||||
|
let w = width as f32;
|
||||||
|
let h = height as f32;
|
||||||
|
let ar = h / w;
|
||||||
|
|
||||||
|
let transform = Transform2::translate(-1.0, ar) * Transform2::scale(2.0 / w, -2.0 / w);
|
||||||
|
|
||||||
|
Self {
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
transform,
|
||||||
|
data,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_sample(&mut self, x: u32, y: u32, color: Rgba, weight: f32) {
|
||||||
|
let i = y as usize * self.width as usize + x as usize;
|
||||||
|
self.data[i] += color * weight;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_camera_sample(&self, x: u32, y: u32, sampler: &mut PixelSample) -> Point2 {
|
||||||
|
let p = point2(x as f32, y as f32) + sampler.get_2d().into();
|
||||||
|
self.transform * p
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn into_data(self) -> Box<[Rgba]> {
|
||||||
|
self.data
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
use std::fmt;
|
use std::{
|
||||||
|
fmt,
|
||||||
|
ops::{Add, AddAssign, Mul},
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
|
@ -9,12 +12,12 @@ pub struct Rgba {
|
||||||
a: f32,
|
a: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Rgba {
|
pub const fn rgba(r: f32, g: f32, b: f32, a: f32) -> Rgba {
|
||||||
pub const BLACK: Self = Self::new(0.0, 0.0, 0.0, 1.0);
|
Rgba { r, g, b, a }
|
||||||
|
}
|
||||||
|
|
||||||
pub const fn new(r: f32, g: f32, b: f32, a: f32) -> Self {
|
impl Rgba {
|
||||||
Self { r, g, b, a }
|
pub const BLACK: Self = rgba(0.0, 0.0, 0.0, 1.0);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for Rgba {
|
impl fmt::Debug for Rgba {
|
||||||
|
@ -27,3 +30,46 @@ impl fmt::Debug for Rgba {
|
||||||
.finish()
|
.finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Add for Rgba {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn add(self, Self { r, g, b, a }: Self) -> Self::Output {
|
||||||
|
Self {
|
||||||
|
r: self.r + r,
|
||||||
|
g: self.g + g,
|
||||||
|
b: self.b + b,
|
||||||
|
a: self.a + a,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AddAssign for Rgba {
|
||||||
|
fn add_assign(&mut self, Self { r, g, b, a }: Self) {
|
||||||
|
self.r += r;
|
||||||
|
self.g += g;
|
||||||
|
self.b += b;
|
||||||
|
self.a += a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mul<f32> for Rgba {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn mul(self, rhs: f32) -> Self::Output {
|
||||||
|
Self {
|
||||||
|
r: self.r * rhs,
|
||||||
|
g: self.g * rhs,
|
||||||
|
b: self.b * rhs,
|
||||||
|
a: self.a * rhs,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mul<Rgba> for f32 {
|
||||||
|
type Output = Rgba;
|
||||||
|
|
||||||
|
fn mul(self, rhs: Rgba) -> Self::Output {
|
||||||
|
rhs * self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ mod shapes;
|
||||||
mod transform;
|
mod transform;
|
||||||
|
|
||||||
pub use _2d::*;
|
pub use _2d::*;
|
||||||
pub use shapes::Shape;
|
pub use shapes::{Hit, Hittable, Shape};
|
||||||
pub use transform::Transform;
|
pub use transform::Transform;
|
||||||
|
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use std::{
|
use std::{
|
||||||
fmt,
|
fmt,
|
||||||
ops::{Add, AddAssign, Div, DivAssign, Mul, Sub},
|
ops::{Add, AddAssign, Deref, Div, DivAssign, Mul, Sub},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
|
@ -88,6 +88,12 @@ pub const fn point2(x: f32, y: f32) -> Point2 {
|
||||||
Point2(glam::vec2(x, y))
|
Point2(glam::vec2(x, y))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<(f32, f32)> for Point2 {
|
||||||
|
fn from((x, y): (f32, f32)) -> Self {
|
||||||
|
point2(x, y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl fmt::Debug for Point2 {
|
impl fmt::Debug for Point2 {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
f.debug_tuple(stringify!(Point))
|
f.debug_tuple(stringify!(Point))
|
||||||
|
@ -97,9 +103,12 @@ impl fmt::Debug for Point2 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<(f32, f32)> for Point2 {
|
impl Deref for Point2 {
|
||||||
fn from((x, y): (f32, f32)) -> Self {
|
type Target = glam::Vec2;
|
||||||
point2(x, y)
|
|
||||||
|
#[inline]
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -126,3 +135,54 @@ impl Sub for Point2 {
|
||||||
Vector2(self.0 - rhs.0)
|
Vector2(self.0 - rhs.0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
#[repr(transparent)]
|
||||||
|
pub struct Transform2(glam::Affine2);
|
||||||
|
|
||||||
|
impl fmt::Debug for Transform2 {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
f.debug_struct(stringify!(Transform))
|
||||||
|
.field(stringify!(matrix2), &self.0.matrix2)
|
||||||
|
.field(stringify!(translation), &self.0.translation)
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Transform2 {
|
||||||
|
pub fn scale(x: f32, y: f32) -> Self {
|
||||||
|
Self(glam::Affine2::from_scale(glam::vec2(x, y)))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn inverse(self) -> Self {
|
||||||
|
Self(self.0.inverse())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn translate(x: f32, y: f32) -> Self {
|
||||||
|
Self(glam::Affine2::from_translation(glam::vec2(x, y)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mul for Transform2 {
|
||||||
|
type Output = Transform2;
|
||||||
|
|
||||||
|
fn mul(self, rhs: Self) -> Self::Output {
|
||||||
|
Self(self.0 * rhs.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mul<Vector2> for Transform2 {
|
||||||
|
type Output = Vector2;
|
||||||
|
|
||||||
|
fn mul(self, rhs: Vector2) -> Self::Output {
|
||||||
|
Vector2(self.0.transform_vector2(rhs.0))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mul<Point2> for Transform2 {
|
||||||
|
type Output = Point2;
|
||||||
|
|
||||||
|
fn mul(self, rhs: Point2) -> Self::Output {
|
||||||
|
Point2(self.0.transform_point2(rhs.0))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -5,14 +5,14 @@ use enum_dispatch::enum_dispatch;
|
||||||
use super::{Point, Ray, Vector};
|
use super::{Point, Ray, Vector};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) struct Hit {
|
pub struct Hit {
|
||||||
pub point: Point,
|
pub point: Point,
|
||||||
pub normal: Vector,
|
pub normal: Vector,
|
||||||
pub t: f32,
|
pub t: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[enum_dispatch]
|
#[enum_dispatch]
|
||||||
pub(crate) trait Hittable {
|
pub trait Hittable {
|
||||||
fn intersect(&self, ray: &Ray, t_max: f32) -> Option<Hit>;
|
fn intersect(&self, ray: &Ray, t_max: f32) -> Option<Hit>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,8 @@
|
||||||
|
use core::f32;
|
||||||
|
|
||||||
|
use geometry::{Hit, Hittable, Ray, point2, vec3};
|
||||||
|
use range2d::Range2D;
|
||||||
|
|
||||||
#[cfg(feature = "bytemuck")]
|
#[cfg(feature = "bytemuck")]
|
||||||
pub mod impl_bytemuck;
|
pub mod impl_bytemuck;
|
||||||
|
|
||||||
|
@ -7,70 +12,57 @@ mod camera;
|
||||||
mod geometry;
|
mod geometry;
|
||||||
mod sampling;
|
mod sampling;
|
||||||
|
|
||||||
use core::f32;
|
|
||||||
|
|
||||||
pub use camera::Camera;
|
pub use camera::Camera;
|
||||||
pub use geometry::{Point, Transform, Vector, shapes::Shape};
|
pub use geometry::{Point, Shape, Transform, Vector};
|
||||||
|
|
||||||
use color::Rgba;
|
use camera::Film;
|
||||||
use geometry::{
|
use color::{Rgba, rgba};
|
||||||
Ray,
|
|
||||||
shapes::{Hit, Hittable},
|
|
||||||
};
|
|
||||||
use sampling::Sampler;
|
use sampling::Sampler;
|
||||||
|
|
||||||
pub struct Scene {
|
pub struct Scene {
|
||||||
camera: Camera,
|
camera: Camera,
|
||||||
contents: Vec<Shape>,
|
objects: Vec<Shape>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Scene {
|
impl Scene {
|
||||||
pub fn new(camera: Camera, contents: Vec<Shape>) -> Self {
|
pub fn new(camera: Camera, objects: Vec<Shape>) -> Self {
|
||||||
Self { camera, contents }
|
Self { camera, objects }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render(&self, width: u32, height: u32, samples: u32) -> Box<[Rgba]> {
|
pub fn render(&self, width: u32, height: u32, samples: u32, seed: u32) -> Box<[Rgba]> {
|
||||||
let mut sampler = Sampler::new(width, height, samples, 0);
|
let mut sampler = Sampler::new(width, height, samples, seed);
|
||||||
let mut data = Box::new_uninit_slice(width as usize * height as usize);
|
let mut film = Film::new(width, height);
|
||||||
let w = width as f32;
|
|
||||||
let h = height as f32;
|
|
||||||
let aspect_ratio = h / w;
|
|
||||||
|
|
||||||
for (i, pixel) in data.iter_mut().enumerate() {
|
|
||||||
let bx = i as u32 % width;
|
|
||||||
let by = i as u32 / width;
|
|
||||||
|
|
||||||
|
for (y, x) in Range2D::new(0..height, 0..width) {
|
||||||
let mut rgb = Vector::ZERO;
|
let mut rgb = Vector::ZERO;
|
||||||
|
|
||||||
for mut pixel_samples in sampler.pixel(bx, by) {
|
for mut pixel_sample in sampler.pixel(x, y) {
|
||||||
let (x_offset, y_offset) = pixel_samples.get_2d();
|
let p_film = film.get_camera_sample(x, y, &mut pixel_sample);
|
||||||
let x = bx as f32 + x_offset;
|
let ray = self.camera.ray(p_film);
|
||||||
let y = by as f32 + y_offset;
|
|
||||||
|
|
||||||
let cx = 2.0 * x / w - 1.0;
|
if let Some(h) = self.intersect(ray) {
|
||||||
let cy = (1.0 - 2.0 * y / h) * aspect_ratio;
|
rgb += h.normal.normalize() * 0.5 + vec3(0.5, 0.5, 0.5);
|
||||||
|
|
||||||
if let Some(h) = self.intersect(self.camera.ray(cx, cy)) {
|
|
||||||
rgb += h.normal.normalize() * 0.5 + Vector::new(0.5, 0.5, 0.5);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
rgb /= samples as f32;
|
rgb /= samples as f32;
|
||||||
|
|
||||||
pixel.write(Rgba::new(rgb.x, rgb.y, rgb.z, 1.0));
|
film.add_sample(x, y, rgba(rgb.x, rgb.y, rgb.z, 0.0), 1.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe { data.assume_init() }
|
film.into_data()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn intersect(&self, ray: Ray) -> Option<Hit> {
|
fn intersect(&self, r: Ray) -> Option<Hit> {
|
||||||
let mut hit: Option<Hit> = None;
|
let mut hit: Option<Hit> = None;
|
||||||
for shape in &self.contents {
|
|
||||||
|
for obj in &self.objects {
|
||||||
let t_max = hit.as_ref().map(|h| h.t).unwrap_or(f32::INFINITY);
|
let t_max = hit.as_ref().map(|h| h.t).unwrap_or(f32::INFINITY);
|
||||||
if let Some(h) = shape.intersect(&ray, t_max) {
|
if let Some(h) = obj.intersect(&r, t_max) {
|
||||||
hit = Some(h);
|
hit = Some(h)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
hit
|
hit
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,8 +55,9 @@ impl PyScene {
|
||||||
Self(Scene::new(camera.0.clone(), contents))
|
Self(Scene::new(camera.0.clone(), contents))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render(&self, width: u32, height: u32, samples: u32) -> Vec<f32> {
|
#[pyo3(signature = (width, height, samples, seed = 0))]
|
||||||
let result = self.0.render(width, height, samples);
|
pub fn render(&self, width: u32, height: u32, samples: u32, seed: u32) -> Vec<f32> {
|
||||||
|
let result = self.0.render(width, height, samples, seed);
|
||||||
|
|
||||||
cast_slice_box::<Rgba, f32>(result).into_vec()
|
cast_slice_box::<Rgba, f32>(result).into_vec()
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue