lol
This commit is contained in:
commit
0b528c5c4f
21 changed files with 92121 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
target
|
1157
Cargo.lock
generated
Normal file
1157
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
9
Cargo.toml
Normal file
9
Cargo.toml
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
[package]
|
||||||
|
name = "cpu-rt"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
image = "*"
|
||||||
|
indicatif = "0.17.8"
|
||||||
|
rand = "0.8.5"
|
BIN
before.png
Normal file
BIN
before.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.4 MiB |
BIN
cpp/a.out
Executable file
BIN
cpp/a.out
Executable file
Binary file not shown.
24
cpp/color.h
Normal file
24
cpp/color.h
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
#ifndef COLOR_H
|
||||||
|
#define COLOR_H
|
||||||
|
|
||||||
|
#include "vec3.h"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
using color = vec3;
|
||||||
|
|
||||||
|
void write_color(std::ostream& out, const color& pixel_color) {
|
||||||
|
auto r = pixel_color.x();
|
||||||
|
auto g = pixel_color.y();
|
||||||
|
auto b = pixel_color.z();
|
||||||
|
|
||||||
|
// Translate the [0,1] component values to the byte range [0,255].
|
||||||
|
int rbyte = int(255.999 * r);
|
||||||
|
int gbyte = int(255.999 * g);
|
||||||
|
int bbyte = int(255.999 * b);
|
||||||
|
|
||||||
|
// Write out the pixel color components.
|
||||||
|
out << rbyte << ' ' << gbyte << ' ' << bbyte << '\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
56
cpp/main.cpp
Normal file
56
cpp/main.cpp
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
#include "color.h"
|
||||||
|
#include "ray.h"
|
||||||
|
#include "vec3.h"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
|
||||||
|
// Image
|
||||||
|
|
||||||
|
auto aspect_ratio = 16.0 / 9.0;
|
||||||
|
int image_width = 400;
|
||||||
|
|
||||||
|
// Calculate the image height, and ensure that it's at least 1.
|
||||||
|
int image_height = int(image_width / aspect_ratio);
|
||||||
|
image_height = (image_height < 1) ? 1 : image_height;
|
||||||
|
|
||||||
|
// Camera
|
||||||
|
|
||||||
|
auto focal_length = 1.0;
|
||||||
|
auto viewport_height = 2.0;
|
||||||
|
auto viewport_width = viewport_height * (double(image_width) / image_height);
|
||||||
|
auto camera_center = point3(0, 0, 0);
|
||||||
|
|
||||||
|
// Calculate the vectors across the horizontal and down the vertical viewport edges.
|
||||||
|
auto viewport_u = vec3(viewport_width, 0, 0);
|
||||||
|
auto viewport_v = vec3(0, -viewport_height, 0);
|
||||||
|
|
||||||
|
// Calculate the horizontal and vertical delta vectors from pixel to pixel.
|
||||||
|
auto pixel_delta_u = viewport_u / image_width;
|
||||||
|
auto pixel_delta_v = viewport_v / image_height;
|
||||||
|
|
||||||
|
// Calculate the location of the upper left pixel.
|
||||||
|
auto viewport_upper_left = camera_center - vec3(0, 0, focal_length) - viewport_u / 2 - viewport_v / 2;
|
||||||
|
auto pixel00_loc = viewport_upper_left + 0.5 * (pixel_delta_u + pixel_delta_v);
|
||||||
|
|
||||||
|
// Render
|
||||||
|
|
||||||
|
std::cout << "P3\n"
|
||||||
|
<< image_width << " " << image_height << "\n255\n";
|
||||||
|
|
||||||
|
for (int j = 0; j < image_height; j++)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < image_width; i++)
|
||||||
|
{
|
||||||
|
auto pixel_center = pixel00_loc + (i * pixel_delta_u) + (j * pixel_delta_v);
|
||||||
|
auto ray_direction = pixel_center - camera_center;
|
||||||
|
|
||||||
|
ray r(camera_center, ray_direction);
|
||||||
|
|
||||||
|
color pixel_color = ray_color(r);
|
||||||
|
write_color(std::cout, pixel_color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
24
cpp/ray.h
Normal file
24
cpp/ray.h
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
#ifndef RAY_H
|
||||||
|
#define RAY_H
|
||||||
|
|
||||||
|
#include "vec3.h"
|
||||||
|
|
||||||
|
class ray {
|
||||||
|
public:
|
||||||
|
ray() {}
|
||||||
|
|
||||||
|
ray(const point3& origin, const vec3& direction) : orig(origin), dir(direction) {}
|
||||||
|
|
||||||
|
const point3& origin() const { return orig; }
|
||||||
|
const vec3& direction() const { return dir; }
|
||||||
|
|
||||||
|
point3 at(double t) const {
|
||||||
|
return orig + t*dir;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
point3 orig;
|
||||||
|
vec3 dir;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
90003
cpp/test.ppm
Normal file
90003
cpp/test.ppm
Normal file
File diff suppressed because it is too large
Load diff
101
cpp/vec3.h
Normal file
101
cpp/vec3.h
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
#ifndef VEC3_H
|
||||||
|
#define VEC3_H
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
using std::sqrt;
|
||||||
|
|
||||||
|
class vec3 {
|
||||||
|
public:
|
||||||
|
double e[3];
|
||||||
|
|
||||||
|
vec3() : e{0,0,0} {}
|
||||||
|
vec3(double e0, double e1, double e2) : e{e0, e1, e2} {}
|
||||||
|
|
||||||
|
double x() const { return e[0]; }
|
||||||
|
double y() const { return e[1]; }
|
||||||
|
double z() const { return e[2]; }
|
||||||
|
|
||||||
|
vec3 operator-() const { return vec3(-e[0], -e[1], -e[2]); }
|
||||||
|
double operator[](int i) const { return e[i]; }
|
||||||
|
double& operator[](int i) { return e[i]; }
|
||||||
|
|
||||||
|
vec3& operator+=(const vec3& v) {
|
||||||
|
e[0] += v.e[0];
|
||||||
|
e[1] += v.e[1];
|
||||||
|
e[2] += v.e[2];
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3& operator*=(double t) {
|
||||||
|
e[0] *= t;
|
||||||
|
e[1] *= t;
|
||||||
|
e[2] *= t;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3& operator/=(double t) {
|
||||||
|
return *this *= 1/t;
|
||||||
|
}
|
||||||
|
|
||||||
|
double length() const {
|
||||||
|
return sqrt(length_squared());
|
||||||
|
}
|
||||||
|
|
||||||
|
double length_squared() const {
|
||||||
|
return e[0]*e[0] + e[1]*e[1] + e[2]*e[2];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// point3 is just an alias for vec3, but useful for geometric clarity in the code.
|
||||||
|
using point3 = vec3;
|
||||||
|
|
||||||
|
|
||||||
|
// Vector Utility Functions
|
||||||
|
|
||||||
|
inline std::ostream& operator<<(std::ostream& out, const vec3& v) {
|
||||||
|
return out << v.e[0] << ' ' << v.e[1] << ' ' << v.e[2];
|
||||||
|
}
|
||||||
|
|
||||||
|
inline vec3 operator+(const vec3& u, const vec3& v) {
|
||||||
|
return vec3(u.e[0] + v.e[0], u.e[1] + v.e[1], u.e[2] + v.e[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline vec3 operator-(const vec3& u, const vec3& v) {
|
||||||
|
return vec3(u.e[0] - v.e[0], u.e[1] - v.e[1], u.e[2] - v.e[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline vec3 operator*(const vec3& u, const vec3& v) {
|
||||||
|
return vec3(u.e[0] * v.e[0], u.e[1] * v.e[1], u.e[2] * v.e[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline vec3 operator*(double t, const vec3& v) {
|
||||||
|
return vec3(t*v.e[0], t*v.e[1], t*v.e[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline vec3 operator*(const vec3& v, double t) {
|
||||||
|
return t * v;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline vec3 operator/(const vec3& v, double t) {
|
||||||
|
return (1/t) * v;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline double dot(const vec3& u, const vec3& v) {
|
||||||
|
return u.e[0] * v.e[0]
|
||||||
|
+ u.e[1] * v.e[1]
|
||||||
|
+ u.e[2] * v.e[2];
|
||||||
|
}
|
||||||
|
|
||||||
|
inline vec3 cross(const vec3& u, const vec3& v) {
|
||||||
|
return vec3(u.e[1] * v.e[2] - u.e[2] * v.e[1],
|
||||||
|
u.e[2] * v.e[0] - u.e[0] * v.e[2],
|
||||||
|
u.e[0] * v.e[1] - u.e[1] * v.e[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline vec3 unit_vector(const vec3& v) {
|
||||||
|
return v / v.length();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
BIN
no_get_ray.png
Normal file
BIN
no_get_ray.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.4 MiB |
BIN
only_one_sample.png
Normal file
BIN
only_one_sample.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.9 MiB |
135
src/camera.rs
Normal file
135
src/camera.rs
Normal file
|
@ -0,0 +1,135 @@
|
||||||
|
use std::f64::INFINITY;
|
||||||
|
|
||||||
|
use indicatif::ProgressBar;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
color::Color,
|
||||||
|
hittable::{Hittable, HittableList},
|
||||||
|
random_f64,
|
||||||
|
ray::Ray,
|
||||||
|
vec::Vec3,
|
||||||
|
Range,
|
||||||
|
};
|
||||||
|
|
||||||
|
const SAMPLES_PER_PIXEL: usize = 10;
|
||||||
|
const PIXEL_SAMPLES_SCALE: f64 = 1.0 / SAMPLES_PER_PIXEL as f64;
|
||||||
|
const MAX_DEPTH: usize = 20;
|
||||||
|
|
||||||
|
pub struct Camera {
|
||||||
|
world: HittableList,
|
||||||
|
|
||||||
|
width: u32,
|
||||||
|
height: u32,
|
||||||
|
|
||||||
|
pixel_delta_u: Vec3,
|
||||||
|
pixel_delta_v: Vec3,
|
||||||
|
|
||||||
|
center: Vec3,
|
||||||
|
pixel00_loc: Vec3,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Camera {
|
||||||
|
pub fn new(world: HittableList, width: u32, height: u32) -> Self {
|
||||||
|
let aspect_ratio = width as f64 / height as f64;
|
||||||
|
|
||||||
|
let focal_length = 1.0;
|
||||||
|
let viewport_height = 2.0;
|
||||||
|
let viewport_width = viewport_height * aspect_ratio;
|
||||||
|
let camera_center = Vec3(0.0, 0.0, 0.0);
|
||||||
|
|
||||||
|
// left to right
|
||||||
|
let viewport_u = Vec3(viewport_width, 0.0, 0.0);
|
||||||
|
// top to bottom
|
||||||
|
let viewport_v = Vec3(0.0, -viewport_height, 0.0);
|
||||||
|
|
||||||
|
// vector of the size between each pixel on the viewport
|
||||||
|
let pixel_delta_u = viewport_u / width as f64;
|
||||||
|
let pixel_delta_v = viewport_v / height as f64;
|
||||||
|
|
||||||
|
let viewport_upper_left =
|
||||||
|
camera_center - Vec3(0.0, 0.0, focal_length) - (viewport_u / 2.0) - (viewport_v / 2.0);
|
||||||
|
let pixel00_loc = viewport_upper_left + 0.5 * (pixel_delta_u + pixel_delta_v);
|
||||||
|
|
||||||
|
Self {
|
||||||
|
world,
|
||||||
|
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
|
||||||
|
pixel_delta_u,
|
||||||
|
pixel_delta_v,
|
||||||
|
|
||||||
|
center: camera_center,
|
||||||
|
pixel00_loc,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn render(&self) -> Box<[Color]> {
|
||||||
|
let mut buf =
|
||||||
|
vec![Color(0.0, 0.0, 0.0); (self.width * self.height) as usize].into_boxed_slice();
|
||||||
|
|
||||||
|
let bar = ProgressBar::new(
|
||||||
|
(self.height as usize * self.width as usize * SAMPLES_PER_PIXEL) as u64,
|
||||||
|
);
|
||||||
|
|
||||||
|
for y in 0..self.height {
|
||||||
|
for x in 0..self.width {
|
||||||
|
let mut color = Color(0.0, 0.0, 0.0);
|
||||||
|
for _ in 0..SAMPLES_PER_PIXEL {
|
||||||
|
// let r = self.get_ray(x, y);
|
||||||
|
let r = Ray::new(
|
||||||
|
self.center,
|
||||||
|
self.pixel00_loc
|
||||||
|
+ (x as f64 + random_f64()) * self.pixel_delta_u
|
||||||
|
+ (y as f64 + random_f64()) * self.pixel_delta_v
|
||||||
|
- self.center,
|
||||||
|
);
|
||||||
|
color += self.ray_color(r, MAX_DEPTH);
|
||||||
|
|
||||||
|
bar.inc(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
buf[(y * self.width + x) as usize] = color * PIXEL_SAMPLES_SCALE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bar.finish();
|
||||||
|
|
||||||
|
buf
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_ray(&self, x: u32, y: u32) -> Ray {
|
||||||
|
let offset = sample_square();
|
||||||
|
let sample = self.pixel00_loc
|
||||||
|
+ ((x as f64 + offset.0) * self.pixel_delta_u)
|
||||||
|
+ ((y as f64 + offset.1) * self.pixel_delta_v);
|
||||||
|
|
||||||
|
let origin = self.center;
|
||||||
|
let direction = sample - origin;
|
||||||
|
|
||||||
|
Ray::new(origin, direction)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ray_color(&self, r: Ray, depth: usize) -> Color {
|
||||||
|
if depth <= 0 {
|
||||||
|
return Color(0.0, 0.0, 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(hit) = self.world.hit(&r, Range(0.0, INFINITY)) {
|
||||||
|
if let Some((attenuation, ray)) = hit.material.scatter(&r, &hit) {
|
||||||
|
return attenuation * self.ray_color(ray, depth - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Color(0.0, 0.0, 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
let unit_direction = r.direction.unit();
|
||||||
|
let a = 0.5 * (unit_direction.1 + 1.0);
|
||||||
|
|
||||||
|
(1.0 - a) * Color(1.0, 1.0, 1.0) + a * Color(0.5, 0.7, 1.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sample_square() -> Vec3 {
|
||||||
|
Vec3(random_f64() - 0.5, random_f64() - 0.5, 0.0)
|
||||||
|
}
|
104
src/color.rs
Normal file
104
src/color.rs
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
use std::{
|
||||||
|
fmt::Debug,
|
||||||
|
ops::{Add, AddAssign, Mul, Sub},
|
||||||
|
};
|
||||||
|
|
||||||
|
use image::{DynamicImage, GenericImage};
|
||||||
|
|
||||||
|
use crate::{vec::Vec3, Range};
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub struct Color(pub f64, pub f64, pub f64);
|
||||||
|
|
||||||
|
impl Color {
|
||||||
|
pub fn write(&self, i: &mut DynamicImage, x: u32, y: u32) {
|
||||||
|
let mut r = self.0;
|
||||||
|
let mut g = self.1;
|
||||||
|
let mut b = self.2;
|
||||||
|
|
||||||
|
r = Color::linear_to_gamma(r);
|
||||||
|
g = Color::linear_to_gamma(g);
|
||||||
|
b = Color::linear_to_gamma(b);
|
||||||
|
|
||||||
|
let intensity = Range(0.0, 0.999);
|
||||||
|
|
||||||
|
let ir = (255.999 * intensity.clamp(r)) as u8;
|
||||||
|
let ig = (255.999 * intensity.clamp(g)) as u8;
|
||||||
|
let ib = (255.999 * intensity.clamp(b)) as u8;
|
||||||
|
let c = image::Rgba([ir, ig, ib, 255]);
|
||||||
|
|
||||||
|
i.put_pixel(x, y, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn linear_to_gamma(r: f64) -> f64 {
|
||||||
|
if r > 0.0 {
|
||||||
|
r.sqrt()
|
||||||
|
} else {
|
||||||
|
0.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Debug for Color {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "{} {} {}", self.0, self.1, self.2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Add<Color> for Color {
|
||||||
|
type Output = Color;
|
||||||
|
|
||||||
|
fn add(self, rhs: Color) -> Self::Output {
|
||||||
|
Color(self.0 + rhs.0, self.1 + rhs.1, self.2 + rhs.2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AddAssign<Color> for Color {
|
||||||
|
fn add_assign(&mut self, rhs: Color) {
|
||||||
|
*self = *self + rhs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mul<Color> for Color {
|
||||||
|
type Output = Color;
|
||||||
|
|
||||||
|
fn mul(self, rhs: Color) -> Self::Output {
|
||||||
|
Color(self.0 * rhs.0, self.1 * rhs.1, self.2 * rhs.2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mul<f64> for Color {
|
||||||
|
type Output = Color;
|
||||||
|
|
||||||
|
fn mul(self, rhs: f64) -> Self::Output {
|
||||||
|
Color(self.0 * rhs, self.1 * rhs, self.2 * rhs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mul<Color> for f64 {
|
||||||
|
type Output = Color;
|
||||||
|
|
||||||
|
fn mul(self, rhs: Color) -> Self::Output {
|
||||||
|
Color(rhs.0 * self, rhs.1 * self, rhs.2 * self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Sub<Color> for f64 {
|
||||||
|
type Output = Color;
|
||||||
|
|
||||||
|
fn sub(self, rhs: Color) -> Self::Output {
|
||||||
|
Color(rhs.0 - self, rhs.1 - self, rhs.2 - self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Color> for Vec3 {
|
||||||
|
fn from(c: Color) -> Self {
|
||||||
|
Vec3(c.0, c.1, c.2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Vec3> for Color {
|
||||||
|
fn from(v: Vec3) -> Self {
|
||||||
|
Color(v.0, v.1, v.2)
|
||||||
|
}
|
||||||
|
}
|
102
src/hittable.rs
Normal file
102
src/hittable.rs
Normal file
|
@ -0,0 +1,102 @@
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use crate::{material::Material, ray::Ray, vec::Vec3, Range};
|
||||||
|
|
||||||
|
pub struct HitRecord {
|
||||||
|
pub p: Vec3,
|
||||||
|
pub normal: Vec3,
|
||||||
|
pub material: Arc<Box<dyn Material>>,
|
||||||
|
pub t: f64,
|
||||||
|
pub front_face: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HitRecord {
|
||||||
|
pub fn set_face_normal(&mut self, r: &Ray, outward_normal: Vec3) {
|
||||||
|
self.front_face = r.direction.dot(outward_normal) < 0.0;
|
||||||
|
self.normal = if self.front_face {
|
||||||
|
outward_normal
|
||||||
|
} else {
|
||||||
|
-outward_normal
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Hittable {
|
||||||
|
fn hit(&self, r: &Ray, t: Range) -> Option<HitRecord>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Sphere {
|
||||||
|
pub center: Vec3,
|
||||||
|
pub radius: f64,
|
||||||
|
pub material: Arc<Box<dyn Material>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Sphere {
|
||||||
|
pub fn new(center: Vec3, radius: f64, material: Arc<Box<dyn Material>>) -> Self {
|
||||||
|
Self {
|
||||||
|
center,
|
||||||
|
radius,
|
||||||
|
material,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Hittable for Sphere {
|
||||||
|
fn hit(&self, r: &Ray, t: Range) -> Option<HitRecord> {
|
||||||
|
let oc = self.center - r.origin;
|
||||||
|
let a = r.direction.length_squared();
|
||||||
|
let h = r.direction.dot(oc);
|
||||||
|
let c = oc.length_squared() - self.radius * self.radius;
|
||||||
|
|
||||||
|
let discriminant = h * h - a * c;
|
||||||
|
if discriminant < 0.0 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let sq = discriminant.sqrt();
|
||||||
|
|
||||||
|
let mut root = (h - sq) / a;
|
||||||
|
if !t.surrounds(root) {
|
||||||
|
root = (h + sq) / a;
|
||||||
|
if !t.surrounds(root) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut record = HitRecord {
|
||||||
|
t: root,
|
||||||
|
p: r.at(root),
|
||||||
|
material: self.material.clone(),
|
||||||
|
normal: Vec3::default(),
|
||||||
|
front_face: false,
|
||||||
|
};
|
||||||
|
let outward_normal = (record.p - self.center) / self.radius;
|
||||||
|
record.set_face_normal(r, outward_normal);
|
||||||
|
|
||||||
|
Some(record)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct HittableList(pub Vec<Box<dyn Hittable>>);
|
||||||
|
|
||||||
|
impl Hittable for HittableList {
|
||||||
|
fn hit(&self, r: &Ray, t: Range) -> Option<HitRecord> {
|
||||||
|
let mut closest_so_far = t.max();
|
||||||
|
let mut hit_record = None;
|
||||||
|
|
||||||
|
for hittable in self.0.iter() {
|
||||||
|
if let Some(record) = hittable.hit(r, Range(t.min(), closest_so_far)) {
|
||||||
|
closest_so_far = record.t;
|
||||||
|
hit_record = Some(record);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hit_record
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Vec<Box<dyn Hittable>>> for HittableList {
|
||||||
|
fn from(v: Vec<Box<dyn Hittable>>) -> Self {
|
||||||
|
Self(v)
|
||||||
|
}
|
||||||
|
}
|
56
src/lib.rs
Normal file
56
src/lib.rs
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
use rand::{rngs::OsRng, Rng};
|
||||||
|
|
||||||
|
pub mod camera;
|
||||||
|
pub mod color;
|
||||||
|
pub mod hittable;
|
||||||
|
pub mod material;
|
||||||
|
pub mod ray;
|
||||||
|
pub mod vec;
|
||||||
|
|
||||||
|
pub struct Range(pub f64, pub f64);
|
||||||
|
|
||||||
|
impl Range {
|
||||||
|
#[inline]
|
||||||
|
pub fn min(&self) -> f64 {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn max(&self) -> f64 {
|
||||||
|
self.1
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn contains(&self, x: f64) -> bool {
|
||||||
|
self.min() <= x && x <= self.max()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn surrounds(&self, x: f64) -> bool {
|
||||||
|
self.min() < x && x < self.max()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn clamp(&self, x: f64) -> f64 {
|
||||||
|
let min = self.min();
|
||||||
|
let max = self.max();
|
||||||
|
|
||||||
|
if x < min {
|
||||||
|
min
|
||||||
|
} else if x > max {
|
||||||
|
max
|
||||||
|
} else {
|
||||||
|
x
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn random_f64() -> f64 {
|
||||||
|
OsRng.gen::<f64>()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn random_bounded_f64(range: &Range) -> f64 {
|
||||||
|
range.min() + (range.max() - range.min()) * random_f64()
|
||||||
|
}
|
103
src/main.rs
Normal file
103
src/main.rs
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
use std::{fs, sync::Arc};
|
||||||
|
|
||||||
|
use cpu_rt::{
|
||||||
|
camera::Camera,
|
||||||
|
color::Color,
|
||||||
|
hittable::{Hittable, HittableList, Sphere},
|
||||||
|
material::{Lambertian, Metal},
|
||||||
|
vec::Vec3,
|
||||||
|
};
|
||||||
|
use image::ImageFormat;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let width = 1280;
|
||||||
|
let height = 720;
|
||||||
|
|
||||||
|
// for a in -11..11 {
|
||||||
|
// for b in -11..11 {
|
||||||
|
// let choose_mat = random_f64();
|
||||||
|
// let center = Vec3(
|
||||||
|
// a as f64 + 0.9 * random_f64(),
|
||||||
|
// 0.2,
|
||||||
|
// b as f64 + 0.9 * random_f64(),
|
||||||
|
// );
|
||||||
|
|
||||||
|
// if (center - Vec3(4.0, 0.2, 0.0)).length() > 0.9 {
|
||||||
|
// let sphere_material: Box<dyn Material> = if choose_mat < 0.8 {
|
||||||
|
// let albedo: Color = (Vec3::random() * Vec3::random()).into();
|
||||||
|
// Box::new(Lambertian::new(albedo))
|
||||||
|
// } else if choose_mat < 0.95 {
|
||||||
|
// let albedo: Color = Vec3::random_bounded(0.5, 1.0).into();
|
||||||
|
// let fuzz = random_f64() * 0.5;
|
||||||
|
// Box::new(Metal::new(albedo, fuzz))
|
||||||
|
// } else {
|
||||||
|
// Box::new(Lambertian::new(Color(0.5, 0.5, 0.5)))
|
||||||
|
// };
|
||||||
|
|
||||||
|
// let a = Sphere::new(center, 0.2, Arc::new(sphere_material));
|
||||||
|
// let a: Box<dyn Hittable> = Box::new(a);
|
||||||
|
|
||||||
|
// world.push(a);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// let material2 = Lambertian::new(Color(0.4, 0.2, 0.1));
|
||||||
|
// world.push(Box::new(Sphere::new(
|
||||||
|
// Vec3(0.0, 1.0, 0.0),
|
||||||
|
// 1.0,
|
||||||
|
// Arc::new(Box::new(material2)),
|
||||||
|
// )));
|
||||||
|
|
||||||
|
// let material3 = Metal::new(Color(0.7, 0.6, 0.5), 0.0);
|
||||||
|
// world.push(Box::new(Sphere::new(
|
||||||
|
// Vec3(-4.0, 1.0, 0.0),
|
||||||
|
// 1.0,
|
||||||
|
// Arc::new(Box::new(material3)),
|
||||||
|
// )));
|
||||||
|
|
||||||
|
let material_ground = Lambertian::new(Color(0.8, 0.8, 0.0));
|
||||||
|
let material_center = Lambertian::new(Color(0.1, 0.2, 0.5));
|
||||||
|
let material_left = Metal::new(Color(0.8, 0.8, 0.8), 0.3);
|
||||||
|
let material_right = Metal::new(Color(0.8, 0.6, 0.2), 1.0);
|
||||||
|
|
||||||
|
let world: HittableList = vec![
|
||||||
|
Box::new(Sphere {
|
||||||
|
center: Vec3(0.0, -100.5, -1.0),
|
||||||
|
radius: 100.0,
|
||||||
|
material: Arc::new(Box::new(material_ground)),
|
||||||
|
}) as Box<dyn Hittable>,
|
||||||
|
Box::new(Sphere {
|
||||||
|
center: Vec3(0.0, 0.0, -1.2),
|
||||||
|
radius: 0.5,
|
||||||
|
material: Arc::new(Box::new(material_center)),
|
||||||
|
}),
|
||||||
|
Box::new(Sphere {
|
||||||
|
center: Vec3(-1.0, 0.0, -1.0),
|
||||||
|
radius: 0.5,
|
||||||
|
material: Arc::new(Box::new(material_left)),
|
||||||
|
}),
|
||||||
|
Box::new(Sphere {
|
||||||
|
center: Vec3(1.0, 0.0, -1.0),
|
||||||
|
radius: 0.5,
|
||||||
|
material: Arc::new(Box::new(material_right)),
|
||||||
|
}),
|
||||||
|
]
|
||||||
|
.into();
|
||||||
|
|
||||||
|
let c = Camera::new(world.into(), width, height);
|
||||||
|
|
||||||
|
let start = std::time::Instant::now();
|
||||||
|
let buf = c.render();
|
||||||
|
println!("Render time: {:?}", start.elapsed());
|
||||||
|
|
||||||
|
let mut i = image::DynamicImage::new_rgba16(width, height);
|
||||||
|
for (y, row) in buf.chunks(width as usize).enumerate() {
|
||||||
|
for (x, color) in row.iter().enumerate() {
|
||||||
|
color.write(&mut i, x as u32, y as u32);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut f = fs::File::create("test.png").unwrap();
|
||||||
|
i.write_to(&mut f, ImageFormat::Png).unwrap();
|
||||||
|
}
|
54
src/material.rs
Normal file
54
src/material.rs
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
use crate::{color::Color, hittable::HitRecord, ray::Ray, vec::Vec3};
|
||||||
|
|
||||||
|
pub trait Material {
|
||||||
|
fn scatter(&self, r_in: &Ray, rec: &HitRecord) -> Option<(Color, Ray)>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Lambertian {
|
||||||
|
albedo: Color,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Lambertian {
|
||||||
|
pub fn new(albedo: Color) -> Self {
|
||||||
|
Self { albedo }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Material for Lambertian {
|
||||||
|
fn scatter(&self, _r_in: &Ray, rec: &HitRecord) -> Option<(Color, Ray)> {
|
||||||
|
let mut scatter_direction = rec.normal + Vec3::random_unit_vector();
|
||||||
|
if scatter_direction.near_zero() {
|
||||||
|
scatter_direction = rec.normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
let scattered = Ray::new(rec.p, scatter_direction);
|
||||||
|
|
||||||
|
Some((self.albedo, scattered))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Metal {
|
||||||
|
albedo: Color,
|
||||||
|
fuzz: f64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Metal {
|
||||||
|
pub fn new(albedo: Color, fuzz: f64) -> Self {
|
||||||
|
Self { albedo, fuzz }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Material for Metal {
|
||||||
|
fn scatter(&self, r_in: &Ray, rec: &HitRecord) -> Option<(Color, Ray)> {
|
||||||
|
let reflected = r_in.direction.reflect(rec.normal);
|
||||||
|
let reflected = reflected.unit() + (self.fuzz * Vec3::random_unit_vector());
|
||||||
|
|
||||||
|
let scattered = Ray::new(rec.p, reflected);
|
||||||
|
|
||||||
|
if scattered.direction.dot(rec.normal) > 0.0 {
|
||||||
|
Some((self.albedo, scattered))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
16
src/ray.rs
Normal file
16
src/ray.rs
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
use crate::vec::Vec3;
|
||||||
|
|
||||||
|
pub struct Ray {
|
||||||
|
pub origin: Vec3,
|
||||||
|
pub direction: Vec3,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Ray {
|
||||||
|
pub fn new(origin: Vec3, direction: Vec3) -> Ray {
|
||||||
|
Ray { origin, direction }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn at(&self, t: f64) -> Vec3 {
|
||||||
|
self.origin + t * self.direction
|
||||||
|
}
|
||||||
|
}
|
176
src/vec.rs
Normal file
176
src/vec.rs
Normal file
|
@ -0,0 +1,176 @@
|
||||||
|
use std::{
|
||||||
|
fmt::Debug,
|
||||||
|
ops::{Add, Div, Mul, Neg, Sub},
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::{color::Color, random_bounded_f64, random_f64, Range};
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Default)]
|
||||||
|
pub struct Vec3(pub f64, pub f64, pub f64);
|
||||||
|
|
||||||
|
impl Vec3 {
|
||||||
|
#[inline]
|
||||||
|
pub fn length_squared(&self) -> f64 {
|
||||||
|
return self.0 * self.0 + self.1 * self.1 + self.2 * self.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn length(&self) -> f64 {
|
||||||
|
self.length_squared().sqrt()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn unit(&self) -> Vec3 {
|
||||||
|
*self / self.length()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn dot(&self, rhs: Vec3) -> f64 {
|
||||||
|
self.0 * rhs.0 + self.1 * rhs.1 + self.2 * rhs.2
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn near_zero(&self) -> bool {
|
||||||
|
const EPSILON: f64 = 1e-8;
|
||||||
|
self.0.abs() < EPSILON && self.1.abs() < EPSILON && self.2.abs() < EPSILON
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn reflect(&self, n: Vec3) -> Vec3 {
|
||||||
|
*self - 2.0 * self.dot(n) * n
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn random() -> Vec3 {
|
||||||
|
Vec3(random_f64(), random_f64(), random_f64())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn random_bounded(min: f64, max: f64) -> Vec3 {
|
||||||
|
let r = Range(min, max);
|
||||||
|
|
||||||
|
Vec3(
|
||||||
|
random_bounded_f64(&r),
|
||||||
|
random_bounded_f64(&r),
|
||||||
|
random_bounded_f64(&r),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn random_in_unit_sphere() -> Vec3 {
|
||||||
|
loop {
|
||||||
|
let p = Vec3::random_bounded(-1.0, 1.0);
|
||||||
|
if p.length_squared() < 1.0 {
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn random_unit_vector() -> Vec3 {
|
||||||
|
Vec3::random_in_unit_sphere().unit()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn random_in_hemisphere(normal: Vec3) -> Vec3 {
|
||||||
|
let in_unit_sphere = Vec3::random_in_unit_sphere();
|
||||||
|
if in_unit_sphere.dot(normal) > 0.0 {
|
||||||
|
in_unit_sphere
|
||||||
|
} else {
|
||||||
|
-in_unit_sphere
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Debug for Vec3 {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "{} {} {}", self.0, self.1, self.2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Add<Vec3> for Vec3 {
|
||||||
|
type Output = Vec3;
|
||||||
|
|
||||||
|
fn add(self, rhs: Vec3) -> Self::Output {
|
||||||
|
Vec3(self.0 + rhs.0, self.1 + rhs.1, self.2 + rhs.2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Add<f64> for Vec3 {
|
||||||
|
type Output = Vec3;
|
||||||
|
|
||||||
|
fn add(self, rhs: f64) -> Self::Output {
|
||||||
|
Vec3(self.0 + rhs, self.1 + rhs, self.2 + rhs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Add<Color> for Vec3 {
|
||||||
|
type Output = Color;
|
||||||
|
|
||||||
|
fn add(self, rhs: Color) -> Self::Output {
|
||||||
|
Color(self.0 + rhs.0, self.1 + rhs.1, self.2 + rhs.2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Sub<Vec3> for Vec3 {
|
||||||
|
type Output = Vec3;
|
||||||
|
|
||||||
|
fn sub(self, rhs: Vec3) -> Self::Output {
|
||||||
|
Vec3(self.0 - rhs.0, self.1 - rhs.1, self.2 - rhs.2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Sub<Vec3> for f64 {
|
||||||
|
type Output = Vec3;
|
||||||
|
|
||||||
|
fn sub(self, rhs: Vec3) -> Self::Output {
|
||||||
|
Vec3(self - rhs.0, self - rhs.1, self - rhs.2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mul<Vec3> for Vec3 {
|
||||||
|
type Output = Vec3;
|
||||||
|
|
||||||
|
fn mul(self, rhs: Vec3) -> Self::Output {
|
||||||
|
Vec3(self.0 * rhs.0, self.1 * rhs.1, self.2 * rhs.2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mul<f64> for Vec3 {
|
||||||
|
type Output = Vec3;
|
||||||
|
|
||||||
|
fn mul(self, rhs: f64) -> Self::Output {
|
||||||
|
Vec3(self.0 * rhs, self.1 * rhs, self.2 * rhs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mul<Vec3> for f64 {
|
||||||
|
type Output = Vec3;
|
||||||
|
|
||||||
|
fn mul(self, rhs: Vec3) -> Self::Output {
|
||||||
|
Vec3(self * rhs.0, self * rhs.1, self * rhs.2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Div<f64> for Vec3 {
|
||||||
|
type Output = Vec3;
|
||||||
|
|
||||||
|
fn div(self, rhs: f64) -> Self::Output {
|
||||||
|
Vec3(self.0 / rhs, self.1 / rhs, self.2 / rhs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mul<Color> for Vec3 {
|
||||||
|
type Output = Color;
|
||||||
|
|
||||||
|
fn mul(self, rhs: Color) -> Self::Output {
|
||||||
|
Color(self.0 * rhs.0, self.1 * rhs.1, self.2 * rhs.2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Neg for Vec3 {
|
||||||
|
type Output = Vec3;
|
||||||
|
|
||||||
|
fn neg(self) -> Self::Output {
|
||||||
|
Vec3(-self.0, -self.1, -self.2)
|
||||||
|
}
|
||||||
|
}
|
BIN
test.png
Normal file
BIN
test.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 660 KiB |
Loading…
Reference in a new issue