Skip to content

USDMeshLight - #6970

Closed
ericmehl wants to merge 11 commits into
GafferHQ:mainfrom
ericmehl:usdMeshLight
Closed

USDMeshLight#6970
ericmehl wants to merge 11 commits into
GafferHQ:mainfrom
ericmehl:usdMeshLight

Conversation

@ericmehl

Copy link
Copy Markdown
Contributor

This adds USDMeshLight support for Arnold and RenderMan. The USD spec, in the default mode, calls for taking the mesh light color texture from the surface shader, if one exists. There are a couple of other simpler modes I haven't implemented yet, but I can add those in a follow-up PR or add them on here.

There's also new support in this PR for visualising mesh light textures using the existing renderer-specific texture registration system. I didn't implement the surface texture swapping for those visulisations, but it can also be added once we're happy with the overall direction here.

Checklist

  • I have read the contribution guidelines.
  • I have updated the documentation, if applicable.
  • I have tested my change(s) in the test suite, and added new test cases where necessary.
  • My code follows the Gaffer project's prevailing coding style and conventions.

@ericmehl

Copy link
Copy Markdown
Contributor Author

There was a test failure from GafferUSDUITest.DocumentationTest where it was finding the plugs on USDMeshLight were not documented. I believe that is because we are calling loadShader() in a lambda that is passed to the MeshLight constructor which means the plugs arr present for that test. Unlike other shaders which won't have been loaded yet.

I didn't see a particularly good fallback, but USD has updated their attribute descriptions in the USDLux schema, so I think it might be ready to use again.

That along with a placeholder description for the Arnold parameters satisfies the documentation requirement for all plugs.

@johnhaddon johnhaddon left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks Eric! Comments inline as usual. In addition to those, I think it's also worth adding a conversion to 3Delight, since folks have been asking for better support on Discord recently. Similarly I think there's a decent case for adding for Cycles too, since the existing CyclesMeshLight is a bit of a weird anomaly (it doesn't actually make a light).

Comment thread startup/gui/menus.py Outdated
Comment thread python/GafferUSDTest/USDMeshLightTest.py Outdated
Comment thread src/IECoreArnold/ShaderNetworkAlgo.cpp Outdated
Comment thread src/IECoreArnold/ShaderNetworkAlgo.cpp Outdated
Comment thread src/IECoreArnold/Renderer.cpp Outdated
Comment thread src/IECoreRenderMan/ShaderNetworkAlgo.cpp Outdated
Comment thread src/IECoreRenderMan/ShaderNetworkAlgo.cpp Outdated
Comment thread src/IECoreRenderMan/Attributes.cpp Outdated
Comment thread Changes.md Outdated
Comment thread python/GafferUSDUI/USDShaderUI.py
@ericmehl
ericmehl force-pushed the usdMeshLight branch 2 times, most recently from 5f7c795 to 6afc7e7 Compare June 17, 2026 21:01
@ericmehl

Copy link
Copy Markdown
Contributor Author

This is in good shape now for a look at the existing fixups. I added one more commit for Arnold : github.com//pull/6970/commits/6afc7e75cfc387638757eeab12165ed7635d0885. That corrects the behavior to multiply the light color with the surface glow response, creating a new multiply shader when needed.

We decided that if a mesh light has an input to color, we leave it alone as the user is probably intending for that shader to be the final light result, not multiplied by any surface glow that may exist.

I'll take care of the Cycles and 3Delight conversions next.

@johnhaddon

Copy link
Copy Markdown
Member

We decided that if a mesh light has an input to color, we leave it alone as the user is probably intending for that shader to be the final light result, not multiplied by any surface glow that may exist.

Sorry, I should have read the UsdLux docs when we were having this discussion. They seem to be fairly clear that we should multiply the two together in this case :

the base color seen by light rays (which is then modulated by all of the other LightAPI controls) is the multiplication of the color feeding the emission/glow input of the Material (i.e. its surface or volume shader) with the scalar or pattern input to inputs:color

Might be worth checking via usdview though, to see what the behaviour is in practice.

@ericmehl

Copy link
Copy Markdown
Contributor Author

I've updated the Arnold side of things here, and another small fix noted above.

  • I still need to change the behavior of textures when there's an input to the light color on the MeshLight itself. That should be connected to the new multiply shader I'm conditionally adding to the Arnold network.
  • The Python binding for what is now called convertUSDMeshLightAttributes() is plucked from some other similar bindings. I don't fully understand if the _copy parameter is needed. Who might be telling this whether or not to use a copy? And is the GIL release needed? I need to look a bit more at the GIL stuff to understand that.

Other than those and the RenderMan side which I think is close to having a plan ready, these updates are ready for a new look.

@johnhaddon

johnhaddon commented Jun 23, 2026

Copy link
Copy Markdown
Member

I've updated the Arnold side of things here, and another small fix noted above.

Thanks Eric - Arnold stuff seems good. Might need to split the first commit up a bit when rebasing - the shaderNetworkAttribute() stuff in the original commit is worthwhile on its own, but not necessary for the USD handling as far as I can see.

I don't fully understand if the _copy parameter is needed.

Because the C++ function returns ConstPtr, we have a problem : Python has no concept of const, and Boost::Python only wants a Ptr. We can either cast, or copy. In some places, casting would be really dangerous - for example, doing it for ValuePlug::getValue() would let a Python scripter accidentally modify a const value held in Gaffer's compute cache, changing the result of future computes! But at times we want to avoid the overhead of the copy, or for unit testing we care that we're getting the original object. So some functions have the _copy parameter. In this case, since the only real use case is testing, I don't think it's super important either way - I think the way you have it is fine.

And is the GIL release needed?

There are two main reasons to release the GIL :

  1. The C++ operation may depend on parallel work, which might re-enter Python on a different thread. If we don't release in this case, we get deadlock.
  2. Letting another thread do Python work while this thread does heavy C++-only work.

In this case, 1 doesn't apply, but 2 could in theory, so I think the way you have it is fine.

@ericmehl
ericmehl force-pushed the usdMeshLight branch 3 times, most recently from 4f3d75f to 3118fae Compare June 29, 2026 14:53
@ericmehl
ericmehl force-pushed the usdMeshLight branch 2 times, most recently from a37184d to a7632fd Compare July 24, 2026 20:47
@ericmehl
ericmehl force-pushed the usdMeshLight branch 2 times, most recently from fad8deb to a980a06 Compare August 12, 2026 19:30
johnhaddon and others added 11 commits August 12, 2026 17:03
This fixes errors like the following :

```
Error: FAIL: testDisplayDriverAndPromotion (GafferSceneTest.CatalogueTest.CatalogueTest.testDisplayDriverAndPromotion)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/__w/gaffer/gaffer/build/python/GafferSceneTest/CatalogueTest.py", line 387, in testDisplayDriverAndPromotion
    self.sendImage( r["out"], s["b"]["c"] )
  File "/__w/gaffer/gaffer/build/python/GafferSceneTest/CatalogueTest.py", line 61, in sendImage
    result = GafferSceneTest.DisplayTest.Driver.sendImage( image, GafferScene.Catalogue.displayDriverServer().portNumber(), extraParameters, close = close )
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/__w/gaffer/gaffer/build/python/GafferSceneTest/DisplayTest.py", line 150, in sendImage
    driver.close()
  File "/__w/gaffer/gaffer/build/python/GafferSceneTest/DisplayTest.py", line 115, in close
    with GafferTest.ParallelAlgoTest.UIThreadCallHandler() as h :
  File "/__w/gaffer/gaffer/build/python/GafferTest/ParallelAlgoTest.py", line 70, in __exit__
    raise AssertionError( "UIThread call queue not empty" )
AssertionError: UIThread call queue not empty
```

We have only seen these on CI, but they can be reproduced artificially by inserting `time.sleep()` before L119 in DisplayTest. The problem is that with the right thread timings, the Catalogue can save the image to disk and request an extra UI thread call before `close()` calls `assertDone()`. The `sleep()` just makes the bad timing inevitable, but it could occur naturally when the CI machine is under unusual load.

The solution is to move the `close()` call under the same UIThreadCallHandler as the one used to check for saving, and only call `assertDone()` once at the end.

The weaving of `DisplayTest.sendImage()` and `CatalogueTest.sendImage()` with all their various permutations is getting a bit much. It does seem tempting to attempt an approach based on `assertEventually()` instead.
These match our current descriptions for Arnold light plugs.
@ericmehl

Copy link
Copy Markdown
Contributor Author

The latest commits have overhauled the way I'm handling the USDMeshLight and also adds 3Delight and Cycles rendering. It's quite a big PR now, but there are patterns I've tried to make common between each renderer where possible that will hopefully make it easier to track.

My main reservation now is that the Arnold backend is treating the lights more like Arnold mesh lights and not so much like USD lights. Originally I think that was intentional to get the better sampling like we get from ArnoldMeshLight, but I think we might instead need to follow the example of the other renderers and duplicate the mesh for the mesh light portion.

But I'm holding off on that for now to get some other eyes on this before I make that departure.

@johnhaddon

Copy link
Copy Markdown
Member

Thanks Eric! Could you reopen this from a branch on the main repo please, so renderer-specific CI can do its thing?

@ericmehl ericmehl mentioned this pull request Aug 13, 2026
4 tasks
@ericmehl

Copy link
Copy Markdown
Contributor Author

Closing this in favor of #7098 for test running.

@ericmehl ericmehl closed this Aug 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants