Draw earth texture to the sphere
This commit is contained in:
parent
8ab0a9d139
commit
bd06d32b34
8 changed files with 85 additions and 19 deletions
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)]
|
||||||
|
|
|
@ -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!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue