ray-trace-w1/src/camera.rs

122 lines
4.3 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 crate::hittable::HittableList;
use crate::math_utils::clamp;
use crate::types_defined::{Camera, Color, Point, Ray, Vec3};
use rand::rngs::ThreadRng;
use rand::{Rng, thread_rng};
impl Camera {
pub fn new(
image_width: i32,
aspect_ratio: f32,
viewport_height: f32,
camera_center: Point,
focal_length: Vec3,
sample_times: i8,
reflect_depth: i8
) -> 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,
}
}
pub fn render(&self, world: &HittableList) -> String {
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 s 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),
);
// content
img_content.push_str(color_clamped.to_color().as_str());
img_content.push('\n');
}
}
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 hr = world.hit(&ray, 0.01, f32::MAX);
if hr.t >= 0.0 {
// diffuse normal vec
let diffuse_vec = Vec3::random_unit();
let lambertian_vec = diffuse_vec + hr.normal;
// 每次反射按0.5计算颜色(反射率
return 0.5 * self.ray_color(&Ray::new(ray.point, lambertian_vec), depth - 1, world);
}
// 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)
}
}