USD: Use nodes for alpha-clip behavior instead of material properties
EEVEE-next has removed the MA_BM_CLIP / alpha_threshold material properties in favor of using nodes for equivalent functionality. This changes USD to build and traverse node graphs during import and export accordingly. Indirectly this allows Cycles to correctly render such materials now too. A complicating factor is that the UsdPreviewSurface defines its opacity threshold using greater-than-equals[1], which Blender does not support (and for which was technically already incorrect as EEVEE-legacy only used greater-than for its shaders). Due to this we actually need to use 2 nodes: A less-than, followed by a one-minus invert, to arrive at the proper value. We'll translate UsdPreviewSurface to this form on Import. For Export we will look for either this 2-node pattern or a Round node plugged into Alpha. Looking for Round is a result of the glTF documentation which recommended the use of this node for thresholds of 0.5[2]. It's a tiny addition that seems reasonable to accommodate. [1] https://openusd.org/release/spec_usdpreviewsurface.html (search for "opacityThreshold") [2] https://docs.blender.org/manual/en/4.2/addons/import_export/scene_gltf2.html#alpha-modes See PR for example images Pull Request: https://projects.blender.org/blender/blender/pulls/122025
This commit is contained in:
committed by
Jesse Yurkovich
parent
21db0daa4e
commit
9daded5d87
@@ -148,53 +148,23 @@ class USDExportTest(AbstractUSDTest):
|
||||
opacity_input = shader.GetInput('opacity')
|
||||
self.assertEqual(opacity_input.HasConnectedSource(), False,
|
||||
"Opacity input should not be connected for opaque material")
|
||||
self.assertAlmostEqual(opacity_input.Get(), 1.0, "Opacity input should be set to 1")
|
||||
self.assertAlmostEqual(opacity_input.Get(), 1.0, 2, "Opacity input should be set to 1")
|
||||
|
||||
# The material already has a texture input to the Base Color.
|
||||
# Now also link this texture to the Alpha input.
|
||||
# Set an opacity threshold appropriate for alpha clipping.
|
||||
mat = bpy.data.materials['Material']
|
||||
bsdf = mat.node_tree.nodes['Principled BSDF']
|
||||
tex_output = bsdf.inputs['Base Color'].links[0].from_node.outputs['Color']
|
||||
alpha_input = bsdf.inputs['Alpha']
|
||||
mat.node_tree.links.new(tex_output, alpha_input)
|
||||
bpy.data.materials['Material'].blend_method = 'CLIP'
|
||||
bpy.data.materials['Material'].alpha_threshold = 0.01
|
||||
export_path = self.tempdir / "alphaclip_material.usda"
|
||||
res = bpy.ops.wm.usd_export(
|
||||
filepath=str(export_path),
|
||||
export_materials=True,
|
||||
evaluation_mode="RENDER",
|
||||
)
|
||||
self.assertEqual({'FINISHED'}, res, f"Unable to export to {export_path}")
|
||||
|
||||
# Inspect and validate the exported USD for the alpha clip case.
|
||||
stage = Usd.Stage.Open(str(export_path))
|
||||
shader_prim = stage.GetPrimAtPath("/root/_materials/Material/Principled_BSDF")
|
||||
# Inspect and validate the exported USD for the alpha clip w/Round node case.
|
||||
shader_prim = stage.GetPrimAtPath("/root/_materials/Clip_With_Round/Principled_BSDF")
|
||||
shader = UsdShade.Shader(shader_prim)
|
||||
opacity_input = shader.GetInput('opacity')
|
||||
opacity_thres_input = shader.GetInput('opacityThreshold')
|
||||
opacity_thresh_input = shader.GetInput('opacityThreshold')
|
||||
self.assertEqual(opacity_input.HasConnectedSource(), True, "Alpha input should be connected")
|
||||
self.assertGreater(opacity_thres_input.Get(), 0.0, "Opacity threshold input should be > 0")
|
||||
self.assertAlmostEqual(opacity_thresh_input.Get(), 0.5, 2, "Opacity threshold input should be 0.5")
|
||||
|
||||
# Modify material again, this time with alpha blend.
|
||||
bpy.data.materials['Material'].blend_method = 'BLEND'
|
||||
export_path = self.tempdir / "alphablend_material.usda"
|
||||
res = bpy.ops.wm.usd_export(
|
||||
filepath=str(export_path),
|
||||
export_materials=True,
|
||||
evaluation_mode="RENDER",
|
||||
)
|
||||
self.assertEqual({'FINISHED'}, res, f"Unable to export to {export_path}")
|
||||
|
||||
# Inspect and validate the exported USD for the alpha blend case.
|
||||
stage = Usd.Stage.Open(str(export_path))
|
||||
shader_prim = stage.GetPrimAtPath("/root/_materials/Material/Principled_BSDF")
|
||||
# Inspect and validate the exported USD for the alpha clip w/LessThan+Invert node case.
|
||||
shader_prim = stage.GetPrimAtPath("/root/_materials/Clip_With_LessThanInvert/Principled_BSDF")
|
||||
shader = UsdShade.Shader(shader_prim)
|
||||
opacity_input = shader.GetInput('opacity')
|
||||
opacity_thres_input = shader.GetInput('opacityThreshold')
|
||||
opacity_thresh_input = shader.GetInput('opacityThreshold')
|
||||
self.assertEqual(opacity_input.HasConnectedSource(), True, "Alpha input should be connected")
|
||||
self.assertEqual(opacity_thres_input.Get(), None, "Opacity threshold should not be specified for alpha blend")
|
||||
self.assertAlmostEqual(opacity_thresh_input.Get(), 0.2, 2, "Opacity threshold input should be 0.2")
|
||||
|
||||
def check_primvar(self, prim, pv_name, pv_typeName, pv_interp, elements_len):
|
||||
pv = UsdGeom.PrimvarsAPI(prim).GetPrimvar(pv_name)
|
||||
|
||||
Reference in New Issue
Block a user