Fix T89037: Cycles: Backfacing node can be wrong for lights with negative scale

When rendering in the viewport (or probably on instanced objects, but I didn't
test that), emissive objects whose scale is negative give the wrong value on the
"backfacing" input when multiple sampling is enabled.

The underlying problem was a corner case in how normal transformation is handled,
which is generally a bit messy.

From what I can tell, the pattern appears to be:
- If you first transform vertices to world space and then compute the normal from
  them (as triangle light samping, MNEE and light tree do), you need to flip
  whenever the transform has negative scale regardless of whether the transform
  has been applied
- If you compute the normal in object space and then transform it to world space
  (as the regular shader_setup_from_ray path does), you only need to flip if the
  transform was already applied and was negative
- If you get the normal from a local intersection result (as bevel and SSS do),
  you only need to flip if the transform was already applied and was negative
- If you get the normal from vertex normals, you don't need to do anything since
  the host-side code does the flip for you (arguably it'd be more consistent to
  do this in the kernel as well, but meh, not worth the potential slowdown)

So, this patch fixes the logic in the triangle emission code.

Also, turns out that the MNEE code had the same problem and was also having
problems in the viewport on negative-scale objects, this is also fixed now.

Differential Revision: https://developer.blender.org/D16952
This commit is contained in:
Lukas Stockner 2023-01-10 02:32:16 +01:00
parent 317a5f61f0
commit c41601becd
Notes: blender-bot 2023-02-14 05:43:04 +01:00
Referenced by issue #89037, Cycles backfacing node doesn't work on objects with negative scale
9 changed files with 20 additions and 12 deletions

View File

