Implemented smooth-shaded triangle meshes.

There are still some things to do to avoid light leakage and
other weird shading in some situations, but the basics are working!
This commit is contained in:
Nathan Vegdahl 2017-07-30 17:55:03 -07:00
parent e77d5b7576
commit 05578a1240
3 changed files with 174 additions and 86 deletions

View File

@ -166,11 +166,15 @@ class Mesh:
w.write("SubdivisionSurface $%s {\n" % self.name) w.write("SubdivisionSurface $%s {\n" % self.name)
w.indent() w.indent()
# Write vertices # Write vertices and (if it's smooth shaded) normals
for ti in range(len(self.time_meshes)): for ti in range(len(self.time_meshes)):
w.write("Vertices [") w.write("Vertices [")
w.write(" ".join([("%f" % i) for vert in self.time_meshes[ti].vertices for i in vert.co]), False) w.write(" ".join([("%f" % i) for vert in self.time_meshes[ti].vertices for i in vert.co]), False)
w.write("]\n", False) w.write("]\n", False)
if self.time_meshes[0].polygons[0].use_smooth and self.ob.data.psychopath.is_subdivision_surface == False:
w.write("Normals [")
w.write(" ".join([("%f" % i) for vert in self.time_meshes[ti].vertices for i in vert.normal]), False)
w.write("]\n", False)
# Write face vertex counts # Write face vertex counts
w.write("FaceVertCounts [") w.write("FaceVertCounts [")

View File

@ -6,7 +6,7 @@ use nom::IResult;
use mem_arena::MemArena; use mem_arena::MemArena;
use math::Point; use math::{Point, Normal};
use surface::triangle_mesh::TriangleMesh; use surface::triangle_mesh::TriangleMesh;
use super::basics::{ws_usize, ws_f32}; use super::basics::{ws_usize, ws_f32};
@ -26,6 +26,7 @@ pub fn parse_mesh_surface<'a>(
tree: &'a DataTree, tree: &'a DataTree,
) -> Result<TriangleMesh<'a>, PsyParseError> { ) -> Result<TriangleMesh<'a>, PsyParseError> {
let mut verts = Vec::new(); // Vec of vecs, one for each time sample let mut verts = Vec::new(); // Vec of vecs, one for each time sample
let mut normals = Vec::new(); // Vec of vecs, on for each time sample
let mut face_vert_counts = Vec::new(); let mut face_vert_counts = Vec::new();
let mut face_vert_indices = Vec::new(); let mut face_vert_indices = Vec::new();
@ -54,6 +55,30 @@ pub fn parse_mesh_surface<'a>(
assert_eq!(vert_count, vs.len()); assert_eq!(vert_count, vs.len());
} }
// Get normals, if they exist
for (_, text, _) in tree.iter_leaf_children_with_type("Normals") {
let mut raw_text = text.trim().as_bytes();
// Collect verts for this time sample
let mut tnormals = Vec::new();
while let IResult::Done(remaining, nor) =
closure!(tuple!(ws_f32, ws_f32, ws_f32))(raw_text)
{
raw_text = remaining;
tnormals.push(Normal::new(nor.0, nor.1, nor.2).normalized());
}
normals.push(tnormals);
}
// Make sure normal's time samples and vert count match the vertices
if !normals.is_empty() {
assert_eq!(normals.len(), verts.len());
for ns in &normals {
assert_eq!(vert_count, ns.len());
}
}
// Get face vert counts // Get face vert counts
if let Some((_, text, _)) = tree.iter_leaf_children_with_type("FaceVertCounts").nth(0) { if let Some((_, text, _)) = tree.iter_leaf_children_with_type("FaceVertCounts").nth(0) {
let mut raw_text = text.trim().as_bytes(); let mut raw_text = text.trim().as_bytes();
@ -101,6 +126,11 @@ pub fn parse_mesh_surface<'a>(
Ok(TriangleMesh::from_verts_and_indices( Ok(TriangleMesh::from_verts_and_indices(
arena, arena,
verts, verts,
if normals.is_empty() {
None
} else {
Some(normals)
},
tri_vert_indices, tri_vert_indices,
)) ))
} }

View File

@ -8,7 +8,7 @@ use boundable::Boundable;
use color::XYZ; use color::XYZ;
use fp_utils::fp_gamma; use fp_utils::fp_gamma;
use lerp::lerp_slice; use lerp::lerp_slice;
use math::{Point, Matrix4x4, cross}; use math::{Point, Normal, Matrix4x4, dot, cross};
use ray::{Ray, AccelRay}; use ray::{Ray, AccelRay};
use shading::surface_closure::{SurfaceClosureUnion, GTRClosure, LambertClosure}; use shading::surface_closure::{SurfaceClosureUnion, GTRClosure, LambertClosure};
@ -20,6 +20,7 @@ use super::triangle;
pub struct TriangleMesh<'a> { pub struct TriangleMesh<'a> {
time_sample_count: usize, time_sample_count: usize,
vertices: &'a [Point], // Vertices, with the time samples for each vertex stored contiguously vertices: &'a [Point], // Vertices, with the time samples for each vertex stored contiguously
normals: Option<&'a [Normal]>, // Vertex normals, organized the same as `vertices`
indices: &'a [(u32, u32, u32, u32)], // (v0_idx, v1_idx, v2_idx, original_tri_idx) indices: &'a [(u32, u32, u32, u32)], // (v0_idx, v1_idx, v2_idx, original_tri_idx)
accel: BVH4<'a>, accel: BVH4<'a>,
} }
@ -28,6 +29,7 @@ impl<'a> TriangleMesh<'a> {
pub fn from_verts_and_indices<'b>( pub fn from_verts_and_indices<'b>(
arena: &'b MemArena, arena: &'b MemArena,
verts: Vec<Vec<Point>>, verts: Vec<Vec<Point>>,
vert_normals: Option<Vec<Vec<Normal>>>,
tri_indices: Vec<(usize, usize, usize)>, tri_indices: Vec<(usize, usize, usize)>,
) -> TriangleMesh<'b> { ) -> TriangleMesh<'b> {
let vert_count = verts[0].len(); let vert_count = verts[0].len();
@ -48,6 +50,25 @@ impl<'a> TriangleMesh<'a> {
vertices vertices
}; };
// Copy vertex normals, if any, organizing them the same as vertices
// above.
let normals = match vert_normals {
Some(ref vnors) => {
let mut normals =
unsafe { arena.alloc_array_uninitialized(vert_count * time_sample_count) };
for vi in 0..vert_count {
for ti in 0..time_sample_count {
normals[(vi * time_sample_count) + ti] = vnors[ti][vi];
}
}
Some(&normals[..])
}
None => None,
};
// Copy triangle vertex indices over, appending the triangle index itself to the tuple // Copy triangle vertex indices over, appending the triangle index itself to the tuple
let mut indices = { let mut indices = {
let mut indices = unsafe { arena.alloc_array_uninitialized(tri_indices.len()) }; let mut indices = unsafe { arena.alloc_array_uninitialized(tri_indices.len()) };
@ -82,6 +103,7 @@ impl<'a> TriangleMesh<'a> {
TriangleMesh { TriangleMesh {
time_sample_count: time_sample_count, time_sample_count: time_sample_count,
vertices: vertices, vertices: vertices,
normals: normals,
indices: indices, indices: indices,
accel: accel, accel: accel,
} }
@ -110,26 +132,27 @@ impl<'a> Surface for TriangleMesh<'a> {
Matrix4x4::new() Matrix4x4::new()
}; };
self.accel self.accel.traverse(
.traverse( &mut accel_rays[..],
&mut accel_rays[..], self.indices, |tri_indices, rs| { self.indices,
|tri_indices, rs| {
for r in rs { for r in rs {
let wr = &wrays[r.id as usize]; let wr = &wrays[r.id as usize];
// Get triangle // Get triangle
let tri = { let tri = {
let p0_slice = &self.vertices[ let p0_slice = &self.vertices[(tri_indices.0 as usize *
(tri_indices.0 as usize * self.time_sample_count).. self.time_sample_count)..
((tri_indices.0 as usize + 1) * self.time_sample_count) ((tri_indices.0 as usize + 1) *
]; self.time_sample_count)];
let p1_slice = &self.vertices[ let p1_slice = &self.vertices[(tri_indices.1 as usize *
(tri_indices.1 as usize * self.time_sample_count).. self.time_sample_count)..
((tri_indices.1 as usize + 1) * self.time_sample_count) ((tri_indices.1 as usize + 1) *
]; self.time_sample_count)];
let p2_slice = &self.vertices[ let p2_slice = &self.vertices[(tri_indices.2 as usize *
(tri_indices.2 as usize * self.time_sample_count).. self.time_sample_count)..
((tri_indices.2 as usize + 1) * self.time_sample_count) ((tri_indices.2 as usize + 1) *
]; self.time_sample_count)];
let p0 = lerp_slice(p0_slice, wr.time); let p0 = lerp_slice(p0_slice, wr.time);
let p1 = lerp_slice(p1_slice, wr.time); let p1 = lerp_slice(p1_slice, wr.time);
@ -144,18 +167,18 @@ impl<'a> Surface for TriangleMesh<'a> {
if space.len() > 1 { if space.len() > 1 {
// Per-ray transform, for motion blur // Per-ray transform, for motion blur
let mat_space = lerp_slice(space, wr.time).inverse(); let mat_space = lerp_slice(space, wr.time).inverse();
(mat_space, (mat_space, (
(tri.0 * mat_space, tri.0 * mat_space,
tri.1 * mat_space, tri.1 * mat_space,
tri.2 * mat_space) tri.2 * mat_space,
) ))
} else { } else {
// Same transform for all rays // Same transform for all rays
(static_mat_space, (static_mat_space, (
(tri.0 * static_mat_space, tri.0 * static_mat_space,
tri.1 * static_mat_space, tri.1 * static_mat_space,
tri.2 * static_mat_space) tri.2 * static_mat_space,
) ))
} }
} else { } else {
// No transforms // No transforms
@ -170,14 +193,47 @@ impl<'a> Surface for TriangleMesh<'a> {
r.mark_done(); r.mark_done();
} else { } else {
// Calculate intersection point and error magnitudes // Calculate intersection point and error magnitudes
let pos = ((tri.0.into_vector() * b0) let pos = ((tri.0.into_vector() * b0) + (tri.1.into_vector() * b1) +
+ (tri.1.into_vector() * b1) (tri.2.into_vector() * b2))
+ (tri.2.into_vector() * b2)).into_point(); .into_point();
let pos_err = (((tri.0.into_vector().abs() * b0) let pos_err = (((tri.0.into_vector().abs() * b0) +
+ (tri.1.into_vector().abs() * b1) (tri.1.into_vector().abs() * b1) +
+ (tri.2.into_vector().abs() * b2)) (tri.2.into_vector().abs() * b2)) *
* fp_gamma(7)).co.h_max(); fp_gamma(7)).co
.h_max();
// Calculate geometric surface normal
let geo_normal = cross(tri.0 - tri.1, tri.0 - tri.2).into_normal();
// Calculate interpolated surface normal, if any
let shading_normal = if let Some(normals) = self.normals {
let n0_slice = &normals[(tri_indices.0 as usize *
self.time_sample_count)..
((tri_indices.0 as usize + 1) *
self.time_sample_count)];
let n1_slice = &normals[(tri_indices.1 as usize *
self.time_sample_count)..
((tri_indices.1 as usize + 1) *
self.time_sample_count)];
let n2_slice = &normals[(tri_indices.2 as usize *
self.time_sample_count)..
((tri_indices.2 as usize + 1) *
self.time_sample_count)];
let n0 = lerp_slice(n0_slice, wr.time).normalized();
let n1 = lerp_slice(n1_slice, wr.time).normalized();
let n2 = lerp_slice(n2_slice, wr.time).normalized();
let s_nor = (n0 * b0) + (n1 * b1) + (n2 * b2);
if dot(s_nor, geo_normal) >= 0.0 {
s_nor
} else {
-s_nor
}
} else {
geo_normal
};
// Fill in intersection data // Fill in intersection data
isects[r.id as usize] = SurfaceIntersection::Hit { isects[r.id as usize] = SurfaceIntersection::Hit {
@ -186,16 +242,14 @@ impl<'a> Surface for TriangleMesh<'a> {
t: t, t: t,
pos: pos, pos: pos,
pos_err: pos_err, pos_err: pos_err,
nor: cross(tri.0 - tri.1, tri.0 - tri.2) nor: shading_normal,
.into_normal(), // TODO nor_g: geo_normal,
nor_g: cross(tri.0 - tri.1, tri.0 - tri.2)
.into_normal(),
uv: (0.0, 0.0), // TODO uv: (0.0, 0.0), // TODO
local_space: mat_space, local_space: mat_space,
}, },
// TODO: get surface closure from surface shader. // TODO: get surface closure from surface shader.
closure: SurfaceClosureUnion::LambertClosure( closure: SurfaceClosureUnion::LambertClosure(
LambertClosure::new(XYZ::new(0.8, 0.8, 0.8)) LambertClosure::new(XYZ::new(0.8, 0.8, 0.8)),
), ),
// closure: // closure:
// SurfaceClosureUnion::GTRClosure( // SurfaceClosureUnion::GTRClosure(
@ -209,7 +263,7 @@ impl<'a> Surface for TriangleMesh<'a> {
} }
} }
} }
} },
); );
} }
} }