ray-trace-w1/src/camera.rs

180 lines
6.8 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

use std::fs::File;
use std::io::{BufWriter};
use crate::hittable::HittableList;
use crate::math_utils::clamp;
use crate::ppm_writer::PPMWriter;
use crate::types_defined::{Camera, Color, HitRecord, Point, Ray, Vec3};
use rand::rngs::ThreadRng;
use rand::{Rng, thread_rng};
use crate::material::{Material, MaterialKind};
impl<'a> Camera<'a> {
pub fn new(
image_width: i32,
aspect_ratio: f32,
viewport_height: f32,
camera_center: Point,
focal_length: Vec3,
sample_times: i8,
reflect_depth: i8,
ppm_file_writer: &'a mut PPMWriter<BufWriter<File>>
) -> Self {
let image_height = ((image_width as f32 / aspect_ratio) as i32).max(1);
let viewport_width = viewport_height * (image_width as f32 / image_height as f32);
let viewport_u = Vec3::new(viewport_width, 0.0, 0.0);
// image x--> right
// |
// y
// space: y up , x right , z back, -z front
let viewport_v = Vec3::new(0.0, -viewport_height, 0.0);
// width per pix
let viewport_u_delta = viewport_u / (image_width as f32);
// height per pix
let viewport_v_delta = viewport_v / (image_height as f32);
let viewport_top_left_pixel =
camera_center + focal_length - viewport_u / 2.0 - viewport_v / 2.0;
// padding 0.5* delta u/v
let viewport_top_left_pixel_center =
viewport_top_left_pixel - viewport_u_delta / 2.0 - viewport_v_delta / 2.0;
Camera {
image_width,
image_height,
aspect_ratio,
viewport_width,
viewport_height,
camera_center,
focal_length,
viewport_u,
viewport_v,
viewport_u_delta,
viewport_v_delta,
viewport_top_left_pixel_center,
sample_times,
reflect_depth,
ppm_file_writer
}
}
pub fn render(&mut self, world: &HittableList) {
// let mut img_content = format!("P3\n{} {}\n255\n", self.image_width, self.image_height);
for j in 0..self.image_height {
// if j % 10 == 0 {
println!("scan line {}/{} ", j + 1, self.image_height);
// }
for i in 0..self.image_width {
// color
let mut color = Color::new(0.0, 0.0, 0.0);
let rng = &mut thread_rng();
for _ in 0..self.sample_times {
let bias = self.random_square(rng);
let pix_sample = self.viewport_top_left_pixel_center
+ (i as f32 + bias.x) * self.viewport_u_delta
+ (j as f32 + bias.y) * self.viewport_v_delta;
let r = Ray::new(self.camera_center, pix_sample - self.camera_center);
let sample_color = self.ray_color(&r, self.reflect_depth, world);
color = color + sample_color;
}
// color * each sample color
color = color * (1.0 / self.sample_times as f32);
// clamp color rgb
let color_clamped = Color::new(
clamp(color.x, 0.0, 1.0),
clamp(color.y, 0.0, 1.0),
clamp(color.z, 0.0, 1.0),
);
self.ppm_file_writer.write(color.to_color());
}
}
// img_content
}
fn ray_color(&self, ray: &Ray, depth: i8, world: &HittableList) -> Vec3 {
// 反射次数
if depth <= 0 {
return Vec3::new(0.0, 0.0, 0.0);
}
// 限制出射光线的角度0.001经验值)
let hit_record = &mut HitRecord{
t: 0.0,
p: Vec3::new(0.0, 0.0, 0.0),
normal: Vec3::new(0.0, 0.0, 0.0),
front_face: false,
material: None,
};
let hit = world.hit(&ray, 0.001, f32::MAX, hit_record);
if hit {
let scatted = &mut Ray::new(Point::new(0.0, 0.0, 0.0), Vec3::random());
let attenuation = &mut Color::new(1.0, 1.0, 1.0);
let hit_m = &hit_record.material;
let hc = &mut HitRecord{
t: hit_record.t,
p: hit_record.p,
normal: hit_record.normal,
front_face: hit_record.front_face,
material: None,
};
let hit_c = match hit_m {
Some(mk) => {
let mc = match mk {
MaterialKind::Lambertian(l) => {
if l.scatter(ray, hc, attenuation, scatted) {
let r_c = self.ray_color(scatted, depth - 1, world);
let sc_color = Vec3::new(attenuation.x * r_c.x, attenuation.y * r_c.y, attenuation.z * r_c.z);
// *attenuation * ;
Color::new(sc_color.x, sc_color.y, sc_color.z)
} else {
Color::new(0.0, 0.0, 0.0)
}
},
MaterialKind::Metal(m) => {
if m.scatter(ray, hc, attenuation, scatted) {
let r_c = self.ray_color(scatted, depth - 1, world);
let sc_color = Vec3::new(attenuation.x * r_c.x, attenuation.y * r_c.y, attenuation.z * r_c.z);
// *attenuation * ;
Color::new(sc_color.x, sc_color.y, sc_color.z)
} else {
Color::new(0.0, 0.0, 0.0)
}
}
};
return mc
},
None => {
// if hit_record.t >= 0.0 {
// // diffuse normal vec
// let diffuse_vec = Vec3::random_unit();
// let lambertian_vec = diffuse_vec + hit_record.normal;
// // 每次反射按0.5计算颜色(反射率
// return 0.5 * self.ray_color(&Ray::new(ray.point, lambertian_vec), depth - 1, world);
// }
Color::new(0.0, 0.0, 0.0)
}
};
return hit_c;
}
// v / |v|
let unit_direction = ray.direction / ray.direction.length();
let a = 0.5 * (unit_direction.y + 1.0);
// return background color.
(1.0 - a) * Color::new(1.0, 1.0, 1.0) + a * Color::new(0.5, 0.7, 1.0)
}
// -> [x, y, 0]
fn random_square(&self, rng: &mut ThreadRng) -> Vec3 {
Vec3::new(rng.gen_range(-0.5..0.1), rng.gen_range(-0.5..0.1), 0.0)
}
}