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.indent()
# Write vertices
# Write vertices and (if it's smooth shaded) normals
for ti in range(len(self.time_meshes)):
w.write("Vertices [")
w.write(" ".join([("%f" % i) for vert in self.time_meshes[ti].vertices for i in vert.co]), 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
w.write("FaceVertCounts [")

View File

@ -6,7 +6,7 @@ use nom::IResult;
use mem_arena::MemArena;
use math::Point;
use math::{Point, Normal};
use surface::triangle_mesh::TriangleMesh;
use super::basics::{ws_usize, ws_f32};
@ -26,6 +26,7 @@ pub fn parse_mesh_surface<'a>(
tree: &'a DataTree,
) -> Result<TriangleMesh<'a>, PsyParseError> {
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_indices = Vec::new();
@ -54,6 +55,30 @@ pub fn parse_mesh_surface<'a>(
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
if let Some((_, text, _)) = tree.iter_leaf_children_with_type("FaceVertCounts").nth(0) {
let mut raw_text = text.trim().as_bytes();
@ -101,6 +126,11 @@ pub fn parse_mesh_surface<'a>(
Ok(TriangleMesh::from_verts_and_indices(
arena,
verts,
if normals.is_empty() {
None
} else {
Some(normals)
},
tri_vert_indices,
))
}

View File

