Compare commits
2 commits
1ab382c5b3
...
094f4947dc
Author | SHA1 | Date | |
---|---|---|---|
094f4947dc | |||
a30b29a130 |
10 changed files with 470 additions and 154 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -3,52 +3,24 @@ use std::{
|
||||||
ops::{Add, AddAssign, Deref, Div, DivAssign, Mul, Sub},
|
ops::{Add, AddAssign, Deref, Div, DivAssign, Mul, Sub},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub mod shapes;
|
mod _2d;
|
||||||
|
mod shapes;
|
||||||
|
mod transform;
|
||||||
|
|
||||||
|
pub use _2d::*;
|
||||||
|
pub use shapes::{Hit, Hittable, Shape};
|
||||||
|
pub use transform::Transform;
|
||||||
|
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub struct Vector(glam::Vec3A);
|
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 {
|
impl Vector {
|
||||||
pub const ZERO: Self = Self(glam::Vec3A::ZERO);
|
pub const ZERO: Self = Self(glam::Vec3A::ZERO);
|
||||||
pub const I: Self = Self(glam::Vec3A::X);
|
pub const I: Self = Self(glam::Vec3A::X);
|
||||||
pub const J: Self = Self(glam::Vec3A::Y);
|
pub const J: Self = Self(glam::Vec3A::Y);
|
||||||
pub const K: Self = Self(glam::Vec3A::Z);
|
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 {
|
pub fn length(self) -> f32 {
|
||||||
self.0.length()
|
self.0.length()
|
||||||
}
|
}
|
||||||
|
@ -68,6 +40,50 @@ impl Vector {
|
||||||
pub fn cross(self, other: Vector) -> Vector {
|
pub fn cross(self, other: Vector) -> Vector {
|
||||||
Self(self.0.cross(other.0))
|
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 {
|
impl Mul<f32> for Vector {
|
||||||
|
@ -118,6 +134,14 @@ impl AddAssign for Vector {
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub struct Point(glam::Vec3A);
|
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 {
|
impl fmt::Debug for Point {
|
||||||
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))
|
||||||
|
@ -136,19 +160,7 @@ impl From<Point> for glam::Vec3 {
|
||||||
|
|
||||||
impl From<(f32, f32, f32)> for Point {
|
impl From<(f32, f32, f32)> for Point {
|
||||||
fn from((x, y, z): (f32, f32, f32)) -> Self {
|
fn from((x, y, z): (f32, f32, f32)) -> Self {
|
||||||
Self::new(x, y, z)
|
point3(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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -168,58 +180,11 @@ impl Sub for Point {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
impl Sub<Vector> for Point {
|
||||||
#[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;
|
type Output = Point;
|
||||||
|
|
||||||
fn mul(self, rhs: Point) -> Self::Output {
|
fn sub(self, rhs: Vector) -> Self::Output {
|
||||||
Point(self.0.transform_point3a(rhs.0))
|
Point(self.0 - rhs.0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -230,11 +195,11 @@ pub struct Ray {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Ray {
|
impl Ray {
|
||||||
pub fn new(origin: Point, direction: Vector) -> Self {
|
|
||||||
Self { origin, direction }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn at(&self, t: f32) -> Point {
|
pub fn at(&self, t: f32) -> Point {
|
||||||
self.origin + t * self.direction
|
self.origin + t * self.direction
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn ray(origin: Point, direction: Vector) -> Ray {
|
||||||
|
Ray { origin, direction }
|
||||||
|
}
|
||||||
|
|
188
core/src/geometry/_2d.rs
Normal file
188
core/src/geometry/_2d.rs
Normal file
|
@ -0,0 +1,188 @@
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
}
|
|
@ -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>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,7 +52,7 @@ impl Hittable for Plane {
|
||||||
let n_dot_r = self.normal.dot(ray.direction);
|
let n_dot_r = self.normal.dot(ray.direction);
|
||||||
(n_dot_r.abs() > f32::EPSILON)
|
(n_dot_r.abs() > f32::EPSILON)
|
||||||
.then(|| {
|
.then(|| {
|
||||||
let n_dot_o = self.normal.dot(ray.origin.to_vector());
|
let n_dot_o = self.normal.dot(ray.origin.into());
|
||||||
-(n_dot_o - self.d) / n_dot_r
|
-(n_dot_o - self.d) / n_dot_r
|
||||||
})
|
})
|
||||||
.filter(|t| *t < t_max && *t > 0.0)
|
.filter(|t| *t < t_max && *t > 0.0)
|
||||||
|
|
69
core/src/geometry/transform.rs
Normal file
69
core/src/geometry/transform.rs
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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