@ -58,7 +58,7 @@ ccl_device_noinline void motion_triangle_shader_setup(KernelGlobals kg,
sd->P = motion_triangle_point_from_uv(kg, sd, isect_object, isect_prim, sd->u, sd->v, verts);
/* Compute face normal. */
float3 Ng;
if (sd->object_flag & SD_OBJECT_NEGATIVE_SCALE_APPLIED) {
if (object_negative_scale_applied(sd->object_flag)) {
Ng = normalize(cross(verts[2] - verts[0], verts[1] - verts[0]));
}
else {

View File

@ -201,6 +201,11 @@ ccl_device_inline void object_normal_transform(KernelGlobals kg,
*N = normalize(transform_direction_transposed(&tfm, *N));
}
ccl_device_inline bool object_negative_scale_applied(const int object_flag)
{
return ((object_flag & SD_OBJECT_NEGATIVE_SCALE) && (object_flag & SD_OBJECT_TRANSFORM_APPLIED));
}
/* Transform direction vector from object to world space */
ccl_device_inline void object_dir_transform(KernelGlobals kg,

View File

@ -21,7 +21,7 @@ ccl_device_inline float3 triangle_normal(KernelGlobals kg, ccl_private ShaderDat
const float3 v2 = kernel_data_fetch(tri_verts, tri_vindex.w + 2);
/* return normal */
if (sd->object_flag & SD_OBJECT_NEGATIVE_SCALE_APPLIED) {
if (object_negative_scale_applied(sd->object_flag)) {
return normalize(cross(v2 - v0, v1 - v0));
}
else {
@ -50,7 +50,7 @@ ccl_device_inline void triangle_point_normal(KernelGlobals kg,
/* get object flags */
int object_flag = kernel_data_fetch(object_flag, object);
/* compute normal */
if (object_flag & SD_OBJECT_NEGATIVE_SCALE_APPLIED) {
if (object_negative_scale_applied(object_flag)) {
*Ng = normalize(cross(v2 - v0, v1 - v0));
}
else {

View File

@ -176,8 +176,9 @@ ccl_device_forceinline void mnee_setup_manifold_vertex(KernelGlobals kg,
/* Geometric normal. */
vtx->ng = normalize(cross(dp_du, dp_dv));
if (sd_vtx->object_flag & SD_OBJECT_NEGATIVE_SCALE_APPLIED)
if (sd_vtx->object_flag & SD_OBJECT_NEGATIVE_SCALE) {
vtx->ng = -vtx->ng;
}
/* Shading normals: Interpolate normals between vertices. */
float n_len;

View File

@ -120,7 +120,7 @@ ccl_device_inline bool subsurface_disk(KernelGlobals kg,
if (path_flag & PATH_RAY_SUBSURFACE_BACKFACING) {
hit_Ng = -hit_Ng;
}
if (object_flag & SD_OBJECT_NEGATIVE_SCALE_APPLIED) {
if (object_negative_scale_applied(object_flag)) {
hit_Ng = -hit_Ng;
}

View File

@ -146,7 +146,7 @@ ccl_device_forceinline bool triangle_light_sample(KernelGlobals kg,
/* flip normal if necessary */
const int object_flag = kernel_data_fetch(object_flag, object);
if (object_flag & SD_OBJECT_NEGATIVE_SCALE_APPLIED) {
if (object_flag & SD_OBJECT_NEGATIVE_SCALE) {
ls->Ng = -ls->Ng;
}
ls->eval_fac = 1.0f;

View File

@ -224,7 +224,7 @@ ccl_device float3 svm_bevel(
float3 hit_Ng = isect.Ng[hit];
int object = isect.hits[hit].object;
int object_flag = kernel_data_fetch(object_flag, object);
if (object_flag & SD_OBJECT_NEGATIVE_SCALE_APPLIED) {
if (object_negative_scale_applied(object_flag)) {
hit_Ng = -hit_Ng;
}

View File

@ -850,8 +850,8 @@ enum ShaderDataObjectFlag {
SD_OBJECT_MOTION = (1 << 1),
/* Vertices have transform applied. */
SD_OBJECT_TRANSFORM_APPLIED = (1 << 2),
/* Vertices have negative scale applied. */
SD_OBJECT_NEGATIVE_SCALE_APPLIED = (1 << 3),
/* The object's transform applies a negative scale. */
SD_OBJECT_NEGATIVE_SCALE = (1 << 3),
/* Object has a volume shader. */
SD_OBJECT_HAS_VOLUME = (1 << 4),
/* Object intersects AABB of an object with volume shader. */
@ -873,7 +873,7 @@ enum ShaderDataObjectFlag {
SD_OBJECT_CAUSTICS = (SD_OBJECT_CAUSTICS_CASTER | SD_OBJECT_CAUSTICS_RECEIVER),
SD_OBJECT_FLAGS = (SD_OBJECT_HOLDOUT_MASK | SD_OBJECT_MOTION | SD_OBJECT_TRANSFORM_APPLIED |
SD_OBJECT_NEGATIVE_SCALE_APPLIED | SD_OBJECT_HAS_VOLUME |
SD_OBJECT_NEGATIVE_SCALE | SD_OBJECT_HAS_VOLUME |
SD_OBJECT_INTERSECTS_VOLUME | SD_OBJECT_SHADOW_CATCHER |
SD_OBJECT_HAS_VOLUME_ATTRIBUTES | SD_OBJECT_CAUSTICS |
SD_OBJECT_HAS_VOLUME_MOTION)

View File

@ -435,6 +435,10 @@ void ObjectManager::device_update_object_transform(UpdateObjectTransformState *s
state->have_motion = true;
}
if (transform_negative_scale(tfm)) {
flag |= SD_OBJECT_NEGATIVE_SCALE;
}
if (geom->geometry_type == Geometry::MESH || geom->geometry_type == Geometry::POINTCLOUD) {
/* TODO: why only mesh? */
Mesh *mesh = static_cast<Mesh *>(geom);
@ -970,8 +974,6 @@ void ObjectManager::apply_static_transforms(DeviceScene *dscene, Scene *scene, P
}
object_flag[i] |= SD_OBJECT_TRANSFORM_APPLIED;
if (geom->transform_negative_scaled)
object_flag[i] |= SD_OBJECT_NEGATIVE_SCALE_APPLIED;
}
}