Compare commits
9 commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b9ac41b453 | |||
| 8df48d3824 | |||
| 5857244ca8 | |||
| 63d4cbb042 | |||
| b73b1209e4 | |||
| 33f3c5fab1 | |||
| 74db91b1ae | |||
| 4c9a1ba336 | |||
| e85f429705 |
8 changed files with 93 additions and 91 deletions
20
.gitea/workflows/ci.yml
Normal file
20
.gitea/workflows/ci.yml
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
name: CI
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
pull_request:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Install Rust
|
||||||
|
uses: dtolnay/rust-toolchain@stable
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: cargo build --release --verbose
|
||||||
|
|
||||||
|
- name: Run tests
|
||||||
|
run: cargo test --verbose
|
||||||
26
.gitea/workflows/image.yml
Normal file
26
.gitea/workflows/image.yml
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
name: Render Image
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
render:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Install Rust
|
||||||
|
uses: dtolnay/rust-toolchain@stable
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: cargo build --release
|
||||||
|
|
||||||
|
- name: Run ray tracer
|
||||||
|
run: ./target/release/rust-ray
|
||||||
|
|
||||||
|
- name: Upload image
|
||||||
|
uses: https://github.com/christopherHX/gitea-upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: rendered-image
|
||||||
|
path: foo.ppm
|
||||||
52
Cargo.lock
generated
52
Cargo.lock
generated
|
|
@ -40,37 +40,6 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "crossbeam-deque"
|
|
||||||
version = "0.8.6"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51"
|
|
||||||
dependencies = [
|
|
||||||
"crossbeam-epoch",
|
|
||||||
"crossbeam-utils",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "crossbeam-epoch"
|
|
||||||
version = "0.9.18"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
|
|
||||||
dependencies = [
|
|
||||||
"crossbeam-utils",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "crossbeam-utils"
|
|
||||||
version = "0.8.21"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "either"
|
|
||||||
version = "1.15.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "equivalent"
|
name = "equivalent"
|
||||||
version = "1.0.2"
|
version = "1.0.2"
|
||||||
|
|
@ -217,32 +186,11 @@ version = "0.10.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0c8d0fd677905edcbeedbf2edb6494d676f0e98d54d5cf9bda0b061cb8fb8aba"
|
checksum = "0c8d0fd677905edcbeedbf2edb6494d676f0e98d54d5cf9bda0b061cb8fb8aba"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rayon"
|
|
||||||
version = "1.11.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f"
|
|
||||||
dependencies = [
|
|
||||||
"either",
|
|
||||||
"rayon-core",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rayon-core"
|
|
||||||
version = "1.13.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91"
|
|
||||||
dependencies = [
|
|
||||||
"crossbeam-deque",
|
|
||||||
"crossbeam-utils",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rust-ray"
|
name = "rust-ray"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"rand",
|
"rand",
|
||||||
"rayon",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
|
||||||
|
|
@ -5,4 +5,3 @@ edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
rand = "0.10.0"
|
rand = "0.10.0"
|
||||||
rayon = "1.11.0"
|
|
||||||
|
|
|
||||||
16
src/main.rs
16
src/main.rs
|
|
@ -9,31 +9,27 @@ use render::{World,Camera};
|
||||||
use vec3::{Point3,Vec3};
|
use vec3::{Point3,Vec3};
|
||||||
|
|
||||||
use material::{Metal,Lambertian,Material};
|
use material::{Metal,Lambertian,Material};
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let aspect_ratio = 16.0 / 9.0;
|
let aspect_ratio = 16.0 / 9.0;
|
||||||
let image_width = 800;
|
let image_width = 1920;
|
||||||
let focal_length = 1.0;
|
let focal_length = 1.0;
|
||||||
let viewport_height = 2.0;
|
let viewport_height = 2.0;
|
||||||
let camera_center = Point3::new(0.0, 0.0, 0.0);
|
|
||||||
|
|
||||||
|
|
||||||
let m = Lambertian::new(Vec3::new(0.5,0.5,0.5));
|
let m = Lambertian::new(Vec3::new(0.5,0.5,0.5));
|
||||||
let world_m: Arc <dyn Material> = Arc::new(m);
|
let world_m: Box <dyn Material> = Box::new(m);
|
||||||
|
|
||||||
let m = Lambertian::new(Vec3::new(0.1, 0.2, 0.5));
|
let m = Lambertian::new(Vec3::new(0.1, 0.2, 0.5));
|
||||||
let center_m: Arc <dyn Material> = Arc::new(m);
|
let center_m: Box <dyn Material> = Box::new(m);
|
||||||
|
|
||||||
let m = Metal::new(Vec3::new(0.8, 0.8, 0.8), 0.3);
|
let m = Metal::new(Vec3::new(0.8, 0.8, 0.8), 0.3);
|
||||||
let left_m: Arc <dyn Material> = Arc::new(m);
|
let left_m: Box <dyn Material> = Box::new(m);
|
||||||
|
|
||||||
|
|
||||||
let m = Metal::new(Vec3::new(0.8, 0.6, 0.2), 0.5);
|
let m = Metal::new(Vec3::new(0.8, 0.6, 0.2), 0.5);
|
||||||
let right_m: Arc <dyn Material> = Arc::new(m);
|
let right_m: Box <dyn Material> = Box::new(m);
|
||||||
|
|
||||||
let mut camera = Camera::new(aspect_ratio,image_width,focal_length,viewport_height,camera_center);
|
let mut camera = Camera::new(aspect_ratio,image_width,focal_length,Point3::new(-1.0,1.0,1.0),Point3::new(0.0,0.0,-1.0),90.0);
|
||||||
let mut world = World::new();
|
let mut world = World::new();
|
||||||
world.add(Sphere::new(0.5, Point3::new(0.0, 0.0, -1.0),center_m));
|
world.add(Sphere::new(0.5, Point3::new(0.0, 0.0, -1.0),center_m));
|
||||||
world.add(Sphere::new(0.5, Point3::new(-1.0, 0.0, -1.0),left_m));
|
world.add(Sphere::new(0.5, Point3::new(-1.0, 0.0, -1.0),left_m));
|
||||||
|
|
|
||||||
|
|
@ -2,22 +2,21 @@ use crate::vec3::{Point3, Ray, Vec3, Interval};
|
||||||
use crate::material::{Material,Scatter};
|
use crate::material::{Material,Scatter};
|
||||||
|
|
||||||
use rand::prelude::*;
|
use rand::prelude::*;
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
|
|
||||||
pub trait Hittable: Send + Sync{
|
pub trait Hittable: Send + Sync{
|
||||||
fn hit(&self, ray: Ray, interval:Interval) -> Option<HitRecord>;
|
fn hit(&self, ray: Ray, interval:Interval) -> Option<HitRecord<'_>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct HitRecord {
|
pub struct HitRecord<'a> {
|
||||||
point: Point3,
|
point: Point3,
|
||||||
normal: Vec3,
|
normal: Vec3,
|
||||||
root: f64,
|
root: f64,
|
||||||
front_face: bool,
|
front_face: bool,
|
||||||
material: Arc<dyn Material>,
|
material: &'a dyn Material,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HitRecord {
|
impl HitRecord <'_> {
|
||||||
pub fn normal(&self) -> Vec3 {
|
pub fn normal(&self) -> Vec3 {
|
||||||
self.normal
|
self.normal
|
||||||
}
|
}
|
||||||
|
|
@ -44,17 +43,17 @@ impl HitRecord {
|
||||||
pub struct Sphere {
|
pub struct Sphere {
|
||||||
radius: f64,
|
radius: f64,
|
||||||
center: Point3,
|
center: Point3,
|
||||||
material: Arc<dyn Material>
|
material: Box<dyn Material>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Sphere {
|
impl Sphere {
|
||||||
pub fn new(radius: f64, center: Point3, material: Arc<dyn Material>) -> Self {
|
pub fn new(radius: f64, center: Point3, material: Box<dyn Material>) -> Self {
|
||||||
Sphere { radius, center , material}
|
Sphere { radius, center , material}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Hittable for Sphere {
|
impl Hittable for Sphere {
|
||||||
fn hit(&self, ray: Ray, interval:Interval) -> Option<HitRecord> {
|
fn hit(&self, ray: Ray, interval:Interval) -> Option<HitRecord<'_>> {
|
||||||
let oc = self.center - ray.origin();
|
let oc = self.center - ray.origin();
|
||||||
let a = ray.direction().length_squared();
|
let a = ray.direction().length_squared();
|
||||||
let h = Vec3::dot(ray.direction(), oc.to_vec());
|
let h = Vec3::dot(ray.direction(), oc.to_vec());
|
||||||
|
|
@ -80,7 +79,7 @@ impl Hittable for Sphere {
|
||||||
point,
|
point,
|
||||||
normal,
|
normal,
|
||||||
front_face: false,
|
front_face: false,
|
||||||
material: self.material.clone()
|
material: self.material.as_ref(),
|
||||||
};
|
};
|
||||||
tmp.set_face_normal(ray, normal);
|
tmp.set_face_normal(ray, normal);
|
||||||
Some(tmp)
|
Some(tmp)
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ impl World {
|
||||||
self.objects.push(Arc::new(object));
|
self.objects.push(Arc::new(object));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn hit(&self, ray: Ray, interval:Interval) -> Option<HitRecord> {
|
pub fn hit(&self, ray: Ray, interval:Interval) -> Option<HitRecord<'_>> {
|
||||||
let mut hit_anything = None;
|
let mut hit_anything = None;
|
||||||
let mut closest_so_far = interval.max();
|
let mut closest_so_far = interval.max();
|
||||||
|
|
||||||
|
|
@ -46,27 +46,34 @@ pub struct Camera {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Camera {
|
impl Camera {
|
||||||
pub fn new(aspect_ratio:f64,image_width: i32, focal_length:f64, viewport_height:f64, camera_center: Point3) -> Self{
|
pub fn new(aspect_ratio:f64,image_width: i32, focal_length:f64, look_from: Point3, look_at: Point3,fov:f64) -> Self{
|
||||||
|
|
||||||
let image_height = (image_width as f64 / aspect_ratio) as i32;
|
let image_height = (image_width as f64 / aspect_ratio) as i32;
|
||||||
|
|
||||||
|
|
||||||
|
let theta = (fov as f64).to_radians();
|
||||||
|
let h = (theta/2.0).tan();
|
||||||
|
|
||||||
|
let viewport_height = 2.0 * h * focal_length;
|
||||||
let viewport_width = viewport_height * (image_width as f64 / image_height as f64);
|
let viewport_width = viewport_height * (image_width as f64 / image_height as f64);
|
||||||
|
|
||||||
let viewport_u = Vec3::new(viewport_width as f64, 0.0, 0.0);
|
let w = (look_from - look_at).to_vec().unit();
|
||||||
let viewport_v = Vec3::new(0.0, -viewport_height as f64, 0.0);
|
let u = Vec3::new(0.0,1.0,0.0).cross(w).unit();
|
||||||
|
let v = w.cross(u);
|
||||||
|
|
||||||
|
|
||||||
|
let viewport_u = u * viewport_width;
|
||||||
|
let viewport_v = (v*-1.0)*viewport_height;
|
||||||
|
|
||||||
let pixel_delta_u = viewport_u / image_width as f64;
|
let pixel_delta_u = viewport_u / image_width as f64;
|
||||||
let pixel_delta_v = viewport_v / image_height as f64;
|
let pixel_delta_v = viewport_v / image_height as f64;
|
||||||
|
|
||||||
let viewport_upper_left = (camera_center.to_vec()
|
let viewport_upper_left = look_from.to_vec() - (w * focal_length) - viewport_u/2.0 - viewport_v/2.0;
|
||||||
- Vec3::new(0.0, 0.0, focal_length)
|
|
||||||
- viewport_u / 2.0
|
|
||||||
- viewport_v / 2.0).to_point();
|
|
||||||
|
|
||||||
let pixel00_loc = viewport_upper_left + ((pixel_delta_u + pixel_delta_v) * 0.5).to_point();
|
let pixel00_loc = (viewport_upper_left + ((pixel_delta_u + pixel_delta_v) * 0.5)).to_point();
|
||||||
|
|
||||||
let img = Image::new(image_width, image_height);
|
let img = Image::new(image_width, image_height);
|
||||||
Self{img,pixel00_loc,camera_center,pixel_delta_u,pixel_delta_v}
|
Self{img,pixel00_loc,camera_center:look_from,pixel_delta_u,pixel_delta_v}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -75,7 +82,7 @@ impl Camera {
|
||||||
let height = self.img.height;
|
let height = self.img.height;
|
||||||
let width = self.img.width;
|
let width = self.img.width;
|
||||||
let total_pixels = height*width;
|
let total_pixels = height*width;
|
||||||
let num_samples = 5;
|
let num_samples = 25;
|
||||||
|
|
||||||
//cast to const ref
|
//cast to const ref
|
||||||
let const_self = &*self;
|
let const_self = &*self;
|
||||||
|
|
@ -90,8 +97,8 @@ impl Camera {
|
||||||
let x = i%width;
|
let x = i%width;
|
||||||
let y = i/width;
|
let y = i/width;
|
||||||
let (mut r,mut g,mut b) = (0.0,0.0,0.0);
|
let (mut r,mut g,mut b) = (0.0,0.0,0.0);
|
||||||
let ray = const_self.get_ray(x,y,&mut rng);
|
|
||||||
for _ in 0..num_samples{
|
for _ in 0..num_samples{
|
||||||
|
let ray = const_self.get_ray(x,y,&mut rng);
|
||||||
let (tr,tg,tb) = trace(ray, &world,10,&mut rng);
|
let (tr,tg,tb) = trace(ray, &world,10,&mut rng);
|
||||||
r+=tr;
|
r+=tr;
|
||||||
g+=tg;
|
g+=tg;
|
||||||
|
|
@ -115,9 +122,12 @@ impl Camera {
|
||||||
for t in threads {
|
for t in threads {
|
||||||
for c in t {
|
for c in t {
|
||||||
let ((x,y),rgb) = c;
|
let ((x,y),rgb) = c;
|
||||||
let px = self.img.get_pixel(x,y).unwrap();
|
if let Some(px) = self.img.get_pixel(x,y){
|
||||||
let (r,g,b)= rgb;
|
let (r,g,b)= rgb;
|
||||||
px.set_color(r,g,b);
|
px.set_color(r,g,b);
|
||||||
|
} else {
|
||||||
|
eprintln!("Invalid pixel: ({} {})",x,y);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.img.save("./foo.ppm").unwrap();
|
self.img.save("./foo.ppm").unwrap();
|
||||||
|
|
|
||||||
12
src/vec3.rs
12
src/vec3.rs
|
|
@ -1,5 +1,6 @@
|
||||||
use std::ops::{Add, Div, Mul, Sub};
|
use std::ops::{Add, Div, Mul, Sub};
|
||||||
|
|
||||||
|
#[repr(align(32))]
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub struct Vec3 {
|
pub struct Vec3 {
|
||||||
x: f64,
|
x: f64,
|
||||||
|
|
@ -7,6 +8,7 @@ pub struct Vec3 {
|
||||||
z: f64,
|
z: f64,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[repr(align(32))]
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub struct Point3 {
|
pub struct Point3 {
|
||||||
x: f64,
|
x: f64,
|
||||||
|
|
@ -132,6 +134,7 @@ impl Vec3 {
|
||||||
z: self.z,
|
z: self.z,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn dot(self, other: Vec3) -> f64 {
|
pub fn dot(self, other: Vec3) -> f64 {
|
||||||
self.x * other.x + self.y * other.y + self.z * other.z
|
self.x * other.x + self.y * other.y + self.z * other.z
|
||||||
}
|
}
|
||||||
|
|
@ -149,10 +152,11 @@ impl Vec3 {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn cross(self, other: Vec3) -> Vec3 {
|
pub fn cross(self, other: Vec3) -> Vec3 {
|
||||||
let x = self.y * other.z - self.z * other.y;
|
Vec3{
|
||||||
let y = self.z * other.x - self.x * other.z;
|
x: self.y * other.z - self.z * other.y,
|
||||||
let z = self.x * other.y - self.y * other.x;
|
y: self.z * other.x - self.x * other.z,
|
||||||
Vec3 { x, y, z }
|
z: self.x * other.y - self.y * other.x,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn length_squared(self) -> f64 {
|
pub fn length_squared(self) -> f64 {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue