Get normals for faces directly from .obj

This commit is contained in:
Jonathan Flueren 2022-07-26 19:50:09 +02:00
parent b37f8e3144
commit acbb572dcd
3 changed files with 80 additions and 45 deletions

20
Cargo.lock generated
View file

@ -39,9 +39,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]] [[package]]
name = "bytemuck" name = "bytemuck"
version = "1.10.0" version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c53dfa917ec274df8ed3c572698f381a24eef2efba9492d797301b72b6db408a" checksum = "a5377c8865e74a160d21f29c2d40669f53286db6eab59b88540cbb12ffc8b835"
[[package]] [[package]]
name = "byteorder" name = "byteorder"
@ -72,9 +72,9 @@ dependencies = [
[[package]] [[package]]
name = "crossbeam-channel" name = "crossbeam-channel"
version = "0.5.5" version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c02a4d71819009c192cf4872265391563fd6a84c81ff2c0f2a7026ca4c1d85c" checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"crossbeam-utils", "crossbeam-utils",
@ -82,9 +82,9 @@ dependencies = [
[[package]] [[package]]
name = "crossbeam-deque" name = "crossbeam-deque"
version = "0.8.1" version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e" checksum = "715e8152b692bba2d374b53d4875445368fdf21a94751410af607a5ac677d1fc"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"crossbeam-epoch", "crossbeam-epoch",
@ -93,9 +93,9 @@ dependencies = [
[[package]] [[package]]
name = "crossbeam-epoch" name = "crossbeam-epoch"
version = "0.9.9" version = "0.9.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07db9d94cbd326813772c968ccd25999e5f8ae22f4f8d1b11effa37ef6ce281d" checksum = "045ebe27666471bb549370b4b0b3e51b07f56325befa4284db65fc89c02511b1"
dependencies = [ dependencies = [
"autocfg", "autocfg",
"cfg-if", "cfg-if",
@ -107,9 +107,9 @@ dependencies = [
[[package]] [[package]]
name = "crossbeam-utils" name = "crossbeam-utils"
version = "0.8.10" version = "0.8.11"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d82ee10ce34d7bc12c2122495e7593a9c41347ecdd64185af4ecf72cb1a7f83" checksum = "51887d4adc7b564537b15adcfb307936f8075dfcd5f00dde9a9f1d29383682bc"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"once_cell", "once_cell",

View file

@ -161,16 +161,22 @@ impl Hittable for Triangle {
rec.p = r.at(t); rec.p = r.at(t);
rec.mat_ptr = self.mat_ptr.clone(); rec.mat_ptr = self.mat_ptr.clone();
// triangle normal alg from https://stackoverflow.com/questions/19350792/calculate-normal-of-a-single-triangle-in-3d-space
rec.normal = self.normal; rec.normal = self.normal;
return true; return true;
/* old normal calculation code if normal is not known
// triangle normal alg from https://stackoverflow.com/questions/19350792/calculate-normal-of-a-single-triangle-in-3d-space
let normal_to_camera = Vec3::new( let normal_to_camera = Vec3::new(
a_to_b.y() * a_to_c.z() - a_to_b.z() * a_to_c.y(), a_to_b.y() * a_to_c.z() - a_to_b.z() * a_to_c.y(),
a_to_b.z() * a_to_c.x() - a_to_b.x() * a_to_c.z(), a_to_b.z() * a_to_c.x() - a_to_b.x() * a_to_c.z(),
a_to_b.x() * a_to_c.y() - a_to_b.y() * a_to_c.x(), a_to_b.x() * a_to_c.y() - a_to_b.y() * a_to_c.x(),
); );
let unit_normal = normal_to_camera / normal_to_camera.length(); let unit_normal = normal_to_camera / normal_to_camera.length();
println!("From obj: {},{},{} - calculated: {},{},{}", self.normal.x(),self.normal.y(),self.normal.z(),unit_normal.x(),unit_normal.y(),unit_normal.z());
if Vec3::dot(unit_normal, r.direction()) >= 0.0 { if Vec3::dot(unit_normal, r.direction()) >= 0.0 {
// normal towards camera // normal towards camera
rec.set_face_normal(r, unit_normal); rec.set_face_normal(r, unit_normal);
@ -180,5 +186,6 @@ impl Hittable for Triangle {
} }
return true; return true;
*/
} }
} }

View file

@ -17,9 +17,6 @@ use material::{Dielectric, Lambertian, Material, Metal};
use ray::Ray; use ray::Ray;
use rayon::prelude::*; use rayon::prelude::*;
use std::env; use std::env;
use std::fs::File;
use std::io::{self, BufRead};
use std::path::Path;
use std::sync::atomic::{AtomicU32, Ordering}; use std::sync::atomic::{AtomicU32, Ordering};
use std::sync::Arc; use std::sync::Arc;
use tobj; use tobj;
@ -42,13 +39,8 @@ fn ray_color(r: &Ray, world: &HittableList, depth: u32) -> Color {
return attenuation * ray_color(&scattered, world, depth - 1); return attenuation * ray_color(&scattered, world, depth - 1);
} }
return Color::null(); return Color::null();
//let target = rec.p + rec.normal + Vec3::random_unit_vector(); // rec.p + rec.normal.random_in_hemisphere();
//return 0.5 * ray_color(&Ray::new(rec.p, target - rec.p), world, depth - 1);
} }
// return Color::new(1.0, 1.0, 1.0);
let unit_direction = r.direction(); let unit_direction = r.direction();
let t = 0.5 * (unit_direction.y() + 1.0); let t = 0.5 * (unit_direction.y() + 1.0);
return (1.0 - t) * Color::new(1.0, 1.0, 1.0) + t * Color::new(0.5, 0.7, 1.0); return (1.0 - t) * Color::new(1.0, 1.0, 1.0) + t * Color::new(0.5, 0.7, 1.0);
@ -144,6 +136,7 @@ fn random_world() -> HittableList {
material5.clone(), material5.clone(),
))); )));
/*
world.add(Box::<Triangle>::new(Triangle::new( world.add(Box::<Triangle>::new(Triangle::new(
Point3::new(0.0, 1.0, 5.0), Point3::new(0.0, 1.0, 5.0),
Point3::new(3.0, 2.0, 0.0), Point3::new(3.0, 2.0, 0.0),
@ -169,6 +162,7 @@ fn random_world() -> HittableList {
Point3::new(6.0, 4.0, -6.0), Point3::new(6.0, 4.0, -6.0),
material5.clone(), material5.clone(),
))); )));
*/
return world; return world;
} }
@ -176,9 +170,24 @@ fn random_world() -> HittableList {
fn from_obj(path: &str) -> HittableList { fn from_obj(path: &str) -> HittableList {
let mut world = HittableList::new(); let mut world = HittableList::new();
let material = Arc::new(Lambertian::new(&Color::new(0.6, 0.2, 0.3))); let material_ground = Arc::new(Lambertian::new(&Color::new(
29.0 / 255.0,
71.0 / 255.0,
14.0 / 255.0,
)));
world.add(Box::<Sphere>::new(Sphere::new(
Point3::new(0.0, -5005.0, 0.0),
5000.0,
material_ground.clone(),
)));
let material = Arc::new(Lambertian::new(&Color::new(
26.0 / 255.0,
82.0 / 255.0,
118.0 / 255.0,
)));
//let material = Arc::new(Dielectric::new(2.0)); //let material = Arc::new(Dielectric::new(2.0));
//let material = Arc::new(Metal::new(&Color::new(0.9, 0.9, 0.7), 0.5)); //let material = Arc::new(Metal::new(&Color::new(0.9, 0.9, 0.7), 1.0));
let cornell_box = tobj::load_obj(path, &tobj::OFFLINE_RENDERING_LOAD_OPTIONS); let cornell_box = tobj::load_obj(path, &tobj::OFFLINE_RENDERING_LOAD_OPTIONS);
let (models, materials) = cornell_box.expect("Failed to load OBJ file"); let (models, materials) = cornell_box.expect("Failed to load OBJ file");
@ -187,6 +196,7 @@ fn from_obj(path: &str) -> HittableList {
for (i, m) in models.iter().enumerate() { for (i, m) in models.iter().enumerate() {
let mesh = &m.mesh; let mesh = &m.mesh;
/* Mesh vector lengths
println!("positions: {}", mesh.positions.len()); println!("positions: {}", mesh.positions.len());
println!("vertex_color: {}", mesh.vertex_color.len()); println!("vertex_color: {}", mesh.vertex_color.len());
println!("normals: {}", mesh.normals.len()); println!("normals: {}", mesh.normals.len());
@ -195,9 +205,7 @@ fn from_obj(path: &str) -> HittableList {
println!("face_arities: {}", mesh.face_arities.len()); println!("face_arities: {}", mesh.face_arities.len());
println!("texcoord_indices: {}", mesh.texcoord_indices.len()); println!("texcoord_indices: {}", mesh.texcoord_indices.len());
println!("normal_indices: {}", mesh.normal_indices.len()); println!("normal_indices: {}", mesh.normal_indices.len());
*/
return world;
let mut next_face = 0; let mut next_face = 0;
for f in 0..mesh.face_arities.len() { for f in 0..mesh.face_arities.len() {
@ -211,7 +219,38 @@ fn from_obj(path: &str) -> HittableList {
let index_a = mesh.indices[3 * v] as usize; let index_a = mesh.indices[3 * v] as usize;
let index_b = mesh.indices[3 * v + 1] as usize; let index_b = mesh.indices[3 * v + 1] as usize;
let index_c = mesh.indices[3 * v + 2] as usize; let index_c = mesh.indices[3 * v + 2] as usize;
let index_normal = mesh.normal_indices[] let index_normal_a = mesh.normal_indices[3 * v] as usize;
let index_normal_b = mesh.normal_indices[3 * v + 1] as usize;
let index_normal_c = mesh.normal_indices[3 * v + 2] as usize;
let normal_avg = Vec3::unit_vector(
Vec3::new(
mesh.positions[3 * index_normal_a] as f64,
mesh.positions[3 * index_normal_a + 1] as f64,
mesh.positions[3 * index_normal_a + 2] as f64,
) + Vec3::new(
mesh.positions[3 * index_normal_b] as f64,
mesh.positions[3 * index_normal_b + 1] as f64,
mesh.positions[3 * index_normal_b + 2] as f64,
) + Vec3::new(
mesh.positions[3 * index_normal_c] as f64,
mesh.positions[3 * index_normal_c + 1] as f64,
mesh.positions[3 * index_normal_c + 2] as f64,
),
);
/*
println!("a:{},{},{}; b:{},{},{}; c:{},{},{}",
mesh.normals[3*index_normal_a],
mesh.normals[3*index_normal_a+1],
mesh.normals[3*index_normal_a+2],
mesh.normals[3*index_normal_b],
mesh.normals[3*index_normal_b+1],
mesh.normals[3*index_normal_b+2],
mesh.normals[3*index_normal_c],
mesh.normals[3*index_normal_c+1],
mesh.normals[3*index_normal_c+2]);
*/
world.add(Box::<Triangle>::new(Triangle::new( world.add(Box::<Triangle>::new(Triangle::new(
Point3::new( Point3::new(
@ -229,7 +268,7 @@ fn from_obj(path: &str) -> HittableList {
mesh.positions[3 * index_c + 1] as f64, mesh.positions[3 * index_c + 1] as f64,
mesh.positions[3 * index_c + 2] as f64, mesh.positions[3 * index_c + 2] as f64,
), ),
mesh. normal_avg,
material.clone(), material.clone(),
))); )));
} }
@ -237,16 +276,6 @@ fn from_obj(path: &str) -> HittableList {
return world; return world;
} }
// The output is wrapped in a Result to allow matching on errors
// Returns an Iterator to the Reader of the lines of the file.
fn read_lines<P>(filename: P) -> io::Result<io::Lines<io::BufReader<File>>>
where
P: AsRef<Path>,
{
let file = File::open(filename)?;
Ok(io::BufReader::new(file).lines())
}
/* /*
Current world view: Current world view:
@ -267,27 +296,26 @@ fn main() {
// Image // Image
let aspect_ratio = 16.0 / 9.0; let aspect_ratio = 16.0 / 9.0;
let image_width = 600; let image_width = 1200;
let image_height = (image_width as f64 / aspect_ratio) as u32; let image_height = (image_width as f64 / aspect_ratio) as u32;
let samples_per_pixel = 1_u32; let samples_per_pixel = 50_u32;
let max_depth = 50; let max_depth = 50;
let vfov = 40.0; let vfov = 40.0;
let lookfrom = Point3::new(2.0, 0.5, 3.0); let lookfrom = Point3::new(2.0, 0.8, 3.0);
let lookat = Point3::new(0.0, 0.0, 0.0); let lookat = Point3::new(0.0, 0.0, 0.0);
let vup = Vec3::new(0.0, 1.0, 0.0); let vup = Vec3::new(0.0, 1.0, 0.0);
let dist_to_focus = 3.0; let dist_to_focus = 3.0;
let aperture = 0.1; let aperture = 0.1;
// limit rayon multithreading thread count // limit rayon multithreading thread count
let thread_count = 0; // if 0, for each logical cpu core a thread wil be created let thread_count = 6; // if 0, for each logical cpu core a thread wil be created
if thread_count > 0 { if thread_count > 0 {
env::set_var("RAYON_NUM_THREADS", thread_count.to_string()); env::set_var("RAYON_NUM_THREADS", thread_count.to_string());
} }
let world = from_obj("obj/suzanne.obj");
// World // World
let world = from_obj("obj/suzanne.obj");
// let world = random_world(); // let world = random_world();
// Camera // Camera