Draw earth texture to the sphere

This commit is contained in:
Michael Zhang 2023-02-25 16:20:59 -06:00
parent 8ab0a9d139
commit bd06d32b34
8 changed files with 85 additions and 19 deletions

View file

@ -29,7 +29,9 @@ pub struct Image {
impl Image { impl Image {
pub fn from_file(path: impl AsRef<Path>) -> Result<Self> { pub fn from_file(path: impl AsRef<Path>) -> Result<Self> {
let file = File::open(path.as_ref())?; let path = path.as_ref();
let file = File::open(path)
.with_context(|| format!("Could not open file at {path:?}"))?;
Self::read(file) Self::read(file)
} }

View file

@ -113,7 +113,7 @@ fn main() -> Result<()> {
Ok(match earliest_intersection { Ok(match earliest_intersection {
// Take the object's material color // Take the object's material color
Some((obj_idx, intersection_context, object)) => { Some((obj_idx, intersection_context, object)) => {
scene.compute_pixel_color(obj_idx, object, intersection_context) scene.compute_pixel_color(obj_idx, object, intersection_context)?
} }
// There was no intersection, so this should default to the scene's // There was no intersection, so this should default to the scene's

View file

@ -1,5 +1,6 @@
use std::iter; use std::iter;
use anyhow::Result;
use ordered_float::NotNan; use ordered_float::NotNan;
use rand::Rng; use rand::Rng;
use rayon::prelude::{ use rayon::prelude::{
@ -10,7 +11,8 @@ use rayon::prelude::{
use crate::{image::Color, ray::Ray, utils::dot, Point, Vector}; use crate::{image::Color, ray::Ray, utils::dot, Point, Vector};
use super::{ use super::{
data::{DepthCueing, Light, LightKind, Object}, data::{DepthCueing, Light, LightKind},
object::Object,
Scene, Scene,
}; };
@ -27,20 +29,22 @@ impl Scene {
obj_idx: usize, obj_idx: usize,
object: &Object, object: &Object,
intersection_context: IntersectionContext, intersection_context: IntersectionContext,
) -> Color { ) -> Result<Color> {
// TODO: Does it make sense to make this function fallible from an API
// design standpoint?
let material = match self.materials.get(object.material_idx) { let material = match self.materials.get(object.material_idx) {
Some(v) => v, Some(v) => v,
None => return self.bkg_color, None => bail!("Material index not found."),
}; };
let diffuse_color = match object.texture_idx { let diffuse_color = match object.texture_idx {
Some(texture_idx) => { Some(texture_idx) => {
let texture = &self.textures[texture_idx]; let (u, v) = object.kind.get_texture_coord(&intersection_context)?;
// TODO: need to implement let texture = match self.textures.get(texture_idx) {
material.diffuse_color Some(v) => v,
None => bail!("Texture index not found."),
};
texture.pixel_at(u, v)
} }
None => material.diffuse_color, None => material.diffuse_color,
}; };
@ -132,7 +136,7 @@ impl Scene {
// Need to clamp the result so none of the components goes over 1 // Need to clamp the result so none of the components goes over 1
let clamped_result = color.map(|v| v.min(1.0)); let clamped_result = color.map(|v| v.min(1.0));
clamped_result Ok(clamped_result)
} }
/// Perform another ray casting to see if there are any objects obstructing /// Perform another ray casting to see if there are any objects obstructing

View file

@ -10,15 +10,17 @@ use crate::{
image::{Color, Image}, image::{Color, Image},
scene::{ scene::{
cylinder::Cylinder, cylinder::Cylinder,
data::{Attenuation, Light, LightKind, Material, Object}, data::{Attenuation, Light, LightKind, Material},
object::{Object, ObjectKind},
sphere::Sphere, sphere::Sphere,
texture::Texture,
triangle::Triangle, triangle::Triangle,
Scene, texture::Texture, Scene,
}, },
Point, Vector, Point, Vector,
}; };
use super::data::{DepthCueing, ObjectKind}; use super::data::DepthCueing;
impl Scene { impl Scene {
/// Parse the input file into a scene /// Parse the input file into a scene

View file

@ -2,15 +2,16 @@ pub mod cylinder;
pub mod data; pub mod data;
pub mod illumination; pub mod illumination;
pub mod input_file; pub mod input_file;
pub mod object;
pub mod sphere; pub mod sphere;
pub mod texture; pub mod texture;
pub mod triangle; pub mod triangle;
pub mod object;
use crate::image::{Color, Image}; use crate::image::{Color, Image};
use crate::{Point, Point2, Vector}; use crate::{Point, Point2, Vector};
use self::data::{Attenuation, DepthCueing, Light, Material, Object}; use self::data::{Attenuation, DepthCueing, Light, Material};
use self::object::Object;
use self::texture::Texture; use self::texture::Texture;
#[derive(Debug, Default)] #[derive(Debug, Default)]

View file

@ -44,4 +44,17 @@ impl ObjectKind {
ObjectKind::Triangle(triangle) => triangle.intersects_ray_at(scene, ray), ObjectKind::Triangle(triangle) => triangle.intersects_ray_at(scene, ray),
} }
} }
/// Get the coordinates in the texture (between 0 and 1) that corresponds to
/// the intersection point
pub fn get_texture_coord(
&self,
ctx: &IntersectionContext,
) -> Result<(f64, f64)> {
match self {
ObjectKind::Sphere(sphere) => sphere.get_texture_coord(ctx),
ObjectKind::Cylinder(cylinder) => todo!(),
ObjectKind::Triangle(triangle) => todo!(),
}
}
} }

View file

@ -1,3 +1,5 @@
use std::f64::consts::PI;
use anyhow::Result; use anyhow::Result;
use ordered_float::NotNan; use ordered_float::NotNan;
@ -72,4 +74,21 @@ impl Sphere {
normal, normal,
})) }))
} }
pub fn get_texture_coord(
&self,
ctx: &IntersectionContext,
) -> Result<(f64, f64)> {
// Reverse engineer the angles from the coordinate of the intersection
let cosp = (ctx.point.z - self.center.z) / self.radius;
let phi = cosp.acos();
let theta =
(ctx.point.y - self.center.y).atan2(ctx.point.x - self.center.x);
// Map theta and phi into 0 - 1 range
let v = phi / PI;
let u = 0.5 + theta / (2.0 * PI);
Ok((u, v))
}
} }

View file

@ -8,9 +8,34 @@ impl Texture {
Self(image) Self(image)
} }
pub fn pixel_at_exact(&self, x: usize, y: usize) -> Color {
// TODO: Debug asserts?
self.0.data[y * self.0.width + x]
}
/// Returns a pixel at the given coordinate. For non-lattice coordinates, /// Returns a pixel at the given coordinate. For non-lattice coordinates,
/// interpolation of the image is done. /// bi-linear interpolation of the image is done.
pub fn pixel_at(&self, x: f64, y: f64) -> Option<Color> { pub fn pixel_at(&self, u: f64, v: f64) -> Color {
todo!() debug_assert!(0.0 <= u && u <= 1.0, "u must be between 0 and 1");
debug_assert!(0.0 <= v && v <= 1.0, "u must be between 0 and 1");
// Slide 121
let x = u * (self.0.width - 1) as f64;
let y = v * (self.0.height - 1) as f64;
let i = x.floor();
let j = y.floor();
let alpha = x - i;
let beta = y - j;
let i = i as usize;
let j = j as usize;
(1.0 - alpha) * (1.0 - beta) * self.pixel_at_exact(i, j)
+ (alpha) * (1.0 - beta) * self.pixel_at_exact(i + 1, j)
+ (1.0 - alpha) * (beta) * self.pixel_at_exact(i, j + 1)
+ (alpha) * (beta) * self.pixel_at_exact(i + 1, j + 1)
} }
} }