Fix #33830: cycles normal mapping was not quite correct, was not correctly
respecting the assumption that normal and tangent are interpolated without normalization.
This commit is contained in:
@@ -31,15 +31,23 @@ shader node_normal_map(
|
|||||||
|
|
||||||
if (space == "Tangent") {
|
if (space == "Tangent") {
|
||||||
vector tangent;
|
vector tangent;
|
||||||
|
vector ninterp;
|
||||||
float tangent_sign;
|
float tangent_sign;
|
||||||
|
|
||||||
getattribute(attr_name, tangent);
|
// get _unnormalized_ interpolated normal and tangent
|
||||||
getattribute(attr_sign_name, tangent_sign);
|
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);
|
// transform to world space
|
||||||
|
Normal = normalize(transform("object", "world", Normal));
|
||||||
vector B = tangent_sign * cross(NormalIn, tangent);
|
}
|
||||||
Normal = normalize(mcolor[0] * tangent + mcolor[1] * B + mcolor[2] * NormalIn);
|
|
||||||
}
|
}
|
||||||
else if (space == "Object")
|
else if (space == "Object")
|
||||||
Normal = normalize(transform("object", "world", vector(mcolor)));
|
Normal = normalize(transform("object", "world", vector(mcolor)));
|
||||||
|
@@ -248,24 +248,27 @@ __device void svm_node_normal_map(KernelGlobals *kg, ShaderData *sd, float *stac
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* first try to get tangent attribute */
|
/* 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_offset = find_attribute(kg, sd, node.z, &attr_elem);
|
||||||
int attr_sign_offset = find_attribute(kg, sd, node.w, &attr_sign_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));
|
stack_store_float3(stack, normal_offset, make_float3(0.0f, 0.0f, 0.0f));
|
||||||
return;
|
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);
|
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);
|
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);
|
/* apply normal map */
|
||||||
tangent = cross(sd->N, normalize(cross(tangent, sd->N)));;
|
float3 B = sign * cross(normal, tangent);
|
||||||
|
N = normalize(color.x * tangent + color.y * B + color.z * normal);
|
||||||
|
|
||||||
float3 B = sign * cross(sd->N, tangent);
|
/* transform to world space */
|
||||||
N = normalize(color.x * tangent + color.y * B + color.z * sd->N);
|
object_normal_transform(kg, sd, &N);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
/* object, world space */
|
/* object, world space */
|
||||||
|
@@ -43,6 +43,7 @@ Mesh::Mesh()
|
|||||||
need_update = true;
|
need_update = true;
|
||||||
transform_applied = false;
|
transform_applied = false;
|
||||||
transform_negative_scaled = false;
|
transform_negative_scaled = false;
|
||||||
|
transform_normal = transform_identity();
|
||||||
displacement_method = DISPLACE_BUMP;
|
displacement_method = DISPLACE_BUMP;
|
||||||
bounds = BoundBox::empty;
|
bounds = BoundBox::empty;
|
||||||
|
|
||||||
@@ -94,6 +95,7 @@ void Mesh::clear()
|
|||||||
|
|
||||||
transform_applied = false;
|
transform_applied = false;
|
||||||
transform_negative_scaled = false;
|
transform_negative_scaled = false;
|
||||||
|
transform_normal = transform_identity();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Mesh::add_triangle(int v0, int v1, int v2, int shader_, bool smooth_)
|
void Mesh::add_triangle(int v0, int v1, int v2, int shader_, bool smooth_)
|
||||||
@@ -181,6 +183,14 @@ void Mesh::add_face_normals()
|
|||||||
fN[i] = -fN[i];
|
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()
|
void Mesh::add_vertex_normals()
|
||||||
@@ -232,10 +242,18 @@ void Mesh::pack_normals(Scene *scene, float4 *normal, float4 *vnormal)
|
|||||||
size_t triangles_size = triangles.size();
|
size_t triangles_size = triangles.size();
|
||||||
uint *shader_ptr = (shader.size())? &shader[0]: NULL;
|
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++) {
|
for(size_t i = 0; i < triangles_size; i++) {
|
||||||
normal[i].x = fN[i].x;
|
float3 fNi = fN[i];
|
||||||
normal[i].y = fN[i].y;
|
|
||||||
normal[i].z = fN[i].z;
|
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 */
|
/* stuff shader id in here too */
|
||||||
if(shader_ptr[i] != last_shader || last_smooth != smooth[i]) {
|
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();
|
size_t verts_size = verts.size();
|
||||||
|
|
||||||
for(size_t i = 0; i < verts_size; i++)
|
for(size_t i = 0; i < verts_size; i++) {
|
||||||
vnormal[i] = make_float4(vN[i].x, vN[i].y, vN[i].z, 0.0f);
|
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)
|
void Mesh::pack_verts(float4 *tri_verts, float4 *tri_vindex, size_t vert_offset)
|
||||||
|
@@ -90,6 +90,7 @@ public:
|
|||||||
BoundBox bounds;
|
BoundBox bounds;
|
||||||
bool transform_applied;
|
bool transform_applied;
|
||||||
bool transform_negative_scaled;
|
bool transform_negative_scaled;
|
||||||
|
Transform transform_normal;
|
||||||
DisplacementMethod displacement_method;
|
DisplacementMethod displacement_method;
|
||||||
|
|
||||||
/* Update Flags */
|
/* Update Flags */
|
||||||
|
@@ -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").c_str()));
|
||||||
attributes->add(ustring((string(attribute.c_str()) + ".tangent_sign").c_str()));
|
attributes->add(ustring((string(attribute.c_str()) + ".tangent_sign").c_str()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
attributes->add(ATTR_STD_VERTEX_NORMAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
ShaderNode::attributes(attributes);
|
ShaderNode::attributes(attributes);
|
||||||
|
@@ -91,38 +91,16 @@ void Object::apply_transform()
|
|||||||
for(size_t i = 0; i < mesh->curve_keys.size(); i++)
|
for(size_t i = 0; i < mesh->curve_keys.size(); i++)
|
||||||
mesh->curve_keys[i].co = transform_point(&tfm, mesh->curve_keys[i].co);
|
mesh->curve_keys[i].co = transform_point(&tfm, mesh->curve_keys[i].co);
|
||||||
|
|
||||||
Attribute *attr_tangent = mesh->curve_attributes.find(ATTR_STD_CURVE_TANGENT);
|
/* store matrix to transform later. when accessing these as attributes we
|
||||||
Attribute *attr_fN = mesh->attributes.find(ATTR_STD_FACE_NORMAL);
|
* do not want the transform to be applied for consistency between static
|
||||||
Attribute *attr_vN = mesh->attributes.find(ATTR_STD_VERTEX_NORMAL);
|
* and dynamic BVH, so we do it on packing. */
|
||||||
|
mesh->transform_normal = transform_transpose(transform_inverse(tfm));
|
||||||
Transform ntfm = transform_transpose(transform_inverse(tfm));
|
|
||||||
|
|
||||||
/* we keep normals pointing in same direction on negative scale, notify
|
/* we keep normals pointing in same direction on negative scale, notify
|
||||||
* mesh about this in it (re)calculates normals */
|
* mesh about this in it (re)calculates normals */
|
||||||
if(transform_negative_scale(tfm))
|
if(transform_negative_scale(tfm))
|
||||||
mesh->transform_negative_scaled = true;
|
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()) {
|
if(bounds.valid()) {
|
||||||
mesh->compute_bounds();
|
mesh->compute_bounds();
|
||||||
compute_bounds(false, 0.0f);
|
compute_bounds(false, 0.0f);
|
||||||
|
Reference in New Issue
Block a user