From 59b169507ca215bfde4c0bdce2530c052b129796 Mon Sep 17 00:00:00 2001 From: dengqn Date: Sun, 3 Aug 2025 14:52:47 +0800 Subject: [PATCH] add Camera --- src/camera.rs | 86 ++++++++++++++++++++++++++++++++++++++++++++ src/hittable.rs | 30 +++++++++++++--- src/image.rs | 58 ------------------------------ src/main.rs | 74 +++++++++++++++----------------------- src/sphere.rs | 1 + src/types_defined.rs | 14 ++++++-- 6 files changed, 152 insertions(+), 111 deletions(-) create mode 100644 src/camera.rs delete mode 100644 src/image.rs diff --git a/src/camera.rs b/src/camera.rs new file mode 100644 index 0000000..6231c32 --- /dev/null +++ b/src/camera.rs @@ -0,0 +1,86 @@ +use crate::hittable::HittableList; +use crate::types_defined::{Camera, Color, Point, Ray, Vec3}; + +impl Camera { + pub fn new( + image_width: i32, + aspect_ratio: f32, + viewport_height: f32, + camera_center: Point, + focal_length: Vec3, + ) -> 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); + + Camera { + image_width, + image_height, + aspect_ratio, + viewport_width, + viewport_height, + camera_center, + focal_length, + } + } + + pub fn render(&self, world: &HittableList) -> String { + let viewport_u = Vec3::new(self.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, -self.viewport_height, 0.0); + // width per pix + let viewport_u_delta = viewport_u / (self.image_width as f32); + // height per pix + let viewport_v_delta = viewport_v / (self.image_height as f32); + let viewport_top_left_pixel = + self.camera_center + self.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; + + 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 { + // every pixcel's position + let pixel_center = viewport_top_left_pixel_center + + (i as f32 * viewport_u_delta) + + (j as f32 * viewport_v_delta); + // Vector(camera, pixcel) + let ray_direction = pixel_center - self.camera_center; + // ray + let r = Ray::new(self.camera_center, ray_direction); + // determind color + let color = self.ray_color(&r, world); + // content + img_content.push_str(color.to_color().as_str()); + img_content.push('\n'); + } + } + + img_content + } + + fn ray_color(&self, ray: &Ray, world: &HittableList) -> Vec3 { + let hr = world.hit(&ray, 0.0, f32::MAX); + if hr.t >= 0.0 { + let normal_color = 0.5 * (hr.normal + Point::new(1.0, 1.0, 1.0)); + return if hr.front_face { + normal_color + } else { + Color::new(1.0, 1.0, 1.0) - normal_color + }; + } + // 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) + } +} diff --git a/src/hittable.rs b/src/hittable.rs index 162829f..4089645 100644 --- a/src/hittable.rs +++ b/src/hittable.rs @@ -1,4 +1,4 @@ -use crate::types_defined::{HitRecord, Ray}; +use crate::types_defined::{HitRecord, Ray, Vec3}; pub trait Hittable { fn hit(&self, r: &Ray, t_min: f32, t_max: f32) -> HitRecord; @@ -8,13 +8,33 @@ pub trait Hittable { pub struct HittableList { pub objects: Vec> } + + impl HittableList { - pub fn hit(&self, r: &Ray, t_min: f32, t_max: f32) -> Vec { - let mut records = vec![]; + pub fn new() -> Self { + HittableList { objects: vec![] } + } + + pub fn put(&mut self, object: Box) { + let _ = &self.objects.push(object); + } + + pub fn hit(&self, r: &Ray, t_min: f32, t_max: f32) -> HitRecord { for object in &self.objects { - records.push(object.hit(r, t_min, t_max)); + let temp = object.hit(r, t_min, t_max); + if temp.t >= 0.0 { + return temp + } + } + HitRecord { + t: -1.0, + normal: Vec3 { + x: 0.0, + y: 0.0, + z: 0.0, + }, + front_face: false, } - records } } diff --git a/src/image.rs b/src/image.rs deleted file mode 100644 index 0c0b1cb..0000000 --- a/src/image.rs +++ /dev/null @@ -1,58 +0,0 @@ -use crate::hittable::Hittable; -use crate::types_defined::{Color, Point, Ray, Sphere, Vec3}; - - - -pub fn gen_ray_sphere_normal_ppm_p3(width: i32, height: i32, camera_center: Vec3, viewport_top_left_pixel_center: Vec3, viewport_u_delta: Vec3, viewport_v_delta: Vec3) -> String { - let mut img_content = format!("P3\n{} {}\n255\n", width, height); - - for j in 0..height { - - if j % 10 == 0 { - println!("scan line {}/{} ", j + 1, height); - } - - for i in 0..width { - // every pixcel's position - let pixel_center = viewport_top_left_pixel_center + (i as f32 * viewport_u_delta) + (j as f32 * viewport_v_delta); - // Vector(camera, pixcel) - let ray_direction = pixel_center - camera_center; - // ray - let r = Ray::new(camera_center, ray_direction); - /* - 球 - */ - let _sphere_center = Point::new(0.0, 0.0, -1.0); - let sphere_radius = -0.5; - let sphere = Sphere::new(_sphere_center, sphere_radius); - - // determind color - let color = ray_color(&r, sphere); - // content - img_content.push_str(color.to_color().as_str()); - img_content.push('\n'); - } - } - - img_content -} - -/* -ray color functions -*/ -fn ray_color(r: &Ray, h: H) -> Color { - let hr = h.hit(&r, 0.0, f32::MAX); - if hr.t >= 0.0 { - let normal_color = 0.5 * (hr.normal + Point::new(1.0, 1.0, 1.0)); - return if hr.front_face { - normal_color - } else { - Color::new(1.0, 1.0, 1.0) - normal_color - } - } - // v / |v| - let unit_direction = r.direction / r.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) -} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index e0d84b6..f319a14 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,53 +1,37 @@ -use write_file_util::{write_image}; - -use crate::image::{gen_ray_sphere_normal_ppm_p3}; -use crate::types_defined::{Point, Vec3}; -mod image; -mod write_file_util; -mod vec3; -mod ray; +use crate::hittable::HittableList; +use crate::types_defined::{Camera, Point, Sphere, Vec3}; +use write_file_util::write_image; +mod camera; +mod color; mod hittable; +mod ray; mod sphere; mod types_defined; -mod color; +mod vec3; +mod write_file_util; fn main() { - ray_sphere_normal_scene_render(); + camera_render(); } +fn camera_render() { + let camera = Camera::new( + 800, + 16.0 / 9.0, + 2.0, + Point::new(0.0, 0.0, 0.0), + Vec3::new(0.0, 0.0, -1.0), + ); + // world + // world objects(spheres) + let mut world = HittableList::new(); + world.put(Box::new(Sphere::new(Point::new(0.0, 0.0, -1.0), 0.5))); + world.put(Box::new(Sphere::new(Point::new(0.1, -100.0, -0.0), 100.0))); + world.put(Box::new(Sphere::new(Point::new(-0.5, 0.2, -0.5), 0.2))); -fn ray_sphere_normal_scene_render() { - let aspect_ratio = 16.0/9.0; - let image_width = 400; - // aleast 1px - let image_height = ((image_width as f32 / aspect_ratio) as i32).max(1); - - let viewport_height = 2.0; - let viewport_width = viewport_height * (image_width as f32 / image_height as f32); - - println!("set image({},{}), viewport({},{})", image_width, image_height, viewport_width, viewport_height); - 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); - - // camerea position - let camera_center = Point::new(0.0, 0.0, 0.0); - // -z 1.0 viewport to camera - let focal_length = Vec3::new(0.0, 0.0, -1.0); - // camera position --> viewport center ---> top center --> top left - 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; - - let ppm_content = gen_ray_sphere_normal_ppm_p3(image_width, image_height, camera_center, viewport_top_left_pixel_center, viewport_u_delta, viewport_v_delta); - - write_image(ppm_content, "./target/ray_sphere_normal_scene_render.ppm".to_string()) -} \ No newline at end of file + let ppm_content = camera.render(&world); + write_image( + ppm_content, + "./target/ray_sphere_normal_scene_render.ppm".to_string(), + ) +} diff --git a/src/sphere.rs b/src/sphere.rs index b1fc8e5..9261dc9 100644 --- a/src/sphere.rs +++ b/src/sphere.rs @@ -43,3 +43,4 @@ impl hittable::Hittable for Sphere { } } } + \ No newline at end of file diff --git a/src/types_defined.rs b/src/types_defined.rs index b7de5b4..0235af5 100644 --- a/src/types_defined.rs +++ b/src/types_defined.rs @@ -18,7 +18,7 @@ fn: P(t) = t*b #[derive(Debug)] pub struct Ray { pub point: Point, - pub direction: Vec3 + pub direction: Vec3, } pub struct HitRecord { @@ -28,7 +28,15 @@ pub struct HitRecord { pub front_face: bool, } - +pub struct Camera { + pub image_width: i32, + pub image_height: i32, + pub aspect_ratio: f32, + pub viewport_width: f32, + pub viewport_height: f32, + pub camera_center: Point, + pub focal_length: Vec3, +} /* ///////////// @@ -38,4 +46,4 @@ pub struct HitRecord { pub struct Sphere { pub center: Point, pub radius: f32, -} \ No newline at end of file +}