Render off of world vec

This commit is contained in:
k 2026-03-27 09:25:52 -04:00
parent 67fc7ea21d
commit 00a1fb16bd
5 changed files with 177 additions and 9 deletions

View file

@ -18,12 +18,12 @@ impl Pixel {
pub struct Image {
data: Vec<Pixel>,
pub width: u16,
pub height: u16,
pub width: i32,
pub height: i32,
}
impl Image {
pub fn new(width: u16, height: u16) -> Self {
pub fn new(width: i32, height: i32) -> Self {
let total_pixels = (width as usize) * (height as usize);
let data = vec![
Pixel {
@ -56,7 +56,7 @@ impl Image {
Ok(())
}
pub fn get_pixel(&mut self, x: u16, y: u16) -> Option<&mut Pixel> {
pub fn get_pixel(&mut self, x: i32, y: i32) -> Option<&mut Pixel> {
if x >= self.width || y >= self.height {
return None;
}

View file

@ -1,8 +1,15 @@
mod image;
mod object;
mod render;
mod vec3;
use image::Image;
use object::Sphere;
use render::World;
use vec3::{Point3, Ray, Vec3};
use crate::vec3::Interval;
fn main() {
let aspect_ratio = 16.0 / 9.0;
let image_width = 800;
@ -10,7 +17,7 @@ fn main() {
let focal_length = 1.0;
let viewport_height = 2.0;
let viewport_width = (viewport_height * (image_width / image_height) as f64) as i32;
let viewport_width = viewport_height * (image_width as f64 / image_height as f64);
let camera_center = Point3::new(0.0, 0.0, 0.0);
let viewport_u = Vec3::new(viewport_width as f64, 0.0, 0.0);
@ -19,11 +26,20 @@ fn main() {
let pixel_delta_u = viewport_u / image_width as f64;
let pixel_delta_v = viewport_v / image_height as f64;
let viewport_upper_left = camera_center
- (Vec3::new(0.0, 0.0, focal_length) - viewport_u / 2.0 - viewport_v / 2.0).to_point();
let viewport_upper_left = (camera_center.to_vec()
- Vec3::new(0.0, 0.0, focal_length)
- viewport_u / 2.0
- viewport_v / 2.0)
.to_point();
let pixel00_loc = viewport_upper_left + ((pixel_delta_u + pixel_delta_v) * 0.5).to_point();
let mut img = Image::new(800, 600);
let mut img = Image::new(image_width, image_height);
let mut world = World::new();
world.add(Sphere::new(0.5, Point3::new(0.0, 0.0, -1.0)));
world.add(Sphere::new(100.0, Point3::new(0.0, -100.5, -1.0)));
for y in 0..img.height {
for x in 0..img.width {
if let Some(px) = img.get_pixel(x, y) {
@ -32,7 +48,13 @@ fn main() {
let ray_direction = (pixel_center - camera_center).to_vec();
let ray = Ray::new(camera_center, ray_direction);
let (r, g, b) = color_ray(ray);
let i = Interval::new(0.001,f64::INFINITY);
if let Some(hit) = world.hit(ray, i) {
let (r,g,b) = hit.color();
px.set_color(r,g,b);
} else {
px.set_color(r, g, b);
}
} else {
}
}

82
src/object.rs Normal file
View file

@ -0,0 +1,82 @@
use crate::vec3::{Point3, Ray, Vec3, Interval};
pub trait Hittable {
fn hit(&self, ray: Ray, interval:Interval) -> Option<HitRecord>;
}
#[derive(Clone, Copy)]
pub struct HitRecord {
point: Point3,
normal: Vec3,
root: f64,
front_face: bool,
color: (f64,f64,f64)
}
impl HitRecord {
pub fn normal(self) -> Vec3 {
self.normal
}
pub fn point(self) -> Point3 {
self.point
}
pub fn root(self) -> f64 {
self.root
}
pub fn color(self) -> (f64,f64,f64) {
self.color
}
pub fn set_face_normal(&mut self, ray: Ray, norm: Vec3) {
self.front_face = ray.direction().dot(norm) < 0.0;
self.normal = if self.front_face { norm } else { norm * -1.0 };
}
}
pub struct Sphere {
radius: f64,
center: Point3,
}
impl Sphere {
pub fn new(radius: f64, center: Point3) -> Self {
Sphere { radius, center }
}
}
impl Hittable for Sphere {
fn hit(&self, ray: Ray, interval:Interval) -> Option<HitRecord> {
let oc = self.center - ray.origin();
let a = ray.direction().length_squared();
let h = Vec3::dot(ray.direction(), oc.to_vec());
let c = oc.to_vec().length_squared() - self.radius * self.radius;
let discriminant = h * h - a * c;
if discriminant < 0.0 {
return None;
}
let sqrtd = discriminant.sqrt();
let mut root = (h - sqrtd) / a;
if !interval.surounds(root) {
root = (h + sqrtd) / a;
if !interval.surounds(root) {
return None;
}
}
let point: Point3 = ray.at(root);
let normal = ((point - self.center) / self.radius).to_vec();
let mut tmp = HitRecord {
root,
point,
normal,
front_face: false,
color: (normal.x(),normal.y(),normal.z())
};
tmp.set_face_normal(ray, normal);
Some(tmp)
}
}

32
src/render.rs Normal file
View file

@ -0,0 +1,32 @@
use crate::object::{HitRecord, Hittable};
use crate::vec3::{Interval, Ray};
pub struct World {
pub objects: Vec<Box<dyn Hittable>>,
}
impl World {
pub fn new() -> Self {
World {
objects: Vec::new(),
}
}
pub fn add(&mut self, object: impl Hittable + 'static) {
self.objects.push(Box::new(object));
}
pub fn hit(&self, ray: Ray, interval:Interval) -> Option<HitRecord> {
let mut hit_anything = None;
let mut closest_so_far = interval.max();
for object in &self.objects {
let i = Interval::new(interval.min(),closest_so_far);
if let Some(record) = object.hit(ray, i) {
closest_so_far = record.root();
hit_anything = Some(record);
}
}
hit_anything
}
}

View file

@ -14,6 +14,38 @@ pub struct Point3 {
z: f64,
}
#[derive(Clone, Copy)]
pub struct Interval {
min: f64,
max: f64
}
impl Interval {
pub fn new(min:f64,max:f64) ->Self{
Self{min,max}
}
pub fn min(self) -> f64{
self.min
}
pub fn max(self) -> f64{
self.max
}
pub fn size(self) -> f64{
self.max - self.min
}
pub fn contains(self, x:f64) -> bool {
self.min <= x && x <= self.max
}
pub fn surounds(self, x:f64) -> bool {
self.min < x && x < self.max
}
}
#[derive(Clone, Copy)]
pub struct Ray {
direction: Vec3,