Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix VP2 rendering of UsdPreviewSurface with opacityThreshold #3947

Open
wants to merge 8 commits into
base: dev
Choose a base branch
from

Conversation

jufrantz
Copy link
Contributor

@jufrantz jufrantz commented Oct 7, 2024

This PR fixes VP2 support for the UsdPreviewSurface opacityThreshold input, aligning it with Pixar's UsdPreviewSurface Specification.

Note: When opacityThreshold is greater than zero, opacity values less than the threshold will not be rendered, while values equal to or greater than the threshold will be fully visible.

Included Changes:
  • Fixed the UsdPreviewSurfaceLightAPI*.xml fragment graphs to expose opacityThreshold. The ShadeFragment node input was not connected to the MShaderInstance parameter, which had no effect.
  • Updated fragment shader implementations so that non-discarded fragments are fully opaque when opacityThreshold is positive.
  • Aligned the C++ implementation PxrMayaUsdPreviewSurface::compute for software rendering.
  • Added unit tests.
Results:

Snaphots of the scene included in the unit tests. TestOpacityThreshold.usda features 5 planes with materials bound that have textured opacity, with opacityThreshold gradually increasing from 0.0 to 1.0 from left to right.

usdview/HdStorm

opacityThreshold_usdview

vp2RenderDelegate before PR

opacityThreshold_vp2Renderdelegate_before

vp2RenderDelegate after PR

opacityThreshold_vp2Renderdelegate_after

@seando-adsk seando-adsk added the vp2renderdelegate Related to VP2RenderDelegate label Oct 8, 2024
Copy link
Collaborator

@JGamache-autodesk JGamache-autodesk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nicely done! Thank you!

@seando-adsk
Copy link
Collaborator

@jufrantz Unfortunately the preflight failed two different build configurations:

  1. Maya 2022, Interactive, Windows:
======================================================================
ERROR: testOpacityThreshold (__main__.testVP2RenderDelegateUSDPreviewSurface)
----------------------------------------------------------------------
Traceback (most recent call last):
  File ".../maya-usd/test/lib/mayaUsd/render/vp2RenderDelegate\testVP2RenderDelegateUSDPreviewSurface.py", line 285, in testOpacityThreshold
    mayaUsdLib.PrimUpdaterManager.editAsMaya(stageShapeNode + ",/scene/camera")
AttributeError: 'module' object has no attribute 'PrimUpdaterManager'

I believe here you just need to conditionally run your new test:
@unittest.skipUnless(ufeFeatureSetVersion() >= 3, 'Test only available in UFE v3 or greater.')

  1. Maya 2026, Interactive, Linux:
======================================================================
FAIL: testDrawModes (__main__.testVP2RenderDelegateDrawModes.testDrawModes)
----------------------------------------------------------------------
Traceback (most recent call last):
  File ".../maya-usd/test/lib/mayaUsd/render/vp2RenderDelegate/testVP2RenderDelegateDrawModes.py", line 87, in testDrawModes
    self.assertSnapshotClose('%s_cross_all_positive.png' % self._testName, imageVersion)
  File ".../maya-usd/test/lib/mayaUsd/render/vp2RenderDelegate/testVP2RenderDelegateDrawModes.py", line 64, in assertSnapshotClose
    return self.assertImagesClose(baselineImage, snapshotImage)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File ".../maya-usd/test/testUtils/imageUtils.py", line 208, in assertImagesClose
    self.fail(msg)
AssertionError: Images differed by 0.001973237790486565 (max allowed: 0.0001):
  .../maya-usd/test/lib/mayaUsd/render/vp2RenderDelegate/VP2RenderDelegateDrawModesTest/baseline/post-22_11-DrawModes_cross_all_positive.png
  .../maya-usd/test/lib/mayaUsd/render/vp2RenderDelegate/testVP2RenderDelegateDrawModesOutput/DrawModes_cross_all_positive.png

I'm not sure if your changes have affected this test. Maybe @JGamache-autodesk can help with this one?

Sean

@JGamache-autodesk
Copy link
Collaborator

The change also affects the card draw mode (which uses UsdPreviewSurface since 23.02 with an opacityThreshold of 0.1). This means @jufrantz will have to provide a set of updated baseline images for the testVP2RenderDelegateDrawModes.py unit test.

@JGamache-autodesk
Copy link
Collaborator

You can also take these:
updated_test_images.zip

@jufrantz
Copy link
Contributor Author

jufrantz commented Oct 9, 2024

Thank you @seando-adsk @JGamache-autodesk.
I updated baseline images with those you provided for USD > 22.11.
I fixed the test with ufe v2 (editAsMaya was not really needed here, it now uses a maya native camera).
Julien

@seando-adsk seando-adsk assigned jufrantz and unassigned jufrantz Oct 10, 2024
@seando-adsk
Copy link
Collaborator

@jufrantz I re-ran the preflight and it still failed on "Maya 2022, Python 2, interactive, Windows"

