Fix #142022: Cycles undisplaced normal not available

Previously with adaptive subdivision this happened to work with the N
attribute, but that was not meant to be undisplaced. This adds a new
undisplaced_N attribute specifically for this purpose.

For backwards compatibility in Blender 4.5, this also keeps N undisplaced.
But that will be changed in 5.0.

Pull Request: https://projects.blender.org/blender/blender/pulls/142090
This commit is contained in:
Brecht Van Lommel
2025-07-24 18:16:25 +02:00
committed by Brecht Van Lommel
parent 97a5e2ea1b
commit 47f9b7a98e
8 changed files with 35 additions and 20 deletions

View File

@@ -877,6 +877,7 @@ enum AttributeStandard {
ATTR_STD_GENERATED_TRANSFORM,
ATTR_STD_POSITION_UNDEFORMED,
ATTR_STD_POSITION_UNDISPLACED,
ATTR_STD_NORMAL_UNDISPLACED,
ATTR_STD_MOTION_VERTEX_POSITION,
ATTR_STD_MOTION_VERTEX_NORMAL,
ATTR_STD_PARTICLE,

View File

@@ -326,6 +326,8 @@ const char *Attribute::standard_name(AttributeStandard std)
return "undeformed";
case ATTR_STD_POSITION_UNDISPLACED:
return "undisplaced";
case ATTR_STD_NORMAL_UNDISPLACED:
return "undisplaced_N";
case ATTR_STD_MOTION_VERTEX_POSITION:
return "motion_P";
case ATTR_STD_MOTION_VERTEX_NORMAL:
@@ -511,6 +513,7 @@ Attribute *AttributeSet::add(AttributeStandard std, ustring name)
if (geometry->is_mesh()) {
switch (std) {
case ATTR_STD_VERTEX_NORMAL:
case ATTR_STD_NORMAL_UNDISPLACED:
attr = add(name, TypeNormal, ATTR_ELEMENT_VERTEX);
break;
case ATTR_STD_UV:

View File

@@ -706,10 +706,6 @@ void GeometryManager::device_update(Device *device,
if (geom->is_mesh() || geom->is_volume()) {
Mesh *mesh = static_cast<Mesh *>(geom);
if (mesh->need_attribute(scene, ATTR_STD_POSITION_UNDISPLACED)) {
mesh->add_undisplaced();
}
/* Test if we need tessellation and setup normals if required. */
if (mesh->need_tesselation()) {
num_tessellation++;

View File

@@ -550,6 +550,13 @@ void GeometryManager::device_update_attributes(Device *device,
for (AttributeRequest &req : attributes.requests) {
Attribute *attr = geom->attributes.find(req);
/* Keep "N" attribute undisplaced for backwards compatibility in Blender 4.5. */
if (attr && attr->std == ATTR_STD_VERTEX_NORMAL) {
if (Attribute *undisplaced_attr = geom->attributes.find(ATTR_STD_NORMAL_UNDISPLACED)) {
attr = undisplaced_attr;
}
}
if (attr) {
/* force a copy if we need to reallocate all the data */
attr->modified |= attributes_need_realloc[Attribute::kernel_type(*attr)];

View File

@@ -742,25 +742,31 @@ void Mesh::add_vertex_normals()
}
}
void Mesh::add_undisplaced()
void Mesh::add_undisplaced(Scene *scene)
{
AttributeSet &attrs = (subdivision_type == SUBDIVISION_NONE) ? attributes : subd_attributes;
if (need_attribute(scene, ATTR_STD_POSITION_UNDISPLACED) &&
!attributes.find(ATTR_STD_POSITION_UNDISPLACED))
{
/* Copy position to attribute. */
Attribute *attr = attributes.add(ATTR_STD_POSITION_UNDISPLACED);
/* don't compute if already there */
if (attrs.find(ATTR_STD_POSITION_UNDISPLACED)) {
return;
size_t size = attr->buffer_size(this, ATTR_PRIM_GEOMETRY) / sizeof(float3);
std::copy_n(verts.data(), size, attr->data_float3());
}
/* get attribute */
Attribute *attr = attrs.add(ATTR_STD_POSITION_UNDISPLACED);
/* Keep "N" attribute undisplaced for backwards compatibility in Blender 4.5. */
if (((need_attribute(scene, ATTR_STD_VERTEX_NORMAL) && has_true_displacement()) ||
need_attribute(scene, ATTR_STD_NORMAL_UNDISPLACED)) &&
!attributes.find(ATTR_STD_NORMAL_UNDISPLACED))
{
/* Copy vertex normal to attribute */
Attribute *attr_N = attributes.find(ATTR_STD_VERTEX_NORMAL);
if (attr_N) {
Attribute *attr = attributes.add(ATTR_STD_NORMAL_UNDISPLACED);
float3 *data = attr->data_float3();
/* copy verts */
size_t size = attr->buffer_size(this, ATTR_PRIM_GEOMETRY) / sizeof(float3);
if (size) {
std::copy_n(verts.data(), size, data);
size_t size = attr->buffer_size(this, ATTR_PRIM_GEOMETRY) / sizeof(float3);
std::copy_n(attr_N->data_float3(), size, attr->data_float3());
}
}
}

View File

@@ -210,7 +210,7 @@ class Mesh : public Geometry {
void compute_bounds() override;
void apply_transform(const Transform &tfm, const bool apply_to_motion) override;
void add_vertex_normals();
void add_undisplaced();
void add_undisplaced(Scene *scene);
void update_generated(Scene *scene);
void update_tangents(Scene *scene);

View File

@@ -142,6 +142,9 @@ bool GeometryManager::displace(Device *device, Scene *scene, Mesh *mesh, Progres
return false;
}
/* Add undisplaced attributes right before doing displacement. */
mesh->add_undisplaced(scene);
const size_t num_verts = mesh->verts.size();
const size_t num_triangles = mesh->num_triangles();

View File

@@ -75,7 +75,6 @@ void SubdAttributeInterpolation::setup()
bool SubdAttributeInterpolation::support_interp_attribute(const Attribute &attr) const
{
// TODO: Recompute UV tangent
switch (attr.std) {
/* Smooth normals are computed from derivatives, for linear interpolate. */
case ATTR_STD_VERTEX_NORMAL: