Compare commits

..

No commits in common. "094f4947dcb640ae954ef6902a1b25833f209353" and "1ab382c5b37b300b807c43377775c8f872d1a533" have entirely different histories.

10 changed files with 155 additions and 471 deletions

7
Cargo.lock generated
View file

@ -107,7 +107,6 @@ dependencies = [
"enum_dispatch",
"fasthash",
"glam",
"range2d",
]
[[package]]
@ -258,12 +257,6 @@ version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc"
[[package]]
name = "range2d"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7169c4e8549f72214a10fe6a64e24bf5e2d75ca9378d0ac7934afb9acbdfc313"
[[package]]
name = "rdrand"
version = "0.4.0"

View file

@ -11,7 +11,6 @@ bytemuck = { version = "1.22.0", optional = true }
enum_dispatch = "0.3.13"
fasthash = "0.4.0"
glam = "0.30.2"
range2d = "0.2.0"
[features]
fast-math = ["glam/fast-math"]

View file

@ -1,8 +1,4 @@
use crate::{
color::Rgba,
geometry::{Point, Point2, Ray, Transform, Transform2, Vector, point2, ray, vec3},
sampling::PixelSample,
};
use crate::geometry::{Point, Ray, Transform, Vector};
#[derive(Debug, Clone)]
pub struct Camera {
@ -20,52 +16,9 @@ impl Camera {
Self { transform }
}
pub fn ray(&self, p_film: Point2) -> Ray {
ray(
self.transform * Point::ORIGIN,
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
pub fn ray(&self, x: f32, y: f32) -> Ray {
let origin = self.transform * Point::ORIGIN;
let direction = self.transform * Vector::new(x, y, 1.0);
Ray::new(origin, direction)
}
}

View file

@ -1,7 +1,4 @@
use std::{
fmt,
ops::{Add, AddAssign, Mul},
};
use std::fmt;
#[derive(Clone, Copy)]
#[repr(C)]
@ -12,12 +9,12 @@ pub struct Rgba {
a: f32,
}
pub const fn rgba(r: f32, g: f32, b: f32, a: f32) -> Rgba {
Rgba { r, g, b, a }
}
impl Rgba {
pub const BLACK: Self = rgba(0.0, 0.0, 0.0, 1.0);
pub const BLACK: Self = Self::new(0.0, 0.0, 0.0, 1.0);
pub const fn new(r: f32, g: f32, b: f32, a: f32) -> Self {
Self { r, g, b, a }
}
}
impl fmt::Debug for Rgba {
@ -30,46 +27,3 @@ impl fmt::Debug for Rgba {
.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
}
}

View file

@ -3,24 +3,52 @@ use std::{
ops::{Add, AddAssign, Deref, Div, DivAssign, Mul, Sub},
};
mod _2d;
mod shapes;
mod transform;
pub use _2d::*;
pub use shapes::{Hit, Hittable, Shape};
pub use transform::Transform;
pub mod shapes;
#[repr(transparent)]
#[derive(Clone, Copy)]
pub struct Vector(glam::Vec3A);
impl fmt::Debug for Vector {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple(stringify!(Vector))
.field(&self.0.x)
.field(&self.0.y)
.field(&self.0.z)
.finish()
}
}
impl From<Vector> for glam::Vec3 {
fn from(value: Vector) -> Self {
value.0.into()
}
}
impl From<(f32, f32, f32)> for Vector {
fn from((x, y, z): (f32, f32, f32)) -> Self {
Self::new(x, y, z)
}
}
impl Deref for Vector {
type Target = glam::Vec3A;
#[inline]
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl Vector {
pub const ZERO: Self = Self(glam::Vec3A::ZERO);
pub const I: Self = Self(glam::Vec3A::X);
pub const J: Self = Self(glam::Vec3A::Y);
pub const K: Self = Self(glam::Vec3A::Z);
pub fn new(x: f32, y: f32, z: f32) -> Self {
Self(glam::vec3a(x, y, z))
}
pub fn length(self) -> f32 {
self.0.length()
}
@ -40,50 +68,6 @@ impl Vector {
pub fn cross(self, other: Vector) -> Vector {
Self(self.0.cross(other.0))
}
pub fn same_hemisphere(self, other: Vector) -> bool {
self.dot(other) > 0.0
}
}
pub fn vec3(x: f32, y: f32, z: f32) -> Vector {
Vector(glam::vec3a(x, y, z))
}
impl fmt::Debug for Vector {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple(stringify!(Vector))
.field(&self.0.x)
.field(&self.0.y)
.field(&self.0.z)
.finish()
}
}
impl From<(f32, f32, f32)> for Vector {
fn from((x, y, z): (f32, f32, f32)) -> Self {
vec3(x, y, z)
}
}
impl From<Point> for Vector {
fn from(p: Point) -> Self {
Self(p.0)
}
}
impl Deref for Vector {
type Target = glam::Vec3A;
#[inline]
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl From<Vector> for glam::Vec3 {
fn from(value: Vector) -> Self {
value.0.into()
}
}
impl Mul<f32> for Vector {
@ -134,14 +118,6 @@ impl AddAssign for Vector {
#[derive(Clone, Copy)]
pub struct Point(glam::Vec3A);
impl Point {
pub const ORIGIN: Self = Self(glam::Vec3A::ZERO);
}
pub fn point3(x: f32, y: f32, z: f32) -> Point {
Point(glam::vec3a(x, y, z))
}
impl fmt::Debug for Point {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple(stringify!(Point))
@ -160,7 +136,19 @@ impl From<Point> for glam::Vec3 {
impl From<(f32, f32, f32)> for Point {
fn from((x, y, z): (f32, f32, f32)) -> Self {
point3(x, y, z)
Self::new(x, y, z)
}
}
impl Point {
pub const ORIGIN: Self = Self(glam::Vec3A::ZERO);
pub fn new(x: f32, y: f32, z: f32) -> Self {
Self(glam::vec3a(x, y, z))
}
pub fn to_vector(self) -> Vector {
Vector(self.0)
}
}
@ -180,11 +168,58 @@ impl Sub for Point {
}
}
impl Sub<Vector> for Point {
#[derive(Copy, Clone)]
#[repr(transparent)]
pub struct Transform(glam::Affine3A);
impl fmt::Debug for Transform {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct(stringify!(Transform))
.field(stringify!(matrix3), &self.0.matrix3)
.field(stringify!(translation), &self.0.translation)
.finish()
}
}
impl Transform {
pub fn scale(x: f32, y: f32, z: f32) -> Self {
Self(glam::Affine3A::from_scale(glam::vec3(x, y, z)))
}
pub fn inverse(self) -> Self {
Self(self.0.inverse())
}
pub fn look_at(eye: Point, center: Point, up: Vector) -> Self {
Self(glam::Affine3A::look_at_lh(
eye.into(),
center.into(),
up.into(),
))
}
}
impl Mul for Transform {
type Output = Transform;
fn mul(self, rhs: Self) -> Self::Output {
Self(self.0 * rhs.0)
}
}
impl Mul<Vector> for Transform {
type Output = Vector;
fn mul(self, rhs: Vector) -> Self::Output {
Vector(self.0.transform_vector3a(rhs.0))
}
}
impl Mul<Point> for Transform {
type Output = Point;
fn sub(self, rhs: Vector) -> Self::Output {
Point(self.0 - rhs.0)
fn mul(self, rhs: Point) -> Self::Output {
Point(self.0.transform_point3a(rhs.0))
}
}
@ -195,11 +230,11 @@ pub struct Ray {
}
impl Ray {
pub fn new(origin: Point, direction: Vector) -> Self {
Self { origin, direction }
}
pub fn at(&self, t: f32) -> Point {
self.origin + t * self.direction
}
}
pub fn ray(origin: Point, direction: Vector) -> Ray {
Ray { origin, direction }
}

View file

@ -1,188 +0,0 @@
use std::{
fmt,
ops::{Add, AddAssign, Deref, Div, DivAssign, Mul, Sub},
};
#[repr(transparent)]
#[derive(Clone, Copy)]
pub struct Vector2(glam::Vec2);
impl Vector2 {
pub const ZERO: Self = Self(glam::Vec2::ZERO);
pub const U: Self = Self(glam::Vec2::X);
pub const V: Self = Self(glam::Vec2::Y);
}
pub const fn vec2(x: f32, y: f32) -> Vector2 {
Vector2(glam::vec2(x, y))
}
impl fmt::Debug for Vector2 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple(stringify!(Vector))
.field(&self.0.x)
.field(&self.0.y)
.finish()
}
}
impl From<(f32, f32)> for Vector2 {
fn from((x, y): (f32, f32)) -> Self {
vec2(x, y)
}
}
impl Mul<f32> for Vector2 {
type Output = Self;
fn mul(self, rhs: f32) -> Self::Output {
Self(self.0 * rhs)
}
}
impl Mul<Vector2> for f32 {
type Output = Vector2;
fn mul(self, rhs: Vector2) -> Self::Output {
Vector2(self * rhs.0)
}
}
impl Div<f32> for Vector2 {
type Output = Self;
fn div(self, rhs: f32) -> Self::Output {
Self(self.0 / rhs)
}
}
impl DivAssign<f32> for Vector2 {
fn div_assign(&mut self, rhs: f32) {
*self = *self / rhs;
}
}
impl Add for Vector2 {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
Self(self.0 + rhs.0)
}
}
impl AddAssign for Vector2 {
fn add_assign(&mut self, rhs: Self) {
*self = *self + rhs;
}
}
#[repr(transparent)]
#[derive(Clone, Copy)]
pub struct Point2(glam::Vec2);
impl Point2 {
pub const ORIGIN: Self = Self(glam::Vec2::ZERO);
}
pub const fn point2(x: f32, y: f32) -> Point2 {
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 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple(stringify!(Point))
.field(&self.0.x)
.field(&self.0.y)
.finish()
}
}
impl Deref for Point2 {
type Target = glam::Vec2;
#[inline]
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl Add<Vector2> for Point2 {
type Output = Point2;
fn add(self, rhs: Vector2) -> Self::Output {
Self(self.0 + rhs.0)
}
}
impl Sub<Vector2> for Point2 {
type Output = Point2;
fn sub(self, rhs: Vector2) -> Self::Output {
Self(self.0 - rhs.0)
}
}
impl Sub for Point2 {
type Output = Vector2;
fn sub(self, rhs: Self) -> Self::Output {
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))
}
}

View file

@ -5,14 +5,14 @@ use enum_dispatch::enum_dispatch;
use super::{Point, Ray, Vector};
#[derive(Debug)]
pub struct Hit {
pub(crate) struct Hit {
pub point: Point,
pub normal: Vector,
pub t: f32,
}
#[enum_dispatch]
pub trait Hittable {
pub(crate) trait Hittable {
fn intersect(&self, ray: &Ray, t_max: f32) -> Option<Hit>;
}
@ -52,7 +52,7 @@ impl Hittable for Plane {
let n_dot_r = self.normal.dot(ray.direction);
(n_dot_r.abs() > f32::EPSILON)
.then(|| {
let n_dot_o = self.normal.dot(ray.origin.into());
let n_dot_o = self.normal.dot(ray.origin.to_vector());
-(n_dot_o - self.d) / n_dot_r
})
.filter(|t| *t < t_max && *t > 0.0)

View file

@ -1,69 +0,0 @@
use std::{fmt, ops::Mul};
use super::{Point, Ray, Vector};
#[derive(Copy, Clone)]
#[repr(transparent)]
pub struct Transform(glam::Affine3A);
impl fmt::Debug for Transform {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct(stringify!(Transform))
.field(stringify!(matrix3), &self.0.matrix3)
.field(stringify!(translation), &self.0.translation)
.finish()
}
}
impl Transform {
pub fn scale(x: f32, y: f32, z: f32) -> Self {
Self(glam::Affine3A::from_scale(glam::vec3(x, y, z)))
}
pub fn inverse(self) -> Self {
Self(self.0.inverse())
}
pub fn look_at(eye: Point, center: Point, up: Vector) -> Self {
Self(glam::Affine3A::look_at_lh(
eye.into(),
center.into(),
up.into(),
))
}
}
impl Mul for Transform {
type Output = Transform;
fn mul(self, rhs: Self) -> Self::Output {
Self(self.0 * rhs.0)
}
}
impl Mul<Vector> for Transform {
type Output = Vector;
fn mul(self, rhs: Vector) -> Self::Output {
Vector(self.0.transform_vector3a(rhs.0))
}
}
impl Mul<Point> for Transform {
type Output = Point;
fn mul(self, rhs: Point) -> Self::Output {
Point(self.0.transform_point3a(rhs.0))
}
}
impl Mul<Ray> for Transform {
type Output = Ray;
fn mul(self, rhs: Ray) -> Self::Output {
Ray {
origin: self * rhs.origin,
direction: self * rhs.direction,
}
}
}

View file

@ -1,8 +1,3 @@
use core::f32;
use geometry::{Hit, Hittable, Ray, point2, vec3};
use range2d::Range2D;
#[cfg(feature = "bytemuck")]
pub mod impl_bytemuck;
@ -12,57 +7,70 @@ mod camera;
mod geometry;
mod sampling;
pub use camera::Camera;
pub use geometry::{Point, Shape, Transform, Vector};
use core::f32;
use camera::Film;
use color::{Rgba, rgba};
pub use camera::Camera;
pub use geometry::{Point, Transform, Vector, shapes::Shape};
use color::Rgba;
use geometry::{
Ray,
shapes::{Hit, Hittable},
};
use sampling::Sampler;
pub struct Scene {
camera: Camera,
objects: Vec<Shape>,
contents: Vec<Shape>,
}
impl Scene {
pub fn new(camera: Camera, objects: Vec<Shape>) -> Self {
Self { camera, objects }
pub fn new(camera: Camera, contents: Vec<Shape>) -> Self {
Self { camera, contents }
}
pub fn render(&self, width: u32, height: u32, samples: u32, seed: u32) -> Box<[Rgba]> {
let mut sampler = Sampler::new(width, height, samples, seed);
let mut film = Film::new(width, height);
pub fn render(&self, width: u32, height: u32, samples: u32) -> Box<[Rgba]> {
let mut sampler = Sampler::new(width, height, samples, 0);
let mut data = Box::new_uninit_slice(width as usize * height as usize);
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;
for mut pixel_sample in sampler.pixel(x, y) {
let p_film = film.get_camera_sample(x, y, &mut pixel_sample);
let ray = self.camera.ray(p_film);
for mut pixel_samples in sampler.pixel(bx, by) {
let (x_offset, y_offset) = pixel_samples.get_2d();
let x = bx as f32 + x_offset;
let y = by as f32 + y_offset;
if let Some(h) = self.intersect(ray) {
rgb += h.normal.normalize() * 0.5 + vec3(0.5, 0.5, 0.5);
let cx = 2.0 * x / w - 1.0;
let cy = (1.0 - 2.0 * y / h) * aspect_ratio;
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;
film.add_sample(x, y, rgba(rgb.x, rgb.y, rgb.z, 0.0), 1.0);
pixel.write(Rgba::new(rgb.x, rgb.y, rgb.z, 1.0));
}
film.into_data()
unsafe { data.assume_init() }
}
fn intersect(&self, r: Ray) -> Option<Hit> {
fn intersect(&self, ray: Ray) -> Option<Hit> {
let mut hit: Option<Hit> = None;
for obj in &self.objects {
for shape in &self.contents {
let t_max = hit.as_ref().map(|h| h.t).unwrap_or(f32::INFINITY);
if let Some(h) = obj.intersect(&r, t_max) {
hit = Some(h)
if let Some(h) = shape.intersect(&ray, t_max) {
hit = Some(h);
}
}
hit
}
}

View file

@ -55,9 +55,8 @@ impl PyScene {
Self(Scene::new(camera.0.clone(), contents))
}
#[pyo3(signature = (width, height, samples, seed = 0))]
pub fn render(&self, width: u32, height: u32, samples: u32, seed: u32) -> Vec<f32> {
let result = self.0.render(width, height, samples, seed);
pub fn render(&self, width: u32, height: u32, samples: u32) -> Vec<f32> {
let result = self.0.render(width, height, samples);
cast_slice_box::<Rgba, f32>(result).into_vec()
}