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
This commit is contained in:
Brecht Van Lommel
2025-08-27 20:57:47 +02:00
parent 6f8978c71a
commit c693e72841
2 changed files with 68 additions and 42 deletions

View File

@@ -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));
}
}

View File

@@ -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<float3>(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)) {