Draw first image

This commit is contained in:
hal8174 2024-09-28 20:15:05 +02:00
parent 4c872f9f91
commit 62b9fdcb56
19 changed files with 1566 additions and 76 deletions

View file

@ -4,3 +4,4 @@ version = "0.1.0"
edition = "2021"
[dependencies]
rand = "0.8.5"

View file

@ -0,0 +1,74 @@
use crate::prelude::*;
use rand::Rng;
pub trait Camera<R: Rng> {
fn forward(&self, x: u32, y: u32, rng: &mut R) -> Ray;
fn width(&self) -> u32;
fn height(&self) -> u32;
}
pub struct BasicCamera {
width: u32,
height: u32,
pos: Pos3,
dir: Dir3,
h: Dir3,
v: Dir3,
}
impl BasicCamera {
pub fn new(
width: u32,
height: u32,
pos: Pos3,
dir: Dir3,
up: Dir3,
horizontal_fov: Float,
) -> Self {
assert!(horizontal_fov < FloatConsts::PI);
let dir = dir.normalize();
let up = up.normalize();
let l = Float::sin(0.5 * horizontal_fov);
let h = l * Dir3::cross(dir, up);
let v = l * ((height as Float) / (width as Float)) * Dir3::cross(dir, h.normalize());
Self {
width,
height,
pos,
dir,
h,
v,
}
}
pub fn from_look_at(
width: u32,
height: u32,
pos: Pos3,
look_at: Pos3,
up: Dir3,
horizontal_fov: Float,
) -> Self {
Self::new(width, height, pos, look_at - pos, up, horizontal_fov)
}
}
impl<R: Rng> Camera<R> for BasicCamera {
fn forward(&self, x: u32, y: u32, rng: &mut R) -> Ray {
// normalize x and y to -0.5 to 0.5
let x = ((x as Float + rng.gen::<Float>()) / (self.width as Float)) - 0.5;
let y = ((y as Float + rng.gen::<Float>()) / (self.height as Float)) - 0.5;
let dir = self.dir + x * self.h + y * self.v;
Ray::new(self.pos, dir.normalize(), 0.0)
}
fn width(&self) -> u32 {
self.width
}
fn height(&self) -> u32 {
self.height
}
}

View file

@ -0,0 +1,67 @@
use crate::prelude::*;
use std::ops::{Add, AddAssign, Div};
#[derive(Debug, Clone, Copy)]
pub struct Color {
r: Float,
g: Float,
b: Float,
}
impl Color {
pub fn new(r: Float, g: Float, b: Float) -> Self {
Self { r, g, b }
}
pub fn r(self) -> Float {
self.r
}
pub fn g(self) -> Float {
self.g
}
pub fn b(self) -> Float {
self.b
}
pub fn gray(x: Float) -> Self {
Self::new(x, x, x)
}
pub fn black() -> Self {
Self::gray(0.0)
}
}
impl Add for Color {
type Output = Color;
fn add(self, rhs: Self) -> Self::Output {
Self {
r: self.r + rhs.r,
g: self.g + rhs.g,
b: self.b + rhs.b,
}
}
}
impl AddAssign for Color {
fn add_assign(&mut self, rhs: Self) {
self.r += rhs.r;
self.g += rhs.g;
self.b += rhs.b;
}
}
impl Div<Float> for Color {
type Output = Color;
fn div(self, rhs: Float) -> Self::Output {
Self {
r: self.r / rhs,
g: self.g / rhs,
b: self.b / rhs,
}
}
}

View file

@ -1,11 +1,16 @@
pub mod camera;
pub mod color;
pub mod material;
pub mod math;
pub mod ray;
pub mod renderer;
pub mod scene;
pub mod prelude {
pub type Float = f64;
pub type Float = f32;
pub use crate::color::Color;
pub use crate::material::Material;
pub use crate::math::{Dir3, Pos3};
pub use crate::math::*;
pub use crate::ray::Ray;
pub use std::f32::consts as FloatConsts;
}

View file

