From 6adfd91657e07e5b749fb3a7aaeeeec88fb15d04 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Tue, 15 Jan 2013 16:35:05 +0000 Subject: Fix #33830: cycles normal mapping was not quite correct, was not correctly respecting the assumption that normal and tangent are interpolated without normalization. --- intern/cycles/kernel/shaders/node_normal_map.osl | 20 +++++++++---- intern/cycles/kernel/svm/svm_tex_coord.h | 17 ++++++----- intern/cycles/render/mesh.cpp | 38 +++++++++++++++++++----- intern/cycles/render/mesh.h | 1 + intern/cycles/render/nodes.cpp | 2 ++ intern/cycles/render/object.cpp | 30 +++---------------- 6 files changed, 62 insertions(+), 46 deletions(-) (limited to 'intern') diff --git a/intern/cycles/kernel/shaders/node_normal_map.osl b/intern/cycles/kernel/shaders/node_normal_map.osl index dc25eb8539f..21382fab06f 100644 --- a/intern/cycles/kernel/shaders/node_normal_map.osl +++ b/intern/cycles/kernel/shaders/node_normal_map.osl @@ -31,15 +31,23 @@ shader node_normal_map( if (space == "Tangent") { vector tangent; + vector ninterp; float tangent_sign; - getattribute(attr_name, tangent); - getattribute(attr_sign_name, tangent_sign); + // get _unnormalized_ interpolated normal and tangent + if(!getattribute(attr_name, tangent) || + !getattribute(attr_sign_name, tangent_sign) || + !getattribute("geom:N", ninterp)) { + Normal = normal(0, 0, 0); + } + else { + // apply normal map + vector B = tangent_sign * cross(ninterp, tangent); + Normal = normalize(mcolor[0] * tangent + mcolor[1] * B + mcolor[2] * ninterp); - tangent = transform("object", "world", tangent); - - vector B = tangent_sign * cross(NormalIn, tangent); - Normal = normalize(mcolor[0] * tangent + mcolor[1] * B + mcolor[2] * NormalIn); + // transform to world space + Normal = normalize(transform("object", "world", Normal)); + } } else if (space == "Object") Normal = normalize(transform("object", "world", vector(mcolor))); diff --git a/intern/cycles/kernel/svm/svm_tex_coord.h b/intern/cycles/kernel/svm/svm_tex_coord.h index 7a1af43b625..d793169261d 100644 --- a/intern/cycles/kernel/svm/svm_tex_coord.h +++ b/intern/cycles/kernel/svm/svm_tex_coord.h @@ -248,24 +248,27 @@ __device void svm_node_normal_map(KernelGlobals *kg, ShaderData *sd, float *stac } /* first try to get tangent attribute */ - AttributeElement attr_elem, attr_sign_elem; + AttributeElement attr_elem, attr_sign_elem, attr_normal_elem; int attr_offset = find_attribute(kg, sd, node.z, &attr_elem); int attr_sign_offset = find_attribute(kg, sd, node.w, &attr_sign_elem); + int attr_normal_offset = find_attribute(kg, sd, ATTR_STD_VERTEX_NORMAL, &attr_normal_elem); - if(attr_offset == ATTR_STD_NOT_FOUND || attr_sign_offset == ATTR_STD_NOT_FOUND) { + if(attr_offset == ATTR_STD_NOT_FOUND || attr_sign_offset == ATTR_STD_NOT_FOUND || attr_normal_offset == ATTR_STD_NOT_FOUND) { stack_store_float3(stack, normal_offset, make_float3(0.0f, 0.0f, 0.0f)); return; } - /* ensure orthogonal and normalized (interpolation breaks it) */ + /* get _unnormalized_ interpolated normal and tangent */ float3 tangent = primitive_attribute_float3(kg, sd, attr_elem, attr_offset, NULL, NULL); float sign = primitive_attribute_float(kg, sd, attr_sign_elem, attr_sign_offset, NULL, NULL); + float3 normal = primitive_attribute_float3(kg, sd, attr_normal_elem, attr_normal_offset, NULL, NULL); - object_normal_transform(kg, sd, &tangent); - tangent = cross(sd->N, normalize(cross(tangent, sd->N)));; + /* apply normal map */ + float3 B = sign * cross(normal, tangent); + N = normalize(color.x * tangent + color.y * B + color.z * normal); - float3 B = sign * cross(sd->N, tangent); - N = normalize(color.x * tangent + color.y * B + color.z * sd->N); + /* transform to world space */ + object_normal_transform(kg, sd, &N); } else { /* object, world space */ diff --git a/intern/cycles/render/mesh.cpp b/intern/cycles/render/mesh.cpp index d4619dcff55..57776e4cfa6 100644 --- a/intern/cycles/render/mesh.cpp +++ b/intern/cycles/render/mesh.cpp @@ -43,6 +43,7 @@ Mesh::Mesh() need_update = true; transform_applied = false; transform_negative_scaled = false; + transform_normal = transform_identity(); displacement_method = DISPLACE_BUMP; bounds = BoundBox::empty; @@ -94,6 +95,7 @@ void Mesh::clear() transform_applied = false; transform_negative_scaled = false; + transform_normal = transform_identity(); } void Mesh::add_triangle(int v0, int v1, int v2, int shader_, bool smooth_) @@ -151,7 +153,7 @@ void Mesh::add_face_normals() /* don't compute if already there */ if(attributes.find(ATTR_STD_FACE_NORMAL)) return; - + /* get attributes */ Attribute *attr_fN = attributes.add(ATTR_STD_FACE_NORMAL); float3 *fN = attr_fN->data_float3(); @@ -181,6 +183,14 @@ void Mesh::add_face_normals() fN[i] = -fN[i]; } } + + /* expected to be in local space */ + if(transform_applied) { + Transform ntfm = transform_inverse(transform_normal); + + for(size_t i = 0; i < triangles_size; i++) + fN[i] = normalize(transform_direction(&ntfm, fN[i])); + } } void Mesh::add_vertex_normals() @@ -188,7 +198,7 @@ void Mesh::add_vertex_normals() /* don't compute if already there */ if(attributes.find(ATTR_STD_VERTEX_NORMAL)) return; - + /* get attributes */ Attribute *attr_fN = attributes.find(ATTR_STD_FACE_NORMAL); Attribute *attr_vN = attributes.add(ATTR_STD_VERTEX_NORMAL); @@ -232,10 +242,18 @@ void Mesh::pack_normals(Scene *scene, float4 *normal, float4 *vnormal) size_t triangles_size = triangles.size(); uint *shader_ptr = (shader.size())? &shader[0]: NULL; + bool do_transform = transform_applied; + Transform ntfm = transform_normal; + for(size_t i = 0; i < triangles_size; i++) { - normal[i].x = fN[i].x; - normal[i].y = fN[i].y; - normal[i].z = fN[i].z; + float3 fNi = fN[i]; + + if(do_transform) + fNi = normalize(transform_direction(&ntfm, fNi)); + + normal[i].x = fNi.x; + normal[i].y = fNi.y; + normal[i].z = fNi.z; /* stuff shader id in here too */ if(shader_ptr[i] != last_shader || last_smooth != smooth[i]) { @@ -249,8 +267,14 @@ void Mesh::pack_normals(Scene *scene, float4 *normal, float4 *vnormal) size_t verts_size = verts.size(); - for(size_t i = 0; i < verts_size; i++) - vnormal[i] = make_float4(vN[i].x, vN[i].y, vN[i].z, 0.0f); + for(size_t i = 0; i < verts_size; i++) { + float3 vNi = vN[i]; + + if(do_transform) + vNi = normalize(transform_direction(&ntfm, vNi)); + + vnormal[i] = make_float4(vNi.x, vNi.y, vNi.z, 0.0f); + } } void Mesh::pack_verts(float4 *tri_verts, float4 *tri_vindex, size_t vert_offset) diff --git a/intern/cycles/render/mesh.h b/intern/cycles/render/mesh.h index b83752ad8df..dca1b00e1ff 100644 --- a/intern/cycles/render/mesh.h +++ b/intern/cycles/render/mesh.h @@ -90,6 +90,7 @@ public: BoundBox bounds; bool transform_applied; bool transform_negative_scaled; + Transform transform_normal; DisplacementMethod displacement_method; /* Update Flags */ diff --git a/intern/cycles/render/nodes.cpp b/intern/cycles/render/nodes.cpp index d1297c9dc94..cc71af58b67 100644 --- a/intern/cycles/render/nodes.cpp +++ b/intern/cycles/render/nodes.cpp @@ -3257,6 +3257,8 @@ void NormalMapNode::attributes(AttributeRequestSet *attributes) attributes->add(ustring((string(attribute.c_str()) + ".tangent").c_str())); attributes->add(ustring((string(attribute.c_str()) + ".tangent_sign").c_str())); } + + attributes->add(ATTR_STD_VERTEX_NORMAL); } ShaderNode::attributes(attributes); diff --git a/intern/cycles/render/object.cpp b/intern/cycles/render/object.cpp index a89f8afd251..8d8087266c1 100644 --- a/intern/cycles/render/object.cpp +++ b/intern/cycles/render/object.cpp @@ -91,38 +91,16 @@ void Object::apply_transform() for(size_t i = 0; i < mesh->curve_keys.size(); i++) mesh->curve_keys[i].co = transform_point(&tfm, mesh->curve_keys[i].co); - Attribute *attr_tangent = mesh->curve_attributes.find(ATTR_STD_CURVE_TANGENT); - Attribute *attr_fN = mesh->attributes.find(ATTR_STD_FACE_NORMAL); - Attribute *attr_vN = mesh->attributes.find(ATTR_STD_VERTEX_NORMAL); - - Transform ntfm = transform_transpose(transform_inverse(tfm)); + /* store matrix to transform later. when accessing these as attributes we + * do not want the transform to be applied for consistency between static + * and dynamic BVH, so we do it on packing. */ + mesh->transform_normal = transform_transpose(transform_inverse(tfm)); /* we keep normals pointing in same direction on negative scale, notify * mesh about this in it (re)calculates normals */ if(transform_negative_scale(tfm)) mesh->transform_negative_scaled = true; - if(attr_fN) { - float3 *fN = attr_fN->data_float3(); - - for(size_t i = 0; i < mesh->triangles.size(); i++) - fN[i] = transform_direction(&ntfm, fN[i]); - } - - if(attr_vN) { - float3 *vN = attr_vN->data_float3(); - - for(size_t i = 0; i < mesh->verts.size(); i++) - vN[i] = transform_direction(&ntfm, vN[i]); - } - - if(attr_tangent) { - float3 *tangent = attr_tangent->data_float3(); - - for(size_t i = 0; i < mesh->curve_keys.size(); i++) - tangent[i] = transform_direction(&tfm, tangent[i]); - } - if(bounds.valid()) { mesh->compute_bounds(); compute_bounds(false, 0.0f); -- cgit v1.2.3