From c693e728416125c75d61209b17647f941901edda Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Wed, 27 Aug 2025 20:57:47 +0200 Subject: [PATCH] Fix #145254: Cycles normal map node strength wrong results with displacement Can not use tangent space interpolation in this case, as the tangent space is for the undisplaced normal. Pull Request: https://projects.blender.org/blender/blender/pulls/145273 --- .../kernel/osl/shaders/node_normal_map.osl | 85 +++++++++++-------- intern/cycles/kernel/svm/tex_coord.h | 25 ++++-- 2 files changed, 68 insertions(+), 42 deletions(-) diff --git a/intern/cycles/kernel/osl/shaders/node_normal_map.osl b/intern/cycles/kernel/osl/shaders/node_normal_map.osl index 14133d73839..321ec158366 100644 --- a/intern/cycles/kernel/osl/shaders/node_normal_map.osl +++ b/intern/cycles/kernel/osl/shaders/node_normal_map.osl @@ -13,6 +13,7 @@ shader node_normal_map(float Strength = 1.0, { color mcolor = 2.0 * color(Color[0] - 0.5, Color[1] - 0.5, Color[2] - 0.5); int is_backfacing = backfacing(); + int linear_interpolate_strength = 0; if (space == "tangent") { vector tangent; @@ -20,8 +21,25 @@ shader node_normal_map(float Strength = 1.0, float tangent_sign; float is_smooth = 0.0; + if (!getattribute(attr_name, tangent) || !getattribute(attr_sign_name, tangent_sign)) { + Normal = N; + return; + } + getattribute("geom:is_smooth", is_smooth); - if (!is_smooth) { + if (is_smooth) { + if (getattribute("undisplaced_N", ninterp)) { + /* Can't interpolate in tangent space as the displaced normal is not used + * for the tangent frame. */ + linear_interpolate_strength = 1; + } + else if (getattribute("geom:normal_map_normal", ninterp)) { + } + else { + ninterp = N; + } + } + else { ninterp = normalize(transform("world", "object", Ng)); /* the normal is already inverted, which is too soon for the math here */ @@ -30,46 +48,44 @@ shader node_normal_map(float Strength = 1.0, } } - // get _unnormalized_ interpolated normal and tangent - if (getattribute(attr_name, tangent) && getattribute(attr_sign_name, tangent_sign) && - (!is_smooth || getattribute("geom:normal_map_normal", ninterp))) - { - /* apply normal map */ - vector B = tangent_sign * cross(ninterp, tangent); + /* apply normal map */ + vector B = tangent_sign * cross(ninterp, tangent); - /* apply strength */ + /* apply strength */ + if (!linear_interpolate_strength) { mcolor[0] *= Strength; mcolor[1] *= Strength; mcolor[2] = mix(1.0, mcolor[2], clamp(Strength, 0.0, 1.0)); - - Normal = normalize(mcolor[0] * tangent + mcolor[1] * B + mcolor[2] * ninterp); - - /* transform to world space */ - Normal = normalize(transform("object", "world", Normal)); } - else { - Normal = normal(0, 0, 0); + + Normal = normalize(mcolor[0] * tangent + mcolor[1] * B + mcolor[2] * ninterp); + + /* transform to world space */ + Normal = normalize(transform("object", "world", Normal)); + } + else { + linear_interpolate_strength = 1; + + if (space == "object") { + Normal = normalize(transform("object", "world", vector(mcolor))); } - } - else if (space == "object") { - Normal = normalize(transform("object", "world", vector(mcolor))); - } - else if (space == "world") { - Normal = normalize(vector(mcolor)); - } - else if (space == "blender_object") { - /* strange blender convention */ - mcolor[1] = -mcolor[1]; - mcolor[2] = -mcolor[2]; + else if (space == "world") { + Normal = normalize(vector(mcolor)); + } + else if (space == "blender_object") { + /* strange blender convention */ + mcolor[1] = -mcolor[1]; + mcolor[2] = -mcolor[2]; - Normal = normalize(transform("object", "world", vector(mcolor))); - } - else if (space == "blender_world") { - /* strange blender convention */ - mcolor[1] = -mcolor[1]; - mcolor[2] = -mcolor[2]; + Normal = normalize(transform("object", "world", vector(mcolor))); + } + else if (space == "blender_world") { + /* strange blender convention */ + mcolor[1] = -mcolor[1]; + mcolor[2] = -mcolor[2]; - Normal = normalize(vector(mcolor)); + Normal = normalize(vector(mcolor)); + } } /* invert normal for backfacing polygons */ @@ -77,6 +93,7 @@ shader node_normal_map(float Strength = 1.0, Normal = -Normal; } - if (Strength != 1.0 && space != "tangent") + if (linear_interpolate_strength && Strength != 1.0) { Normal = normalize(N + (Normal - N) * max(Strength, 0.0)); + } } diff --git a/intern/cycles/kernel/svm/tex_coord.h b/intern/cycles/kernel/svm/tex_coord.h index 012722b35b3..b36e15071a8 100644 --- a/intern/cycles/kernel/svm/tex_coord.h +++ b/intern/cycles/kernel/svm/tex_coord.h @@ -337,6 +337,8 @@ ccl_device_noinline void svm_node_normal_map(KernelGlobals kg, const bool is_backfacing = (sd->flag & SD_BACKFACING) != 0; float3 N; float strength = stack_load_float(stack, strength_offset); + bool linear_interpolate_strength = false; + if (space == NODE_NORMAL_MAP_TANGENT) { /* tangent space */ if (sd->object == OBJECT_NONE || (sd->type & PRIMITIVE_TRIANGLE) == 0) { @@ -366,6 +368,9 @@ ccl_device_noinline void svm_node_normal_map(KernelGlobals kg, if (attr_undisplaced_normal.offset != ATTR_STD_NOT_FOUND) { normal = primitive_surface_attribute(kg, sd, attr_undisplaced_normal, false, false).val; + /* Can't interpolate in tangent space as the displaced normal is not used + * for the tangent frame. */ + linear_interpolate_strength = true; } else { normal = triangle_smooth_normal_unnormalized(kg, sd, sd->Ng, sd->prim, sd->u, sd->v); @@ -382,9 +387,11 @@ ccl_device_noinline void svm_node_normal_map(KernelGlobals kg, object_inverse_normal_transform(kg, sd, &normal); } /* Apply strength in the tangent case. */ - color.x *= strength; - color.y *= strength; - color.z = mix(1.0f, color.z, saturatef(strength)); + if (!linear_interpolate_strength) { + color.x *= strength; + color.y *= strength; + color.z = mix(1.0f, color.z, saturatef(strength)); + } /* apply normal map */ const float3 B = sign * cross(normal, tangent); @@ -399,6 +406,8 @@ ccl_device_noinline void svm_node_normal_map(KernelGlobals kg, } } else { + linear_interpolate_strength = true; + /* strange blender convention */ if (space == NODE_NORMAL_MAP_BLENDER_OBJECT || space == NODE_NORMAL_MAP_BLENDER_WORLD) { color.y = -color.y; @@ -419,12 +428,12 @@ ccl_device_noinline void svm_node_normal_map(KernelGlobals kg, if (is_backfacing) { N = -N; } + } - /* Apply strength in all but tangent space. */ - if (strength != 1.0f) { - strength = max(strength, 0.0f); - N = safe_normalize(sd->N + (N - sd->N) * strength); - } + /* Use simple linear linear interpolation if we can't do it in tangent space. */ + if (linear_interpolate_strength && strength != 1.0f) { + strength = max(strength, 0.0f); + N = safe_normalize(sd->N + (N - sd->N) * strength); } if (is_zero(N) || !isfinite_safe(N)) {