Implementation of part 1. The green circle.

This commit is contained in:
2018-08-03 21:56:09 -07:00
parent ca3aa6c8b7
commit 277c6ef60b
9 changed files with 542 additions and 0 deletions

60
bheisler/src/lib.rs Normal file
View File

@@ -0,0 +1,60 @@
extern crate image;
pub mod point;
pub mod rendering;
pub mod scene;
pub mod vector;
use image::GenericImage;
use image::Pixel;
use rendering::Intersectable;
use scene::Scene;
pub fn render(scene: &Scene) -> image::DynamicImage {
let mut image = image::DynamicImage::new_rgb8(scene.width, scene.height);
let black = image::Rgba::from_channels(0, 0, 0, 0);
for x in 0..scene.width {
for y in 0..scene.height {
let ray = rendering::Ray::create_prime(x, y, scene);
if scene.sphere.intersect(&ray) {
let c = &scene.sphere.color;
image.put_pixel(x, y, image::Rgba(c.to_rgba()))
} else {
image.put_pixel(x, y, black);
}
}
}
image
}
#[test]
fn test_can_render_scene() {
use point::Point;
use rendering::Sphere;
use scene::Color;
let scene = Scene {
width: 800,
height: 600,
fov: 90.0,
sphere: Sphere {
center: Point {
x: 0.0,
y: 0.0,
z: -5.0,
},
radius: 1.0,
color: Color {
red: 0.4,
green: 1.0,
blue: 0.4,
},
},
};
let img: image::DynamicImage = render(&scene);
assert_eq!(scene.width, img.width());
assert_eq!(scene.height, img.height());
img.save("/tmp/bheisler.png").unwrap();
}

3
bheisler/src/main.rs Normal file
View File

@@ -0,0 +1,3 @@
fn main() {
println!("Hello, world!");
}

30
bheisler/src/point.rs Normal file
View File

@@ -0,0 +1,30 @@
use std::ops::Sub;
#[derive(Clone, Copy)]
pub struct Point {
pub x: f32,
pub y: f32,
pub z: f32,
}
impl Point {
pub fn zero() -> Point {
Point {
x: 0.,
y: 0.,
z: 0.,
}
}
}
impl Sub for Point {
type Output = Point;
fn sub(self, other: Point) -> Point {
Point {
x: self.x - other.x,
y: self.y - other.y,
z: self.z - other.z,
}
}
}

53
bheisler/src/rendering.rs Normal file
View File

@@ -0,0 +1,53 @@
use point::Point;
use scene::Color;
use scene::Scene;
use vector::Vector3;
pub trait Intersectable {
fn intersect(&self, ray: &Ray) -> bool;
}
pub struct Sphere {
pub center: Point,
pub radius: f32,
pub color: Color,
}
impl Intersectable for Sphere {
fn intersect(&self, ray: &Ray) -> bool {
//Create a line segment between the ray origin and the center of the sphere
let l: Vector3 = (self.center - ray.origin).into();
//Use l as a hypotenuse and find the length of the adjacent side
let adj2 = l.dot(&ray.direction);
//Find the length-squared of the opposite side
//This is equivalent to (but faster than) (l.length() * l.length()) - (adj2 * adj2)
let d2 = l.dot(&l) - (adj2 * adj2);
//If that length-squared is less than radius squared, the ray intersects the sphere
d2 < (self.radius * self.radius)
}
}
pub struct Ray {
pub origin: Point,
pub direction: Vector3,
}
impl Ray {
pub fn create_prime(x: u32, y: u32, scene: &Scene) -> Ray {
assert!(scene.width > scene.height);
let fov_adjustment = (scene.fov.to_radians() / 2.0).tan();
let aspect_ratio = (scene.width as f32) / (scene.height as f32);
let sensor_x =
((((x as f32 + 0.5) / scene.width as f32) * 2.0 - 1.0) * aspect_ratio) * fov_adjustment;
let sensor_y = (1.0 - ((y as f32 + 0.5) / scene.height as f32) * 2.0) * fov_adjustment;
Ray {
origin: Point::zero(),
direction: Vector3 {
x: sensor_x,
y: sensor_y,
z: -1.0,
}.normalize(),
}
}
}

26
bheisler/src/scene.rs Normal file
View File

@@ -0,0 +1,26 @@
use rendering::Sphere;
pub struct Color {
pub red: f32,
pub green: f32,
pub blue: f32,
}
impl Color {
pub fn to_rgba(&self) -> [u8; 4] {
let v: [u8; 4] = [
(255. * self.red) as u8,
(255. * self.green) as u8,
(255. * self.blue) as u8,
255u8,
];
v
}
}
pub struct Scene {
pub width: u32,
pub height: u32,
pub fov: f32,
pub sphere: Sphere,
}

48
bheisler/src/vector.rs Normal file
View File

@@ -0,0 +1,48 @@
use point::Point;
use std::ops::Div;
pub struct Vector3 {
pub x: f32,
pub y: f32,
pub z: f32,
}
impl Vector3 {
pub fn normalize(&self) -> Vector3 {
self / self.length()
}
pub fn length_squared(&self) -> f32 {
self.x * self.x + self.y * self.y + self.z * self.z
}
pub fn length(&self) -> f32 {
self.length_squared().sqrt()
}
pub fn dot(&self, rhs: &Vector3) -> f32 {
self.x * rhs.x + self.y * rhs.y + self.z * rhs.z
}
}
impl<'a> Div<f32> for &'a Vector3 {
type Output = Vector3;
fn div(self, rhs: f32) -> Vector3 {
Vector3 {
x: self.x / rhs,
y: self.y / rhs,
z: self.z / rhs,
}
}
}
impl From<Point> for Vector3 {
fn from(p: Point) -> Self {
Vector3 {
x: p.x,
y: p.y,
z: p.z,
}
}
}