Compare commits
2 Commits
89a2333644
...
388c48d236
Author | SHA1 | Date | |
---|---|---|---|
388c48d236 | |||
46ec8663ff |
1
.gitignore
vendored
1
.gitignore
vendored
@ -2,3 +2,4 @@
|
|||||||
*.ppm
|
*.ppm
|
||||||
.idea
|
.idea
|
||||||
*.iml
|
*.iml
|
||||||
|
.vscode/
|
||||||
|
@ -4,42 +4,56 @@ pub struct Camera {
|
|||||||
aspect_ratio: f64,
|
aspect_ratio: f64,
|
||||||
viewport_height: f64,
|
viewport_height: f64,
|
||||||
viewport_width: f64,
|
viewport_width: f64,
|
||||||
focal_length: f64,
|
|
||||||
origin: Point3,
|
origin: Point3,
|
||||||
horizontal: Vec3,
|
horizontal: Vec3,
|
||||||
vertical: Vec3,
|
vertical: Vec3,
|
||||||
lower_left_corner: Vec3
|
lower_left_corner: Vec3,
|
||||||
|
lens_radius: f64,
|
||||||
|
u: Vec3,
|
||||||
|
v: Vec3,
|
||||||
|
w: Vec3
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Camera {
|
impl Camera {
|
||||||
pub fn get_ray(&self, u: f64, v: f64) -> Ray {
|
pub fn get_ray(&self, s: f64, t: f64) -> Ray {
|
||||||
|
let rd = self.lens_radius * Vec3::random_unit_disk();
|
||||||
|
let offset = self.u * rd.x() + self.v * rd.y();
|
||||||
Ray::new(
|
Ray::new(
|
||||||
self.origin,
|
self.origin + offset,
|
||||||
self.lower_left_corner + u*self.horizontal + v*self.vertical - self.origin)
|
self.lower_left_corner + s*self.horizontal + t*self.vertical - self.origin - offset)
|
||||||
}
|
}
|
||||||
pub fn new(aspect_ratio: f64) -> Self {
|
pub fn new(
|
||||||
let viewport_height = 2.0;
|
look_from: Point3,
|
||||||
|
look_at: Point3,
|
||||||
|
up: Vec3,
|
||||||
|
aspect_ratio: f64,
|
||||||
|
vfov: f64,
|
||||||
|
aperture: f64,
|
||||||
|
focus_dist: f64) -> Self {
|
||||||
|
|
||||||
|
let theta = vfov.to_radians();
|
||||||
|
let h = (theta / 2.0).tan();
|
||||||
|
let viewport_height = 2.0 * h;
|
||||||
let viewport_width = aspect_ratio * viewport_height;
|
let viewport_width = aspect_ratio * viewport_height;
|
||||||
let origin = Point3::default();
|
|
||||||
let horizontal = Vec3::new(viewport_width, 0.0, 0.0);
|
let w = (look_from - look_at).unit_vector();
|
||||||
let vertical = Vec3::new(0.0, viewport_height, 0.0);
|
let u = up.cross(&w).unit_vector();
|
||||||
let focal_length = 1.0;
|
let v = w.cross(&u);
|
||||||
|
|
||||||
|
let horizontal = focus_dist * viewport_width * u;
|
||||||
|
let vertical = focus_dist * viewport_height * v;
|
||||||
Camera {
|
Camera {
|
||||||
aspect_ratio,
|
aspect_ratio,
|
||||||
viewport_height,
|
viewport_height,
|
||||||
viewport_width,
|
viewport_width,
|
||||||
focal_length,
|
origin: look_from,
|
||||||
origin,
|
|
||||||
horizontal,
|
horizontal,
|
||||||
vertical,
|
vertical,
|
||||||
lower_left_corner: origin - horizontal/2.0 - vertical/2.0 - Vec3::new(0.0, 0.0, focal_length)
|
lower_left_corner: look_from - horizontal/2.0 - vertical/2.0 - focus_dist*w,
|
||||||
|
lens_radius: aperture / 2.0,
|
||||||
|
u,
|
||||||
|
v,
|
||||||
|
w
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Camera {
|
|
||||||
fn default() -> Self {
|
|
||||||
let aspect_ratio = 16.0 / 9.0;
|
|
||||||
Camera::new(aspect_ratio)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -42,16 +42,16 @@ impl Hittable for Sphere {
|
|||||||
let discriminant = half_b * half_b - a * c;
|
let discriminant = half_b * half_b - a * c;
|
||||||
if discriminant < 0.0 { return None; }
|
if discriminant < 0.0 { return None; }
|
||||||
|
|
||||||
let sqrtd = f64::sqrt(discriminant);
|
let sqrtd = discriminant.sqrt();
|
||||||
let root = (-half_b - sqrtd) / a;
|
let mut root = (-half_b - sqrtd) / a;
|
||||||
if root < t_min || t_max < root {
|
if root < t_min || t_max < root {
|
||||||
let root = (-half_b + sqrtd) / a;
|
root = (-half_b + sqrtd) / a;
|
||||||
if root < t_min || t_max < root { return None; }
|
if root < t_min || t_max < root { return None; }
|
||||||
}
|
}
|
||||||
|
|
||||||
let point = ray.at(root);
|
let point = ray.at(root);
|
||||||
let normal = (point - self.center) / self.radius;
|
let normal = (point - self.center) / self.radius;
|
||||||
let front_face = ray.direction().dot(&normal) < 0.0;
|
let dot = ray.direction().dot(&normal);
|
||||||
|
let front_face = dot < 0.0;
|
||||||
let normal = if front_face { normal } else { -normal };
|
let normal = if front_face { normal } else { -normal };
|
||||||
Some(HitRecord {
|
Some(HitRecord {
|
||||||
point,
|
point,
|
||||||
|
103
src/main.rs
103
src/main.rs
@ -8,59 +8,79 @@ use crate::material::{Dielectric, Lambertian, Material, Metal};
|
|||||||
mod vec3;
|
mod vec3;
|
||||||
mod ray;
|
mod ray;
|
||||||
mod hittable;
|
mod hittable;
|
||||||
mod camera;
|
|
||||||
mod material;
|
mod material;
|
||||||
|
mod camera;
|
||||||
|
|
||||||
|
fn random_scene() -> Vec<Box<dyn Hittable>> {
|
||||||
|
let mut world:Vec<Box<dyn Hittable>> = Vec::new();
|
||||||
|
|
||||||
|
let material_ground = Material::Lambertian(Lambertian::new(Color::new(0.5, 0.5, 0.5)));
|
||||||
|
let ground = Sphere {
|
||||||
|
center: Point3::new(0.0, -1000.0, 0.0),
|
||||||
|
radius: 1000.0,
|
||||||
|
material: material_ground
|
||||||
|
};
|
||||||
|
world.push(Box::new(ground));
|
||||||
|
|
||||||
|
let unit_range = Uniform::from(0.0..1.0);
|
||||||
|
let fuzz_range = Uniform::from(0.0..0.5);
|
||||||
|
let mut rng = rand::thread_rng();
|
||||||
|
let p = Point3::new(4.0, 0.2, 0.0);
|
||||||
|
for a in -1..11 {
|
||||||
|
for b in -11..11 {
|
||||||
|
let choose_material = unit_range.sample(&mut rng);
|
||||||
|
let center = Point3::new(
|
||||||
|
(a as f64) + 0.9*unit_range.sample(&mut rng),
|
||||||
|
0.2,
|
||||||
|
(b as f64) + 0.9*unit_range.sample(&mut rng));
|
||||||
|
if (center - p).length() < 0.9 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let material = match choose_material {
|
||||||
|
_ if choose_material < 0.8 => Material::Lambertian(Lambertian::new(
|
||||||
|
Color::random(0.0, 1.0) * Color::random(0.0, 1.0))),
|
||||||
|
_ if choose_material < 0.95 => Material::Metal(Metal::new(
|
||||||
|
Color::random(0.5, 1.0),
|
||||||
|
fuzz_range.sample(&mut rng))),
|
||||||
|
_ => Material::Dielectric(Dielectric::new(1.5)),
|
||||||
|
};
|
||||||
|
let sphere = Box::new(Sphere {
|
||||||
|
center,
|
||||||
|
radius: 0.2,
|
||||||
|
material
|
||||||
|
});
|
||||||
|
world.push(sphere);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
world
|
||||||
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
// Image
|
// Image
|
||||||
const ASPECT_RATIO: f64 = 16.0 / 9.0;
|
const ASPECT_RATIO: f64 = 3.0 / 2.0;
|
||||||
const IMAGE_WIDTH: i32 = 400;
|
const IMAGE_WIDTH: i32 = 1200;
|
||||||
const IMAGE_HEIGHT: i32 = (IMAGE_WIDTH as f64 / ASPECT_RATIO) as i32;
|
const IMAGE_HEIGHT: i32 = (IMAGE_WIDTH as f64 / ASPECT_RATIO) as i32;
|
||||||
const SAMPLES_PER_PIXEL: i32 = 10;
|
const SAMPLES_PER_PIXEL: i32 = 10;
|
||||||
const MAX_DEPTH: i32 = 50;
|
const MAX_DEPTH: i32 = 50;
|
||||||
|
|
||||||
|
let look_from = Point3::new(13.0, 2.0, 3.0);
|
||||||
|
let look_at = Point3::new(0.0, 0.0, 0.0);
|
||||||
|
let focus_dist = 10.0;
|
||||||
// Camera
|
// Camera
|
||||||
let cam = Camera::new(ASPECT_RATIO);
|
let cam = Camera::new(
|
||||||
|
look_from,
|
||||||
|
look_at,
|
||||||
|
Vec3::new(0.0, 1.0, 0.0),
|
||||||
|
ASPECT_RATIO,
|
||||||
|
20.0,
|
||||||
|
0.1,
|
||||||
|
focus_dist);
|
||||||
|
|
||||||
// World
|
// World
|
||||||
let mut world:Vec<Box<dyn Hittable>> = Vec::new();
|
let world= random_scene();
|
||||||
|
|
||||||
let center = Box::new(Sphere {
|
|
||||||
center: Point3::new(0.0, 0.0, -1.0),
|
|
||||||
radius: 0.5,
|
|
||||||
material: Material::Lambertian(Lambertian::new(Color::new(0.7, 0.3, 0.3)))
|
|
||||||
});
|
|
||||||
let center_glass = Box::new(Sphere {
|
|
||||||
center: Point3::new(0.0, 0.0, -1.0),
|
|
||||||
radius: 0.5,
|
|
||||||
material: Material::Dielectric(Dielectric::new(1.5))
|
|
||||||
});
|
|
||||||
world.push(center_glass);
|
|
||||||
let ground = Sphere {
|
|
||||||
center: Point3::new(0.0, -100.5, -1.0),
|
|
||||||
radius: 100.0,
|
|
||||||
material: Material::Lambertian(Lambertian::new(Color::new(0.8, 0.8, 0.0)))
|
|
||||||
};
|
|
||||||
world.push(Box::new(ground));
|
|
||||||
let left = Box::new(Sphere {
|
|
||||||
center: Point3::new(-1.0, 0.0, -1.0),
|
|
||||||
radius: 0.5,
|
|
||||||
material: Material::Metal(Metal::new(Color::new(0.8, 0.8, 0.8), 0.3))
|
|
||||||
});
|
|
||||||
let left_glass = Box::new(Sphere {
|
|
||||||
center: Point3::new(-1.0, 0.0, -1.0),
|
|
||||||
radius: 0.5,
|
|
||||||
material: Material::Dielectric(Dielectric::new(1.5))
|
|
||||||
});
|
|
||||||
world.push(left_glass);
|
|
||||||
let right = Box::new(Sphere {
|
|
||||||
center: Point3::new(1.0, 0.0, -1.0),
|
|
||||||
radius: 0.5,
|
|
||||||
material: Material::Metal(Metal::new(Color::new(0.8, 0.6, 0.2), 1.0))
|
|
||||||
});
|
|
||||||
world.push(right);
|
|
||||||
|
|
||||||
println!("P3\n{} {}\n255", IMAGE_WIDTH, IMAGE_HEIGHT);
|
println!("P3\n{} {}\n255", IMAGE_WIDTH, IMAGE_HEIGHT);
|
||||||
|
|
||||||
let between = Uniform::from(0.0..1.0);
|
let between = Uniform::from(0.0..1.0);
|
||||||
let mut rng = rand::thread_rng();
|
let mut rng = rand::thread_rng();
|
||||||
for j in (0..IMAGE_HEIGHT).rev() {
|
for j in (0..IMAGE_HEIGHT).rev() {
|
||||||
@ -72,6 +92,9 @@ fn main() {
|
|||||||
let u = (i as f64 + random_number) / (IMAGE_WIDTH - 1) as f64;
|
let u = (i as f64 + random_number) / (IMAGE_WIDTH - 1) as f64;
|
||||||
let v = (j as f64 + random_number) / (IMAGE_HEIGHT - 1) as f64;
|
let v = (j as f64 + random_number) / (IMAGE_HEIGHT - 1) as f64;
|
||||||
let ray = cam.get_ray(u, v);
|
let ray = cam.get_ray(u, v);
|
||||||
|
if i == 200 && (j == 112 || j == 113) {
|
||||||
|
let tt = 0;
|
||||||
|
}
|
||||||
color += ray.pixel_color(&world, MAX_DEPTH);
|
color += ray.pixel_color(&world, MAX_DEPTH);
|
||||||
}
|
}
|
||||||
color.write_color(SAMPLES_PER_PIXEL);
|
color.write_color(SAMPLES_PER_PIXEL);
|
||||||
|
@ -104,6 +104,7 @@ impl Scatterable for Dielectric {
|
|||||||
self.index_of_refraction
|
self.index_of_refraction
|
||||||
};
|
};
|
||||||
let unit_direction = ray.direction().unit_vector();
|
let unit_direction = ray.direction().unit_vector();
|
||||||
|
|
||||||
let cos_theta = ((-unit_direction).dot(&hit_record.normal)).min(1.0);
|
let cos_theta = ((-unit_direction).dot(&hit_record.normal)).min(1.0);
|
||||||
let sin_theta = (1.0 - cos_theta*cos_theta).sqrt();
|
let sin_theta = (1.0 - cos_theta*cos_theta).sqrt();
|
||||||
let cannot_refract = refraction_ratio * sin_theta > 1.0;
|
let cannot_refract = refraction_ratio * sin_theta > 1.0;
|
||||||
@ -112,12 +113,10 @@ impl Scatterable for Dielectric {
|
|||||||
|
|
||||||
if cannot_refract || reflectance > rng.gen::<f64>() {
|
if cannot_refract || reflectance > rng.gen::<f64>() {
|
||||||
let reflected = unit_direction.reflected(&hit_record.normal);
|
let reflected = unit_direction.reflected(&hit_record.normal);
|
||||||
let reflected = Vec3::new(-reflected.x(), reflected.y(), -reflected.z());
|
|
||||||
let scattered = Ray::new(hit_record.point, reflected);
|
let scattered = Ray::new(hit_record.point, reflected);
|
||||||
Some((Some(scattered), color))
|
Some((Some(scattered), color))
|
||||||
} else {
|
} else {
|
||||||
//let direction = unit_direction.refract(&hit_record.normal, refraction_ratio);
|
let direction = unit_direction.refract(&hit_record.normal, refraction_ratio);
|
||||||
let direction = unit_direction.refract_sort_of_works(&hit_record.normal, refraction_ratio);
|
|
||||||
let scattered = Ray::new(hit_record.point, direction);
|
let scattered = Ray::new(hit_record.point, direction);
|
||||||
Some((Some(scattered), color))
|
Some((Some(scattered), color))
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ use crate::hittable::HitRecord;
|
|||||||
use crate::material::Scatterable;
|
use crate::material::Scatterable;
|
||||||
use crate::vec3::Point3;
|
use crate::vec3::Point3;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct Ray {
|
pub struct Ray {
|
||||||
origin: Point3,
|
origin: Point3,
|
||||||
direction: Vec3
|
direction: Vec3
|
||||||
|
28
src/vec3.rs
28
src/vec3.rs
@ -63,6 +63,19 @@ impl Vec3 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn random_unit_vector() -> Self { Vec3::random_in_unit_sphere().unit_vector() }
|
pub fn random_unit_vector() -> Self { Vec3::random_in_unit_sphere().unit_vector() }
|
||||||
|
pub fn random_unit_disk() -> Self {
|
||||||
|
let between = Uniform::from(-1.0..1.0);
|
||||||
|
let mut rng = rand::thread_rng();
|
||||||
|
loop {
|
||||||
|
let v = Vec3::new(
|
||||||
|
between.sample(&mut rng),
|
||||||
|
between.sample(&mut rng),
|
||||||
|
0.0);
|
||||||
|
if v.length_squared() < 1.0 {
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
pub fn near_zero(&self) -> bool {
|
pub fn near_zero(&self) -> bool {
|
||||||
const MAXIMUM_DISTANCE_FROM_ZERO:f64 = 1e-8;
|
const MAXIMUM_DISTANCE_FROM_ZERO:f64 = 1e-8;
|
||||||
self.x.abs() < MAXIMUM_DISTANCE_FROM_ZERO &&
|
self.x.abs() < MAXIMUM_DISTANCE_FROM_ZERO &&
|
||||||
@ -84,21 +97,6 @@ impl Vec3 {
|
|||||||
let out_parallel = r * (*normal);
|
let out_parallel = r * (*normal);
|
||||||
out_perp + out_parallel
|
out_perp + out_parallel
|
||||||
}
|
}
|
||||||
pub fn refract_sort_of_works(&self, normal: &Vec3, etai_over_etat: f64) -> Vec3 {
|
|
||||||
let dot = (-(*self)).dot(normal);
|
|
||||||
let cos_theta = dot.min(1.0);
|
|
||||||
let out_perp = etai_over_etat * ((*self) + cos_theta * (*normal));
|
|
||||||
let inner = 1.0 - out_perp.length_squared();
|
|
||||||
let abs = inner.abs();
|
|
||||||
let r = -(abs.sqrt());
|
|
||||||
let out_parallel = r * (*normal);
|
|
||||||
// Why?
|
|
||||||
let mut res = out_perp + out_parallel;
|
|
||||||
res.x = -res.x;
|
|
||||||
//res.y = -res.y;
|
|
||||||
res.z = -res.z;
|
|
||||||
res
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
Loading…
Reference in New Issue
Block a user