raytracing-in-a-weekend/ria-weekend/examples/surface_normal_sphere.rs

55 lines
1.8 KiB
Rust
Raw Normal View History

extern crate ria_weekend;
use ria_weekend::{demo::Demo, ray, ray::Ray, vec3::Vec3};
fn main() {
let demo = Demo::new("surface_normal_sphere");
let dimensions = demo.dimensions();
let mut buf = String::new();
// linear interpolation based on y coordinate
// top to down
let color = |ray: Ray| -> Vec3 {
// center at z=-1. xy axis cuts sphere in half
// blending parameter
let t = ray_hit_sphere(Vec3::new(0.0, 0.0, 1.0), 0.5, &ray);
if t > 0.0 {
// For all rays that hit sphere, return red color
// This will result in a sphere that is red in color
let N = ray.point_at_parameter(t) - Vec3::new(0.0, 0.0, -1.0);
return Vec3::new(N.x() + 1.0, N.y() + 1.0, N.z() + 1.0) * 0.5;
}
let unit_direction = ray.direction().unit_vector();
// For rays that don't hit sphere, It'll paint the gradient as the background
// Linear gradient depends on y
let t = 0.5 * (unit_direction.y() + 1.0);
// start color + end color
Vec3::new(1.0, 1.0, 1.0) * (1.0 - t) + Vec3::new(0.0, 0.0, 0.0) * t
};
ray::create_ray_demo(&mut buf, dimensions, color);
demo.save_as_ppm(buf);
}
fn ray_hit_sphere(center: Vec3, radius: f32, ray: &Ray) -> f32 {
// dot(A + t*B - C, A + t*B - C) = R*R
// when expanded we get
// t * t * dot(B, B) + 2 * t * dot(B, A-C) + dot(A-C, A-C) - R*R = 0
// A-C
let ac = ray.origin() - center;
let a = ray.direction().dot(&ray.direction());
let b = 2.0 * ac.dot(&ray.direction());
let c = ac.dot(&ac) - radius * radius;
let discriminant = b * b - 4.0 * a * c;
if discriminant >= 0.0 {
// return quadratic root
(-b + discriminant.sqrt()) / (2.0 * a)
} else {
-1.0
}
}