@ -8,7 +8,7 @@ use boundable::Boundable;
use color::XYZ;
use fp_utils::fp_gamma;
use lerp::lerp_slice;
use math::{Point, Matrix4x4, cross};
use math::{Point, Normal, Matrix4x4, dot, cross};
use ray::{Ray, AccelRay};
use shading::surface_closure::{SurfaceClosureUnion, GTRClosure, LambertClosure};
@ -20,6 +20,7 @@ use super::triangle;
pub struct TriangleMesh<'a> {
time_sample_count: usize,
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)
accel: BVH4<'a>,
}
@ -28,6 +29,7 @@ impl<'a> TriangleMesh<'a> {
pub fn from_verts_and_indices<'b>(
arena: &'b MemArena,
verts: Vec<Vec<Point>>,
vert_normals: Option<Vec<Vec<Normal>>>,
tri_indices: Vec<(usize, usize, usize)>,
) -> TriangleMesh<'b> {
let vert_count = verts[0].len();
@ -48,6 +50,25 @@ impl<'a> TriangleMesh<'a> {
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
let mut indices = {
let mut indices = unsafe { arena.alloc_array_uninitialized(tri_indices.len()) };
@ -82,6 +103,7 @@ impl<'a> TriangleMesh<'a> {
TriangleMesh {
time_sample_count: time_sample_count,
vertices: vertices,
normals: normals,
indices: indices,
accel: accel,
}
@ -110,106 +132,138 @@ impl<'a> Surface for TriangleMesh<'a> {
Matrix4x4::new()
};
self.accel
.traverse(
&mut accel_rays[..], self.indices, |tri_indices, rs| {
for r in rs {
let wr = &wrays[r.id as usize];
self.accel.traverse(
&mut accel_rays[..],
self.indices,
|tri_indices, rs| {
for r in rs {
let wr = &wrays[r.id as usize];
// Get triangle
let tri = {
let p0_slice = &self.vertices[
(tri_indices.0 as usize * self.time_sample_count)..
((tri_indices.0 as usize + 1) * self.time_sample_count)
];
let p1_slice = &self.vertices[
(tri_indices.1 as usize * self.time_sample_count)..
((tri_indices.1 as usize + 1) * self.time_sample_count)
];
let p2_slice = &self.vertices[
(tri_indices.2 as usize * self.time_sample_count)..
((tri_indices.2 as usize + 1) * self.time_sample_count)
];
// Get triangle
let tri = {
let p0_slice = &self.vertices[(tri_indices.0 as usize *
self.time_sample_count)..
((tri_indices.0 as usize + 1) *
self.time_sample_count)];
let p1_slice = &self.vertices[(tri_indices.1 as usize *
self.time_sample_count)..
((tri_indices.1 as usize + 1) *
self.time_sample_count)];
let p2_slice = &self.vertices[(tri_indices.2 as usize *
self.time_sample_count)..
((tri_indices.2 as usize + 1) *
self.time_sample_count)];
let p0 = lerp_slice(p0_slice, wr.time);
let p1 = lerp_slice(p1_slice, wr.time);
let p2 = lerp_slice(p2_slice, wr.time);
let p0 = lerp_slice(p0_slice, wr.time);
let p1 = lerp_slice(p1_slice, wr.time);
let p2 = lerp_slice(p2_slice, wr.time);
(p0, p1, p2)
};
(p0, p1, p2)
};
// Transform triangle as necessary, and get transform
// space.
let (mat_space, tri) = if !space.is_empty() {
if space.len() > 1 {
// Per-ray transform, for motion blur
let mat_space = lerp_slice(space, wr.time).inverse();
(mat_space,
(tri.0 * mat_space,
tri.1 * mat_space,
tri.2 * mat_space)
)
} else {
// Same transform for all rays
(static_mat_space,
(tri.0 * static_mat_space,
tri.1 * static_mat_space,
tri.2 * static_mat_space)
)
}
// Transform triangle as necessary, and get transform
// space.
let (mat_space, tri) = if !space.is_empty() {
if space.len() > 1 {
// Per-ray transform, for motion blur
let mat_space = lerp_slice(space, wr.time).inverse();
(mat_space, (
tri.0 * mat_space,
tri.1 * mat_space,
tri.2 * mat_space,
))
} else {
// No transforms
(Matrix4x4::new(), tri)
};
// Same transform for all rays
(static_mat_space, (
tri.0 * static_mat_space,
tri.1 * static_mat_space,
tri.2 * static_mat_space,
))
}
} else {
// No transforms
(Matrix4x4::new(), tri)
};
// Test ray against triangle
if let Some((t, b0, b1, b2)) = triangle::intersect_ray(wr, tri) {
if t < r.max_t {
if r.is_occlusion() {
isects[r.id as usize] = SurfaceIntersection::Occlude;
r.mark_done();
// Test ray against triangle
if let Some((t, b0, b1, b2)) = triangle::intersect_ray(wr, tri) {
if t < r.max_t {
if r.is_occlusion() {
isects[r.id as usize] = SurfaceIntersection::Occlude;
r.mark_done();
} else {
// Calculate intersection point and error magnitudes
let pos = ((tri.0.into_vector() * b0) + (tri.1.into_vector() * b1) +
(tri.2.into_vector() * b2))
.into_point();
let pos_err = (((tri.0.into_vector().abs() * b0) +
(tri.1.into_vector().abs() * b1) +
(tri.2.into_vector().abs() * b2)) *
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 {
// Calculate intersection point and error magnitudes
let pos = ((tri.0.into_vector() * b0)
+ (tri.1.into_vector() * b1)
+ (tri.2.into_vector() * b2)).into_point();
geo_normal
};
let pos_err = (((tri.0.into_vector().abs() * b0)
+ (tri.1.into_vector().abs() * b1)
+ (tri.2.into_vector().abs() * b2))
* fp_gamma(7)).co.h_max();
// Fill in intersection data
isects[r.id as usize] = SurfaceIntersection::Hit {
intersection_data: SurfaceIntersectionData {
incoming: wr.dir,
t: t,
pos: pos,
pos_err: pos_err,
nor: cross(tri.0 - tri.1, tri.0 - tri.2)
.into_normal(), // TODO
nor_g: cross(tri.0 - tri.1, tri.0 - tri.2)
.into_normal(),
uv: (0.0, 0.0), // TODO
local_space: mat_space,
},
// TODO: get surface closure from surface shader.
closure: SurfaceClosureUnion::LambertClosure(
LambertClosure::new(XYZ::new(0.8, 0.8, 0.8))
),
// Fill in intersection data
isects[r.id as usize] = SurfaceIntersection::Hit {
intersection_data: SurfaceIntersectionData {
incoming: wr.dir,
t: t,
pos: pos,
pos_err: pos_err,
nor: shading_normal,
nor_g: geo_normal,
uv: (0.0, 0.0), // TODO
local_space: mat_space,
},
// TODO: get surface closure from surface shader.
closure: SurfaceClosureUnion::LambertClosure(
LambertClosure::new(XYZ::new(0.8, 0.8, 0.8)),
),
// closure:
// SurfaceClosureUnion::GTRClosure(
// GTRClosure::new(XYZ::new(0.8, 0.8, 0.8),
// 0.1,
// 2.0,
// 1.0)),
};
r.max_t = t;
}
};
r.max_t = t;
}
}
}
}
);
},
);
}
}