Page MenuHome

Subdiv: support interpolating orco coordinates in subdivision surfaces
ClosedPublic

Authored by Brecht Van Lommel (brecht) on May 17 2022, 6:51 PM.

Details

Summary

This makes changes to the opensubdiv module to support additional vertex data
besides the vertex position, that is smootly interpolated the same way. This is
different than varying data which is interpolated linearly.

Fixes T96596: wrong generated texture coordinates with GPU subdivision. In that
bug lazy subdivision would not interpolate orcos.

Later on, this implementation can also be used to remove the modifier stack
mechanism where modifiers are evaluated a second time for orcos, which is messy
and inefficient. But that's a more risky change, this is just the part to fix
the bug in 3.2.

Diff Detail

Repository
rB Blender

Event Timeline

Brecht Van Lommel (brecht) requested review of this revision.May 17 2022, 6:51 PM
Brecht Van Lommel (brecht) created this revision.

Just to note, I was about to send a simple patch to fix the bug by recreating the layer when creating the wrapper, as indeed there is no interpolation for CD_ORCO. I guess this approach is a bit more robust, and also handles CD_CLOTH_ORCO I see.


diff --git a/source/blender/blenkernel/intern/mesh_wrapper.cc b/source/blender/blenkernel/intern/mesh_wrapper.cc
index c505a74724b..009ac347fd1 100644
--- a/source/blender/blenkernel/intern/mesh_wrapper.cc
+++ b/source/blender/blenkernel/intern/mesh_wrapper.cc
@@ -348,6 +348,8 @@ static Mesh *mesh_wrapper_ensure_subdivision(const Object *ob, Mesh *me)
     CustomData_clear_layer_flag(&me->ldata, CD_NORMAL, CD_FLAG_TEMPORARY);
   }
 
+  const bool has_orco = CustomData_has_layer(&me->vdata, CD_ORCO);
+
   Mesh *subdiv_mesh = BKE_subdiv_to_mesh(subdiv, &mesh_settings, me);
 
   if (use_clnors) {
@@ -367,6 +369,16 @@ static Mesh *mesh_wrapper_ensure_subdivision(const Object *ob, Mesh *me)
   }
 
   if (subdiv_mesh != me) {
+    /* We need to recreate the ORCO layer if there was one, as it not interpolated during
+     * subdivision. */
+    if (has_orco) {
+      float(*orco)[3] = BKE_mesh_vert_coords_alloc(subdiv_mesh, nullptr);
+      float(*layerorco)[3] = (float(*)[3])CustomData_get_layer(&subdiv_mesh->vdata, CD_ORCO);
+      BKE_mesh_orco_verts_transform((Mesh *)ob->data, orco, subdiv_mesh->totvert, 0);
+      memcpy(layerorco, orco, sizeof(float[3]) * subdiv_mesh->totvert);
+      MEM_freeN(orco);
+    }
+
     if (me->runtime.mesh_eval != nullptr) {
       BKE_id_free(nullptr, me->runtime.mesh_eval);
     }

Recreating the layer would not be correct, since you would then get the deformed coordinates instead of the undeformed ones. You really need the CD_ORCO created before e.g. an armature modifier evaluation.

Note this is part of a bigger patchset (P2807) that I implemented earlier, it just so happened to fix this bug.

I made P2955 (applies on top of this patch) to use the extra source data introduced here in the draw code for the orco layer, as currently it is doing linear interpolation, like any other custom data layer.

There was a crash with the GPU evaluator where the extra source data was not initialized due to missing orco layer, so I added some logic to invalidate the evaluator and refiner if suddenly an orco layer is present on the mesh.

The BMesh case is now missing, although for drawing it directly indexes into Mesh.vdata, so I don't think this is going to be an issue (unless vertex indices go out of sync).

The only trouble would be the presence of CD_CLOTH_ORCO (which is not needed for drawing). Either the patch evaluation compute shader would need to updated to use proper stride, or we could avoid setting the data in the evaluator (via set_extra_vertex_data_from_orco in this here patch). E.g. if the evaluator type is GPU then don't copy the cloth orco. Then OpenSubdiv_Converter would also need this information somehow.

Sergey Sharybin (sergey) requested changes to this revision.May 18 2022, 9:53 AM

Other than support of configurable length of the vertex-varying data, how this ExtraVertexData idea different from the VaryingData? The proper way to go about it is to extend the varying data API to support variable length data instead of adding another new concept (which is really solving the legacy TODO about it to go away from hard-coded 3-element vectors per vertex).

intern/opensubdiv/opensubdiv_topology_refiner_capi.h
41 ↗(On Diff #51578)

I am not convinced this belongs here.

Intuitively, neither topology refiner nor topology itself depends on the number of vertex-varying data, and one would think that it should be possible to modify the amount of vertex-varying data after topology has been refined.

What am i missing here?

This revision now requires changes to proceed.May 18 2022, 9:53 AM

Other than support of configurable length of the vertex-varying data, how this ExtraVertexData idea different from the VaryingData? The proper way to go about it is to extend the varying data API to support variable length data instead of adding another new concept (which is really solving the legacy TODO about it to go away from hard-coded 3-element vectors per vertex).

"Varying" in OpenSubdiv means linear interpolation, which we don't want here.

As for why I did not make the vertex data itself variable length, I found it conceptually simpler to handle this more similiar to varying or face varying data, and treat it as just another type of attribute to be interpolated. Otherwise we'd have to make the code that deals with vertex positions and normals take this into account, to me it seemed cleaner to keep that separated.

For meshes, we are also moving towards storing vertex positions, colors and UVs as a flat array of floats. So that might enable us to pass arrays to opensubdiv without copying, and then the less interleaving of data we do the better.

The only trouble would be the presence of CD_CLOTH_ORCO (which is not needed for drawing). Either the patch evaluation compute shader would need to updated to use proper stride, or we could avoid setting the data in the evaluator (via set_extra_vertex_data_from_orco in this here patch). E.g. if the evaluator type is GPU then don't copy the cloth orco. Then OpenSubdiv_Converter would also need this information somehow.

CD_CLOTH_ORCO is only really needed inside the modifier stack evaluation. It's part of this patch because it's intended to work also inside the modifier stack, but for the GPU subdivision or subdivision wrapper case this really is not needed. We could explicitly free CD_CLOTH_ORCO at the end of modifier stack evaluation to ensure it doesn't end up in GPU subdivision.

intern/opensubdiv/internal/evaluator/eval_output.h
365
  • Rename ExtraVertexData to VertexData
  • Add OpenSubdiv_EvaluatorSettings to replace storing number of channels in topology refiner
  • Remove CD_CLOTH_ORCO layer after modifier evaluation, so we don't wasty memory and time subdividing this

Thanks for the explanations!

Little suggestion to avoid ambiguous "extra" in the comment.

intern/opensubdiv/internal/evaluator/evaluator_impl.h
64
This revision is now accepted and ready to land.May 18 2022, 4:29 PM

@Kévin Dietrich (kevindietrich) I'll commit this patch, would be great if you could submit your GPU subdivision support as its own patch.

Your implementation seems to be doing orco subdivision in a separate shader. I guess it would be more efficient to do it in the same shader where you compute vertex normals and coordinates, if that's possible?