======================================================================
FAIL: testOpacityThreshold (__main__.testVP2RenderDelegateUSDPreviewSurface)
----------------------------------------------------------------------
Traceback (most recent call last):
  File ".../maya-usd/test/lib/mayaUsd/render/vp2RenderDelegate\testVP2RenderDelegateUSDPreviewSurface.py", line 310, in testOpacityThreshold
    self.assertSnapshotClose('opacityThreshold.png')
  File ".../maya-usd/test/lib/mayaUsd/render/vp2RenderDelegate\testVP2RenderDelegateUSDPreviewSurface.py", line 60, in assertSnapshotClose
    return self.assertImagesClose(baseline_image, snapshot_image)
  File "...\maya-usd\test\testUtils\imageUtils.py", line 208, in assertImagesClose
    self.fail(msg)
AssertionError: Images differed by 0.0262658122277 (max allowed: 0.0001):

@jufrantz
Copy link
Contributor Author

jufrantz commented Oct 11, 2024

Sorry for this @seando-adsk,
I tried on maya2022 python2 linux and the test is passing.
This might be failing only on windows and I won't have access to a windows setup in the short term.
Are there any additional relevant logs ? Would it be possible to share the snapshot generated by the failing test?
Thank you,
Julien

@jufrantz
Copy link
Contributor Author

Hello,
I found the issue. I was testing with Maya 2022.5 (Maya light API v2), I just tried Maya 2022.0 (API v1) and got the same image diff ratio than the preflight. Since I didn't add support for opacityThreshold with API v1 and adding it would require more work due to fragment graph changes in V2, I'll just disable the test in this case.
Julien

UsdPreviewSurface fragment graph for v1 does not support opacityThreshold input.
Copy link
Collaborator

@JGamache-autodesk JGamache-autodesk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Only one minor comment.

PxrMayaUsdPreviewSurfaceTokens->OpacityThresholdAttrName.GetText());
const MDataHandle opacityThresholdData
= dataBlock.inputValue(opacityThresholdAttr, &status);
CHECK_MSTATUS(status);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you replicate the opacityConnected code you see for opacity, but on opacityThreshold? A connected opacityThreshold should also activate masked mode.

Copy link
Contributor Author

@jufrantz jufrantz Oct 17, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you @JGamache-autodesk for your feedback,

Interesting point, but I think I disagree with this solution.

As you know, marking the whole material as transparent informs the rasterizer to use alpha blending for proper rendering (e.g. GL_BLEND and object sorting). Conversely, opaque material allows the rasterizer to perform simple depth testing and overwrite fragments. Moreover for masked materials, some fragments will be discarded without writing to the depth or color buffers, which still works with simple depth testing.

OpacityThreshold functions similarly to a combination of glTF alphaMode and alphaCutoff. For a material with authored opacity:

  • A zero opacityThreshold is equivalent to glTF.alphaMode = BLEND
  • Positive values would translate to glTF.alphaMode=MASK and glTF.alphaCutoff=opacityThreshold.

Texturing opacityThreshold is akin to texturing both alphaCutoff AND alphaMode: since you can get zero-valued threshold texels, you would expect a partially masked and partially translucent material. In this scenario, to ensure proper rendering of the semi-transparent regions, the only viable solution to me is to render these objects in the transparent pass (that would also still work with alpha cut/discarded regions).

For reference, HdStorm’s implementation of masked detection for GLSLFX UsdPreviewSurface only tests for a constant positive opacityThreshold value, while for translucent it tests opacity input connections and decides that a varying opacity differs from plain one, like the current VP2 shading node implementation.

https://github.com/PixarAnimationStudios/OpenUSD/blob/59992d2178afcebd89273759f2bddfe730e59aa8/pxr/imaging/hdSt/materialNetwork.cpp#L75-L83

    // Next check for authored terminal.opacityThreshold value > 0
    for (auto const& paramIt : terminal.parameters) {
        if (paramIt.first != _tokens->opacityThreshold) continue;


        VtValue const& vtOpacityThreshold = paramIt.second;
        if (vtOpacityThreshold.Get<float>() > 0.0f) {
            return HdStMaterialTagTokens->masked;
        }
    }

Moreover, I may have over-interpreted it but - as I understand it - the USDPreviewSurface Specification discourages a material with a texture used for translucency and mask.

Thus,` the opacityThreshold serves as a switch for how the opacity input is interpreted; this 'translucent or masked' behavior is common in engines and renderers, and makes the UsdPreviewSurface easier to interchange. It does imply, however, that it is not possible to faithfully recreate a glassy/translucent material that also provides an opacity-based mask… so no single-polygon glass leaves.

If we want to improve this, an alternative could be to disallow connections to the opacityThreshold parameter during VP2 shader translation. This restriction can be implemented with MPxShadingNodeOverride.

void PxrMayaUsdPreviewSurfaceShadingNodeOverride::getCustomMappings(

Please let me know if this approach meets your requirements,
Best,
Julien

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good explanation. I agree with you. This would mean the implementation of _GetUsdPreviewSurfaceMaterialTag() I added to materialXFilter.cpp is buggy since it considers textured opacityThreshold as masked.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably no need to forbid connections since the result is not fatal and will render correctly, albeit slowly, in a transparency pass.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
vp2renderdelegate Related to VP2RenderDelegate
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants