This commit is contained in:
DevMiner 2024-04-20 21:11:35 +02:00
commit 0b528c5c4f
21 changed files with 92121 additions and 0 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
target

1157
Cargo.lock generated Normal file

File diff suppressed because it is too large Load diff

9
Cargo.toml Normal file
View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 MiB

BIN
cpp/a.out Executable file

Binary file not shown.

24
cpp/color.h Normal file
View 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
View 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
View 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

File diff suppressed because it is too large Load diff

101
cpp/vec3.h Normal file
View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 MiB

BIN
only_one_sample.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 MiB

135
src/camera.rs Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 660 KiB