Draw first image
This commit is contained in:
parent
4c872f9f91
commit
62b9fdcb56
19 changed files with 1566 additions and 76 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -1 +1,2 @@
|
||||||
.target
|
.target
|
||||||
|
**.exr
|
||||||
|
|
|
||||||
1072
Cargo.lock
generated
1072
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
|
@ -1,4 +1,4 @@
|
||||||
[workspace]
|
[workspace]
|
||||||
|
|
||||||
members = [ "ray-tracing-core"]
|
members = [ "ray-tracing-core", "ray-tracing-image", "ray-tracing-scene"]
|
||||||
resolver = "2"
|
resolver = "2"
|
||||||
|
|
|
||||||
|
|
@ -4,3 +4,4 @@ version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
rand = "0.8.5"
|
||||||
|
|
|
||||||
74
ray-tracing-core/src/camera.rs
Normal file
74
ray-tracing-core/src/camera.rs
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
67
ray-tracing-core/src/color.rs
Normal file
67
ray-tracing-core/src/color.rs
Normal 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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,11 +1,16 @@
|
||||||
|
pub mod camera;
|
||||||
|
pub mod color;
|
||||||
pub mod material;
|
pub mod material;
|
||||||
pub mod math;
|
pub mod math;
|
||||||
pub mod ray;
|
pub mod ray;
|
||||||
|
pub mod renderer;
|
||||||
pub mod scene;
|
pub mod scene;
|
||||||
|
|
||||||
pub mod prelude {
|
pub mod prelude {
|
||||||
pub type Float = f64;
|
pub type Float = f32;
|
||||||
|
pub use crate::color::Color;
|
||||||
pub use crate::material::Material;
|
pub use crate::material::Material;
|
||||||
pub use crate::math::{Dir3, Pos3};
|
pub use crate::math::*;
|
||||||
pub use crate::ray::Ray;
|
pub use crate::ray::Ray;
|
||||||
|
pub use std::f32::consts as FloatConsts;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,54 +1,26 @@
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use std::ops::{Add, Div, Mul, Neg, Sub};
|
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)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub struct Dir3 {
|
pub struct Dir3 {
|
||||||
x: Float,
|
pub(crate) x: Float,
|
||||||
y: Float,
|
pub(crate) y: Float,
|
||||||
z: 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 Dir3 {
|
impl Dir3 {
|
||||||
pub fn new(x: Float, y: Float, z: Float) -> Self {
|
pub fn new(x: Float, y: Float, z: Float) -> Self {
|
||||||
Self { x, y, z }
|
Self { x, y, z }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn x(self) -> Float {
|
pub fn x(self) -> Float {
|
||||||
self.x
|
self.x
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn y(self) -> Float {
|
pub fn y(self) -> Float {
|
||||||
self.y
|
self.y
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn z(self) -> Float {
|
pub fn z(self) -> Float {
|
||||||
self.z
|
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 {
|
pub fn normalize(self) -> Self {
|
||||||
self / self.length()
|
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 {
|
pub fn length(self) -> Float {
|
||||||
type Output = Dir3;
|
Float::sqrt(self.length_squared())
|
||||||
|
}
|
||||||
|
|
||||||
fn sub(self, rhs: Self) -> Self::Output {
|
pub fn dot(self, rhs: Self) -> Float {
|
||||||
Dir3 {
|
self.x * rhs.x + self.y * rhs.y + self.z * rhs.z
|
||||||
x: self.x - rhs.x,
|
}
|
||||||
y: self.y - rhs.y,
|
|
||||||
z: 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 {
|
impl Add<pos3::Pos3> for Dir3 {
|
||||||
type Output = Pos3;
|
type Output = pos3::Pos3;
|
||||||
|
|
||||||
fn add(self, rhs: Pos3) -> Self::Output {
|
fn add(self, rhs: pos3::Pos3) -> Self::Output {
|
||||||
Pos3 {
|
pos3::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 {
|
|
||||||
x: self.x + rhs.x,
|
x: self.x + rhs.x,
|
||||||
y: self.y + rhs.y,
|
y: self.y + rhs.y,
|
||||||
z: self.z + rhs.z,
|
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 {
|
impl Div<Float> for Dir3 {
|
||||||
type Output = Dir3;
|
type Output = Dir3;
|
||||||
|
|
||||||
5
ray-tracing-core/src/math/mod.rs
Normal file
5
ray-tracing-core/src/math/mod.rs
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
pub mod dir3;
|
||||||
|
pub mod pos3;
|
||||||
|
|
||||||
|
pub use dir3::Dir3;
|
||||||
|
pub use pos3::Pos3;
|
||||||
57
ray-tracing-core/src/math/pos3.rs
Normal file
57
ray-tracing-core/src/math/pos3.rs
Normal 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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,7 +2,25 @@ use crate::prelude::*;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub struct Ray {
|
pub struct Ray {
|
||||||
pub start: Pos3,
|
start: Pos3,
|
||||||
pub dir: Dir3,
|
dir: Dir3,
|
||||||
pub time: Float,
|
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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
63
ray-tracing-core/src/renderer.rs
Normal file
63
ray-tracing-core/src/renderer.rs
Normal 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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,24 +1,34 @@
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
use rand::Rng;
|
||||||
|
|
||||||
pub trait Scene {
|
pub trait Scene<R: Rng> {
|
||||||
fn intersect(&self, ray: Ray, min: Float, max: Float) -> Option<Intersection<'_>>;
|
fn intersect(&self, ray: Ray, min: Float, max: Float) -> Option<Intersection<'_, R>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Intersection<'sc> {
|
pub struct Intersection<'sc, R: Rng> {
|
||||||
t: Float,
|
t: Float,
|
||||||
material: &'sc dyn Material,
|
normal: Dir3,
|
||||||
|
material: &'sc dyn Material<R>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'sc> Intersection<'sc> {
|
impl<'sc, R: Rng> Intersection<'sc, R> {
|
||||||
pub fn new(t: Float, material: &'sc dyn Material) -> Self {
|
pub fn new(t: Float, normal: Dir3, material: &'sc dyn Material<R>) -> Self {
|
||||||
Self { t, material }
|
Self {
|
||||||
|
t,
|
||||||
|
normal,
|
||||||
|
material,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn t(&self) -> Float {
|
pub fn t(&self) -> Float {
|
||||||
self.t
|
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
|
self.material
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
11
ray-tracing-image/Cargo.toml
Normal file
11
ray-tracing-image/Cargo.toml
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
[package]
|
||||||
|
name = "ray-tracing-image"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
image = "0.25.2"
|
||||||
|
rand = { version = "0.8.5", features = ["small_rng"] }
|
||||||
|
ray-tracing-core = { path = "../ray-tracing-core" }
|
||||||
|
ray-tracing-scene = { path = "../ray-tracing-scene" }
|
||||||
|
rayon = "1.10.0"
|
||||||
52
ray-tracing-image/src/main.rs
Normal file
52
ray-tracing-image/src/main.rs
Normal file
|
|
@ -0,0 +1,52 @@
|
||||||
|
use image::{ImageBuffer, ImageResult, Rgb};
|
||||||
|
use rand::{rngs::SmallRng, SeedableRng};
|
||||||
|
use ray_tracing_core::{
|
||||||
|
camera::BasicCamera,
|
||||||
|
prelude::*,
|
||||||
|
renderer::{ClassicalRenderer, DepthRenderer},
|
||||||
|
};
|
||||||
|
use ray_tracing_scene::BasicScene;
|
||||||
|
use rayon::prelude::*;
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
fn render_image<C: ClassicalRenderer<SmallRng> + Sync>(
|
||||||
|
renderer: C,
|
||||||
|
outputfilename: impl AsRef<Path>,
|
||||||
|
samples_per_pixel: usize,
|
||||||
|
) -> ImageResult<()> {
|
||||||
|
let mut data = vec![Color::black(); (renderer.width() * renderer.height()) as usize];
|
||||||
|
|
||||||
|
data.par_iter_mut().enumerate().for_each(|(i, c)| {
|
||||||
|
let x = (i % renderer.width() as usize) as u32;
|
||||||
|
let y = (i / renderer.width() as usize) as u32;
|
||||||
|
|
||||||
|
let mut rng = SmallRng::seed_from_u64((x + y * renderer.width()) as u64);
|
||||||
|
for _ in 0..samples_per_pixel {
|
||||||
|
*c += renderer.render_pixel(x, y, &mut rng) / (samples_per_pixel as Float);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let img = ImageBuffer::from_fn(renderer.width(), renderer.height(), |x, y| {
|
||||||
|
let c = data[(x + y * renderer.width()) as usize];
|
||||||
|
Rgb::<Float>([c.r(), c.g(), c.b()])
|
||||||
|
});
|
||||||
|
|
||||||
|
img.save(outputfilename)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() -> ImageResult<()> {
|
||||||
|
let s = BasicScene::new();
|
||||||
|
|
||||||
|
let c = BasicCamera::new(
|
||||||
|
640,
|
||||||
|
400,
|
||||||
|
Pos3::new(-10.0, 0.0, 0.0),
|
||||||
|
Dir3::new(1.0, 0.0, 0.0),
|
||||||
|
Dir3::up(),
|
||||||
|
Float::to_radians(90.0),
|
||||||
|
);
|
||||||
|
|
||||||
|
let r = DepthRenderer::new(s, c);
|
||||||
|
|
||||||
|
render_image(r, "test.exr", 16)
|
||||||
|
}
|
||||||
8
ray-tracing-scene/Cargo.toml
Normal file
8
ray-tracing-scene/Cargo.toml
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
[package]
|
||||||
|
name = "ray-tracing-scene"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
rand = "0.8.5"
|
||||||
|
ray-tracing-core = { path = "../ray-tracing-core" }
|
||||||
51
ray-tracing-scene/src/lib.rs
Normal file
51
ray-tracing-scene/src/lib.rs
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
use rand::Rng;
|
||||||
|
use ray_tracing_core::material::DefaultMaterial;
|
||||||
|
use ray_tracing_core::prelude::*;
|
||||||
|
use ray_tracing_core::scene::{Intersection, Scene};
|
||||||
|
|
||||||
|
pub mod sphere;
|
||||||
|
|
||||||
|
pub struct BasicScene {
|
||||||
|
spheres: Vec<(Pos3, Float)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BasicScene {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
spheres: vec![(Pos3::zero(), 1.0), (Pos3::new(0.0, 0.0, 2.5), 2.0)],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R: Rng> Scene<R> for BasicScene {
|
||||||
|
fn intersect(&self, ray: Ray, min: Float, max: Float) -> Option<Intersection<'_, R>> {
|
||||||
|
let mut intersection: Option<Intersection<'_, R>> = None;
|
||||||
|
|
||||||
|
for &(c, r) in &self.spheres {
|
||||||
|
let offset = ray.start() - c;
|
||||||
|
let p = Dir3::dot(ray.dir(), offset);
|
||||||
|
let delta = p * p - (offset.length_squared() - r * r);
|
||||||
|
|
||||||
|
if delta >= 0.0 {
|
||||||
|
let d = -p - Float::sqrt(delta);
|
||||||
|
|
||||||
|
let int = Intersection::new(
|
||||||
|
d,
|
||||||
|
((ray.start() + d * ray.dir()) - c).normalize(),
|
||||||
|
&DefaultMaterial {},
|
||||||
|
);
|
||||||
|
if d >= min && d <= max {
|
||||||
|
if let Some(i) = intersection.as_ref() {
|
||||||
|
if i.t() > d {
|
||||||
|
intersection.replace(int);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
intersection = Some(int)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
intersection
|
||||||
|
}
|
||||||
|
}
|
||||||
0
ray-tracing-scene/src/sphere.rs
Normal file
0
ray-tracing-scene/src/sphere.rs
Normal file
Loading…
Add table
Add a link
Reference in a new issue