You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

253 lines
6.3 KiB

extern crate rand;
use std::cmp::max;
use std::f32::consts::PI;
use std::io::Cursor;
use std::ops;
use std::ops::Rem;
use image::ImageFormat;
use image::io::Reader as ImageReader;
use rand::prelude::*;
struct Circle {
x: i32,
y: i32,
r: i32,
}
impl Circle {}
fn createCircle() -> Circle {
Circle {
x: 0,
y: 0,
r: 0,
}
}
#[derive(Clone, Copy, Debug, Default)]
struct Vector2f { x: f32, y: f32 }
#[derive(Clone, Copy, Debug, Default)]
struct Vector2i { x: i32, y: i32 }
#[derive(Clone, Copy, Debug, Default)]
struct Vector3f { x: f32, y: f32, z: f32 }
#[derive(Clone, Copy, Debug, Default)]
struct Vector4f { x: f32, y: f32, z: f32, w: f32 }
fn dot(vec_a: Vector3f, vec_b: Vector3f) -> f32 {
vec_a.x * vec_b.x + vec_a.y * vec_b.y + vec_a.z * vec_b.z
}
fn mult(vec: Vector3f, scalar: f32) -> Vector3f {
Vector3f {
x: vec.x * scalar,
y: vec.y * scalar,
z: vec.z * scalar,
}
}
fn sub(vec_a: Vector3f, vec_b: Vector3f) -> Vector3f {
Vector3f {
x: vec_a.x - vec_b.x,
y: vec_a.y - vec_b.y,
z: vec_a.z - vec_b.z,
}
}
fn add(vec_a: Vector3f, vec_b: Vector3f) -> Vector3f {
Vector3f {
x: vec_a.x + vec_b.x,
y: vec_a.y + vec_b.y,
z: vec_a.z + vec_b.z,
}
}
fn mix(a: Vector3f, b: Vector3f, mixValue: f32) -> Vector3f
{
add(mult(a, (1.0 - mixValue)), mult(b, mixValue))
}
fn normalize(ray: Vector3f) -> Vector3f {
let multiplier = (ray.x * ray.x + ray.y * ray.y + ray.z * ray.z).sqrt();
Vector3f {
x: ray.x / multiplier,
y: ray.y / multiplier,
z: ray.z / multiplier,
}
}
fn intersect(ray_orig: Vector3f, ray_dir: Vector3f, sphere_center: Vector3f, sphere_radius: f32) -> Option<f32> {
let mut t0 = 0.0;
let mut t1 = 0.0;
let L = sub(ray_orig, sphere_center);
let a = dot(ray_dir, ray_dir);
let b = 2.0 * dot(L, ray_dir);
let c = dot(L, L) - sphere_radius;
solve_quadratic(a, b, c, t0, t1)
}
fn solve_quadratic(a: f32, b: f32, c: f32, mut x0: f32, mut x1: f32) -> Option<f32>
{
let discr: f32 = b * b - 4.0 * a * c;
if (discr < 0.0) {
return None;
} else if (discr == 0.0) {
x0 = -0.5 * b / a;
x1 = x0;
} else {
let q = if b > 0.0 {
-0.5 * (b + discr.sqrt())
} else {
-0.5 * (b - discr.sqrt())
};
x0 = q / a;
x1 = c / q;
}
match f32::max(x0, x1) > 0.0 {
true => { Some(f32::max(x0, x1)) }
false => { None }
}
}
fn get_surface_data(phit: Vector3f, nhit: Vector3f, sphere_center: Vector3f) -> Vector2f
{
let nhit = sub(phit, sphere_center);
let nhit = normalize(nhit);
// In this particular case, the normal is simular to a point on a unit sphere
// centred around the origin. We can thus use the normal coordinates to compute
// the spherical coordinates of Phit.
// atan2 returns a value in the range [-pi, pi] and we need to remap it to range [0, 1]
// acosf returns a value in the range [0, pi] and we also need to remap it to the range [0, 1]
Vector2f {
x: (1.0 + (nhit.x).atan2(nhit.z) / PI) * 0.5,
y: (nhit.y).acos() / PI,
}
}
fn main() {
let mut rng = rand::thread_rng();
let view_res = Vector2i { x: 800, y: 800 };
let mut circles = Vec::new();
for i in 0..100 {
circles.push(Circle {
x: (rng.gen::<f32>() * view_res.x as f32) as i32,
y: (rng.gen::<f32>() * view_res.y as f32) as i32,
r: 10,
});
}
let mut viewport_matrix = vec![Vector3f {
x: 0.0,
y: 0.0,
z: 0.0,
}; (view_res.x as usize * view_res.y as usize * 4)];
for y in (-view_res.y / 2)..(view_res.y / 2) {
for x in (-view_res.x / 2)..(view_res.x / 2) {
let ray = Vector3f { z: -800.0, x: x as f32, y: y as f32 };
let ray = Vector3f {
x: (ray.z * (1.57 as f32).sin() + ray.x * (1.57 as f32).cos()),
y: (ray.y),
z: (ray.z * (1.57 as f32).cos() - ray.x * (1.57 as f32).sin()),
};
let ray = normalize(ray);
let index = ((x + view_res.x / 2) + view_res.x * (y + view_res.y / 2)) as usize;
viewport_matrix[index] = Vector3f {
x: ray.x,
y: ray.y,
z: ray.z,
};
}
}
// Create a new ImgBuf with width: imgx and height: imgy
let mut imgbuf = image::ImageBuffer::new(view_res.x as u32, view_res.y as u32);
let sphere_center = Vector3f { x: -70.0, y: 20.0, z: -10.0 };
let sphere_color = Vector3f {
x: 78.0,
y: 22.0,
z: 125.0,
};
let radius = 10.0;
// Iterate over the coordinates and pixels of the image
for (x, y, pixel) in imgbuf.enumerate_pixels_mut() {
let index = (x + view_res.x as u32 * y) as usize;
let orig = Vector3f {
x: 0.0,
y: 0.0,
z: 0.0,
};
let dir = viewport_matrix[index];
match intersect(orig, dir, sphere_center, radius) {
None => {}
Some(t) => {
let phit = add(orig, mult(dir, t));
let nhit = Vector3f::default();
let tex = get_surface_data(phit, nhit, sphere_center);
let scale = 4.0;
let pattern = match ((tex.x * scale).rem(1.0) > 0.5) ^ ((tex.y * scale).rem(1.0) > 0.5) {
true => { 1.0 }
false => { 0.0 }
};
let ndir = Vector3f {
x: -dir.x,
y: -dir.y,
z: -dir.z,
};
let hit_color = mult(mix(sphere_color, mult(sphere_color, 0.8), f32::max(0.0, dot(nhit, ndir))), pattern);
*pixel = image::Rgb([hit_color.x as u8, hit_color.y as u8, hit_color.z as u8]);
}
};
}
// A redundant loop to demonstrate reading image data
// for x in 0..view_res.x {
// for y in 0..view_res.y {
// let pixel = imgbuf.get_pixel_mut(x as u32, y as u32);
// let data: image::Rgb<u8> = *pixel; // read the prexisting data if needed
// *pixel = image::Rgb([0, 0, 255]);
// }
// }
// Save the image as “fractal.png”, the format is deduced from the path
imgbuf.save("fractal.png").unwrap();
}