|
|
@@ -1,18 +1,63 @@ |
|
|
|
extern crate image; |
|
|
|
|
|
|
|
|
|
|
|
mod vec3; |
|
|
|
use vec3::Vec3; |
|
|
|
use image; |
|
|
|
use vec3::{Vec3, Ray}; |
|
|
|
|
|
|
|
/// Given a filename, pixel data, and bounds, render a PNG image. |
|
|
|
|
|
|
|
fn encode_png(filename: &str, pixels: &[Vec3], bounds: (usize, usize)) { |
|
|
|
let mut imgbuf = |
|
|
|
image::ImageBuffer::new(bounds.0 as u32, bounds.1 as u32); |
|
|
|
for (x, y, pixel) in imgbuf.enumerate_pixels_mut() { |
|
|
|
let (x, y) = (x as usize, y as usize); |
|
|
|
*pixel = pixels[y*bounds.0 + x].to_rgb().unwrap(); |
|
|
|
} |
|
|
|
let imgbuf = image::imageops::flip_vertical(&imgbuf); |
|
|
|
imgbuf.save(filename).unwrap(); |
|
|
|
} |
|
|
|
|
|
|
|
fn main() { |
|
|
|
let imgx = 320; |
|
|
|
let imgy = 240; |
|
|
|
let imgx = 600; |
|
|
|
let imgy = 450; |
|
|
|
|
|
|
|
let mut imgbuf = image::ImageBuffer::new(imgx, imgy); |
|
|
|
let mut pixels = vec![Vec3(0.0, 0.0, 0.0); imgx*imgy]; |
|
|
|
|
|
|
|
for (x, y, pixel) in imgbuf.enumerate_pixels_mut() { |
|
|
|
let v = Vec3::new(x as f32 / imgx as f32, y as f32 / imgy as f32, 0.4); |
|
|
|
*pixel = v.to_rgb().unwrap(); |
|
|
|
let lower_left = Vec3(-2.0, -1.5, -1.0); |
|
|
|
let horizontal = Vec3(4.0, 0.0, 0.0); |
|
|
|
let vertical = Vec3(0.0, 3.0, 0.0); |
|
|
|
let origin = Vec3(0.0, 0.0, 0.0); |
|
|
|
for i in 0..imgx*imgy { |
|
|
|
let u = (i % imgx) as f32 / imgx as f32; |
|
|
|
let v = (i / imgx) as f32 / imgy as f32; |
|
|
|
let r = Ray{origin, direction: lower_left + u*horizontal + v*vertical}; |
|
|
|
pixels[i] = colour(r); |
|
|
|
} |
|
|
|
encode_png("hello.png", &pixels, (imgx, imgy)); |
|
|
|
} |
|
|
|
|
|
|
|
imgbuf.save("hello.png").unwrap(); |
|
|
|
fn colour(r: Ray) -> Vec3 { |
|
|
|
let t = hit_sphere(Vec3(0.0,0.0,-1.0), 0.5, r); |
|
|
|
if t > 0.0 { |
|
|
|
let normal = (r.point_at(t) - Vec3(0.0, 0.0, -1.0)).unit(); |
|
|
|
0.5*Vec3(normal.0+1.0, normal.1+1.0,normal.2+1.0) |
|
|
|
} else { |
|
|
|
let u_direction = r.direction.unit(); |
|
|
|
let t = 0.5 * (u_direction.1 + 1.0); |
|
|
|
(1.0 - t) * Vec3(1.0, 1.0, 1.0) + t * Vec3(0.5, 0.7, 1.0) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
fn hit_sphere(center: Vec3, radius: f32, r: Ray) -> f32 { |
|
|
|
let oc = r.origin - center; |
|
|
|
let a = r.direction.squared_length(); |
|
|
|
let b = 2.0 * oc.dot(r.direction); |
|
|
|
let c = oc.squared_length() - radius*radius; |
|
|
|
let disc = b*b - 4.0*a*c; |
|
|
|
if disc < 0.0 { |
|
|
|
-1.0 |
|
|
|
} else { |
|
|
|
(-b - disc.sqrt()) / (2.0 * a) |
|
|
|
} |
|
|
|
|
|
|
|
} |