@ -1 +1,15 @@
pub trait Material {}
use crate::prelude::*;
use rand::Rng;
/// All calculations for the material are done a tangent space of the intersection.
pub trait Material<R: Rng> {
fn eval(&self, w_in: Dir3, w_out: Dir3, rng: &mut R) -> Color;
}
pub struct DefaultMaterial {}
impl<R: Rng> Material<R> for DefaultMaterial {
fn eval(&self, _w_in: Dir3, _w_out: Dir3, rng: &mut R) -> Color {
Color::black()
}
}

View file

@ -1,54 +1,26 @@
use crate::prelude::*;
use std::ops::{Add, Div, Mul, Neg, Sub};
#[derive(Debug, Clone, Copy)]
pub struct Pos3 {
x: Float,
y: Float,
z: Float,
}
#[derive(Debug, Clone, Copy)]
pub struct Dir3 {
x: Float,
y: Float,
z: Float,
}
impl Pos3 {
pub fn new(x: Float, y: Float, z: Float) -> Self {
Self { x, y, z }
}
pub fn x(self) -> Float {
self.x
}
pub fn y(self) -> Float {
self.y
}
pub fn z(self) -> Float {
self.z
}
pub fn zero() -> Self {
Self {
x: 0.0,
y: 0.0,
z: 0.0,
}
}
pub(crate) x: Float,
pub(crate) y: Float,
pub(crate) z: Float,
}
impl Dir3 {
pub fn new(x: Float, y: Float, z: Float) -> Self {
Self { x, y, z }
}
pub fn x(self) -> Float {
self.x
}
pub fn y(self) -> Float {
self.y
}
pub fn z(self) -> Float {
self.z
}
@ -61,23 +33,32 @@ impl Dir3 {
}
}
pub fn up() -> Self {
Self::new(0.0, 1.0, 0.0)
}
pub fn normalize(self) -> Self {
self / self.length()
}
pub fn length(self) -> Float {
Float::sqrt(self.x * self.x + self.y * self.y + self.z * self.z)
pub fn length_squared(self) -> Float {
self.x * self.x + self.y * self.y + self.z * self.z
}
}
impl Sub for Pos3 {
type Output = Dir3;
pub fn length(self) -> Float {
Float::sqrt(self.length_squared())
}
fn sub(self, rhs: Self) -> Self::Output {
Dir3 {
x: self.x - rhs.x,
y: self.y - rhs.y,
z: self.z - rhs.z,
}
pub fn dot(self, rhs: Self) -> Float {
self.x * rhs.x + self.y * rhs.y + self.z * rhs.z
}
pub fn cross(self, rhs: Self) -> Self {
Self::new(
self.y * rhs.z - self.z * rhs.y,
self.z * rhs.x - self.x * rhs.z,
self.x * rhs.y - self.y * rhs.x,
)
}
}
@ -93,23 +74,11 @@ impl Add for Dir3 {
}
}
impl Add<Pos3> for Dir3 {
type Output = Pos3;
impl Add<pos3::Pos3> for Dir3 {
type Output = pos3::Pos3;
fn add(self, rhs: Pos3) -> Self::Output {
Pos3 {
x: self.x + rhs.x,
y: self.y + rhs.y,
z: self.z + rhs.z,
}
}
}
impl Add<Dir3> for Pos3 {
type Output = Pos3;
fn add(self, rhs: Dir3) -> Self::Output {
Pos3 {
fn add(self, rhs: pos3::Pos3) -> Self::Output {
pos3::Pos3 {
x: self.x + rhs.x,
y: self.y + rhs.y,
z: self.z + rhs.z,
@ -141,6 +110,18 @@ impl Mul<Float> for Dir3 {
}
}
impl Mul<Dir3> for Float {
type Output = Dir3;
fn mul(self, rhs: Dir3) -> Self::Output {
Dir3 {
x: self * rhs.x,
y: self * rhs.y,
z: self * rhs.z,
}
}
}
impl Div<Float> for Dir3 {
type Output = Dir3;

View file

@ -0,0 +1,5 @@
pub mod dir3;
pub mod pos3;
pub use dir3::Dir3;
pub use pos3::Pos3;

View file

@ -0,0 +1,57 @@
use crate::prelude::*;
use std::ops::{Add, Sub};
#[derive(Debug, Clone, Copy)]
pub struct Pos3 {
pub(crate) x: Float,
pub(crate) y: Float,
pub(crate) z: Float,
}
impl Pos3 {
pub fn new(x: Float, y: Float, z: Float) -> Self {
Self { x, y, z }
}
pub fn x(self) -> Float {
self.x
}
pub fn y(self) -> Float {
self.y
}
pub fn z(self) -> Float {
self.z
}
pub fn zero() -> Self {
Self {
x: 0.0,
y: 0.0,
z: 0.0,
}
}
}
impl Sub for pos3::Pos3 {
type Output = Dir3;
fn sub(self, rhs: Self) -> Self::Output {
Dir3 {
x: self.x - rhs.x,
y: self.y - rhs.y,
z: self.z - rhs.z,
}
}
}
impl Add<Dir3> for pos3::Pos3 {
type Output = pos3::Pos3;
fn add(self, rhs: Dir3) -> Self::Output {
pos3::Pos3 {
x: self.x + rhs.x,
y: self.y + rhs.y,
z: self.z + rhs.z,
}
}
}

View file

@ -2,7 +2,25 @@ use crate::prelude::*;
#[derive(Debug, Clone, Copy)]
pub struct Ray {
pub start: Pos3,
pub dir: Dir3,
pub time: Float,
start: Pos3,
dir: Dir3,
time: Float,
}
impl Ray {
pub fn new(start: Pos3, dir: Dir3, time: Float) -> Self {
Self { start, dir, time }
}
pub fn start(self) -> Pos3 {
self.start
}
pub fn dir(self) -> Dir3 {
self.dir
}
pub fn time(self) -> Float {
self.time
}
}

View file

@ -0,0 +1,63 @@
use std::marker::PhantomData;
use crate::{camera::Camera, prelude::*, scene::Scene};
use rand::Rng;
pub trait ClassicalRenderer<R: Rng> {
fn render_pixel(&self, x: u32, y: u32, rng: &mut R) -> Color;
fn width(&self) -> u32;
fn height(&self) -> u32;
}
pub struct DepthRenderer<S, C, R>
where
S: Scene<R>,
C: Camera<R>,
R: Rng,
{
scene: S,
camera: C,
rng: PhantomData<R>,
}
impl<S, C, R> DepthRenderer<S, C, R>
where
S: Scene<R>,
C: Camera<R>,
R: Rng,
{
pub fn new(scene: S, camera: C) -> Self {
Self {
scene,
camera,
rng: PhantomData {},
}
}
}
impl<S, C, R> ClassicalRenderer<R> for DepthRenderer<S, C, R>
where
S: Scene<R>,
C: Camera<R>,
R: Rng,
{
fn render_pixel(&self, x: u32, y: u32, rng: &mut R) -> Color {
let r = self.camera.forward(x, y, rng);
if let Some(i) = self.scene.intersect(r, 0.0, Float::INFINITY) {
// Color::gray(1.0 / i.t())
let c = 0.5 * (i.normal() + Dir3::new(1.0, 1.0, 1.0));
Color::new(c.x, c.y, c.z)
} else {
Color::black()
}
}
fn width(&self) -> u32 {
self.camera.width()
}
fn height(&self) -> u32 {
self.camera.height()
}
}

View file

@ -1,24 +1,34 @@
use crate::prelude::*;
use rand::Rng;
pub trait Scene {
fn intersect(&self, ray: Ray, min: Float, max: Float) -> Option<Intersection<'_>>;
pub trait Scene<R: Rng> {
fn intersect(&self, ray: Ray, min: Float, max: Float) -> Option<Intersection<'_, R>>;
}
pub struct Intersection<'sc> {
pub struct Intersection<'sc, R: Rng> {
t: Float,
material: &'sc dyn Material,
normal: Dir3,
material: &'sc dyn Material<R>,
}
impl<'sc> Intersection<'sc> {
pub fn new(t: Float, material: &'sc dyn Material) -> Self {
Self { t, material }
impl<'sc, R: Rng> Intersection<'sc, R> {
pub fn new(t: Float, normal: Dir3, material: &'sc dyn Material<R>) -> Self {
Self {
t,
normal,
material,
}
}
pub fn t(&self) -> Float {
self.t
}
pub fn material(&self) -> &'sc dyn Material {
pub fn normal(&self) -> Dir3 {
self.normal
}
pub fn material(&self) -> &'sc dyn Material<R> {
self.material
}
}