add Camera

This commit is contained in:
dengqn 2025-08-03 14:52:47 +08:00
parent 5a77b92d9e
commit 59b169507c
6 changed files with 152 additions and 111 deletions

86
src/camera.rs Normal file
View File

@ -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)
}
}

View File

@ -1,4 +1,4 @@
use crate::types_defined::{HitRecord, Ray}; use crate::types_defined::{HitRecord, Ray, Vec3};
pub trait Hittable { pub trait Hittable {
fn hit(&self, r: &Ray, t_min: f32, t_max: f32) -> HitRecord; fn hit(&self, r: &Ray, t_min: f32, t_max: f32) -> HitRecord;
@ -8,13 +8,33 @@ pub trait Hittable {
pub struct HittableList { pub struct HittableList {
pub objects: Vec<Box<dyn Hittable>> pub objects: Vec<Box<dyn Hittable>>
} }
impl HittableList { impl HittableList {
pub fn hit(&self, r: &Ray, t_min: f32, t_max: f32) -> Vec<HitRecord> { pub fn new() -> Self {
let mut records = vec![]; HittableList { objects: vec![] }
}
pub fn put(&mut self, object: Box<dyn Hittable>) {
let _ = &self.objects.push(object);
}
pub fn hit(&self, r: &Ray, t_min: f32, t_max: f32) -> HitRecord {
for object in &self.objects { 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
} }
} }

View File

@ -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<H: Hittable>(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)
}

View File

@ -1,53 +1,37 @@
use write_file_util::{write_image}; use crate::hittable::HittableList;
use crate::types_defined::{Camera, Point, Sphere, Vec3};
use crate::image::{gen_ray_sphere_normal_ppm_p3}; use write_file_util::write_image;
use crate::types_defined::{Point, Vec3}; mod camera;
mod image; mod color;
mod write_file_util;
mod vec3;
mod ray;
mod hittable; mod hittable;
mod ray;
mod sphere; mod sphere;
mod types_defined; mod types_defined;
mod color; mod vec3;
mod write_file_util;
fn main() { 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 ppm_content = camera.render(&world);
let aspect_ratio = 16.0/9.0; write_image(
let image_width = 400; ppm_content,
// aleast 1px "./target/ray_sphere_normal_scene_render.ppm".to_string(),
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())
}

View File

@ -43,3 +43,4 @@ impl hittable::Hittable for Sphere {
} }
} }
} }

View File

@ -18,7 +18,7 @@ fn: P(t) = t*b
#[derive(Debug)] #[derive(Debug)]
pub struct Ray { pub struct Ray {
pub point: Point, pub point: Point,
pub direction: Vec3 pub direction: Vec3,
} }
pub struct HitRecord { pub struct HitRecord {
@ -28,7 +28,15 @@ pub struct HitRecord {
pub front_face: bool, 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 struct Sphere {
pub center: Point, pub center: Point,
pub radius: f32, pub radius: f32,
} }