Gaffer 0.46.0 and Arnold 5.x LPE Support?

637 views
Skip to first unread message

Sachin Shrestha

unread,
Jun 11, 2018, 11:14:13 AM6/11/18
to gaffer-dev
Hi,

Is there built-in support in gaffer 0.46.0 to add custom Arnold 5 LPEs to the scene graph? We only see the AOVs that currently ship with Arnold 5.x in the arnold output options node. We tried adding a blank output and supplying the necessary parameters like below:
















However, if we inspect the generated .ass file we do not see the lpe statements or the lpe node that arnold 5 uses to generate the lpe AOVs. Either we are doing this wrong or it isn't supported yet. Can somebody please confirm?

Also, are the operator nodes in arnold 5 supported in gaffer?

Thanks,
Sachin

John Haddon

unread,
Jun 12, 2018, 6:04:58 AM6/12/18
to gaffe...@googlegroups.com
Hi Sachin,

LPEs should be supported already. I think you just need to change "colour C.*" to "lpe C.*" in your example for it to work. You might also want to add a string "layerName" parameter to give the LPE a more readable name in Gaffer's viewer.

We haven't added any support for Arnold 5's operator nodes, and we don't currently have any plans to do so. Can I ask what you would want to use them for?

Cheers...
John 

--
You received this message because you are subscribed to the Google Groups "gaffer-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to gaffer-dev+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.



--
John Haddon - R&D Programmer
Image Engine
studio: +1 (604) 874-5634 | jo...@image-engine.com | www.image-engine.com



15 West 5th Avenue, Vancouver, BC, V5Y 1H4, Canada

If you are not the intended recipient, disclosure, copying, distribution and use of this email is prohibited. Please notify us immediately and delete this email from your systems. You may contact us at in...@image-engine.com if you do not wish to receive further commercial electronic messages. We may still send you messages for which we do not require consent.

Sachin Shrestha

unread,
Jun 12, 2018, 8:41:31 AM6/12/18
to gaffer-dev
Aah nice! That did it John, thanks.

Re the operators, I wanted to explore it for using materialX or do deferred shader assignments for objects generated from within a procedural. As a general question though, if I have to add the support for a new arnold node like an operator myself, then how would I proceed in the gaffer scheme of things? I'm trying to correlate it to our internal package (R&H's crom) and here is how we go about it there.

The base crom engine usually does not need to be changed or updated for adding support like this. There, I would use the separate arnold translator layer which is a python module to add this support. There are "node" specific modules which manage the actual creation of a new node within crom so for an operator node, I would introduce a new operator node module. This wold have the UI layout description of the node as well as the compute/execution part of the node. Depending on what type of node this is (i.e. light or shader or geometry, etc.) it will be inherited from the appropriate parent class. Once the node module is ready, I need to update the actual translation module (which is again a python module) that converts the crom nodes into the arnold scene description. Here I would look for the required node instances in the scene graph and call the relevant arnold api functions to translate it into the final arnold objects.

In gaffer, do we have a similar provision or does this require the user to edit the actual gaffer source? The GafferArnold* python modules seem to be all using the compiled so libs so I'm not sure if such functionality can be extended without updating the source and recompiling the application. Please correct me if I'm mistaken.

Thanks,
Sachin


On Tuesday, 12 June 2018 15:34:58 UTC+5:30, John Haddon wrote:
Hi Sachin,

LPEs should be supported already. I think you just need to change "colour C.*" to "lpe C.*" in your example for it to work. You might also want to add a string "layerName" parameter to give the LPE a more readable name in Gaffer's viewer.

We haven't added any support for Arnold 5's operator nodes, and we don't currently have any plans to do so. Can I ask what you would want to use them for?

Cheers...
John 
On 11 June 2018 at 16:14, Sachin Shrestha <noizf...@gmail.com> wrote:
Hi,

Is there built-in support in gaffer 0.46.0 to add custom Arnold 5 LPEs to the scene graph? We only see the AOVs that currently ship with Arnold 5.x in the arnold output options node. We tried adding a blank output and supplying the necessary parameters like below:
















However, if we inspect the generated .ass file we do not see the lpe statements or the lpe node that arnold 5 uses to generate the lpe AOVs. Either we are doing this wrong or it isn't supported yet. Can somebody please confirm?

Also, are the operator nodes in arnold 5 supported in gaffer?

Thanks,
Sachin

--
You received this message because you are subscribed to the Google Groups "gaffer-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to gaffer-dev+...@googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

Daniel Dresser

unread,
Jun 13, 2018, 2:42:25 PM6/13/18
to gaffer-dev
In Gaffer, our renderer backends are written in C++ ... I'm not sure how you could get decent performance if this was in Python.

So there is often some C++ coding required if you want to support a new "kind" of Arnold node.  We have generic support for things like lights, shaders, or geometry, so adding support for new nodes of those kinds is usually trivial, but operators don't quite fit.

Operators are most like shaders.  I took a quick look at how far away we are from supporting them as shaders, and the things we would need to fix are:
* we would need the code that sets up shader output types to know about operators, and create an output plug to create connections to
* we would need a node similar to ArnoldAOVShader to allow "shader" assignments to the globals ( perhaps with the same style of support in the backend as we have for AOV shaders, where we could give names to multiple operator setups, and then they get implicitly merged so that they can all be assigned )

This should actually be pretty straightforward, maybe we should look into it.  It hasn't been a high priority yet for us because at IE we generally try to avoid procedurals - we would rather expand things into Gaffer where they can be introspected, and then we hand them to the renderer ourselves.

-Daniel

Sachin Shrestha

unread,
Jun 13, 2018, 11:13:23 PM6/13/18
to gaffer-dev
Thanks for the info Daniel.

I’m looking at implementing the xgen procedural inside gaffer and in our current maya workflow we use multiple procedural nodes per xgen description to allow different shader assignments which I was hoping to avoid by using operators and doing shader assignments for the generated curves at render time using the operators. Similarly, for dense foliage we use procedurals extensively and operators would allow us to perform overrides on the generated geometry at render time.

Without going into the C++ source, I was wondering if there was a way to hack this. Does gaffer allow the user to inject custom statements into the Arnold scene source using the python node? If that was possible then I was thinking I could use it to inject some operators into the output .ass file with the arnold python api.

Andrew Kaufman

unread,
Jun 14, 2018, 12:22:14 AM6/14/18
to gaffe...@googlegroups.com
I suspect the reason John initially questioned your interest in Arnold operators is that most of the use cases you’re describing are typically things people would prefer to do in Gaffer directly. Performing render overrides on generated geometry is exactly where Gaffer excels compared to some other workflows.

Dense foliage, for example, can be quite easily handled in Gaffer with no need for external procedurals. If your foliage has exceptionally heavy hierarchy/geo, you can always use an Encapsulate node to hide it away from downstream processing. It might be easiest to think of that workflow as making an on-the-fly procedural, all within the Gaffer graph.

Now, your example of making shader assignments within an XGen procedural is indeed a case where Arnold operators might make sense in Gaffer. I’m curious to hear John’s thoughts on that use case.

We haven’t hit that sort of issue at Image Engine because instead of using hair procedurals, we extract curves from external hair software (XGen, Yeti, Houdini, etc), store them on disk in an Alembic-like file, and load the baked curve geo into Gaffer, where we have nodes for further deforming it or simply binding it to an animating skin mesh. We’ve prototyped a similar workflow for crowds (though without the extra baking step) to avoid needing external crowd procedurals as well.

Typically, at Image Engine at least, we would never generate .ass files unless we’re debugging the backend or making a test case for SolidAngle. We instead use Gaffer to communicate with Arnold directly, and the translation of a Gaffer scene into an Arnold universe happens live, entirely in c++, in the same process that generates the pixels.

But if you’re committed to generating .ass files and kicking them, rather than rendering live with a ‘gaffer execute’ command, you may be able to ‘hack’ operators in using a chain of TaskNodes:

ArnoldRender (set to output a SceneDescription)
|
PythonCommand (reads the .ass file, appends to it, writes it back to disk)
|
SystemCommand (kick the modified .ass file)

Cheers,
Andrew

Sachin Shrestha

unread,
Jun 14, 2018, 1:23:39 AM6/14/18
to gaffer-dev
Hi Andrew,

Thanks for your insights. Its certainly very helpful to look at the same problem from a different angle. Now that you have mentioned it, it does sound interesting that gaffer itself could be a good replacement for many procedural workflows. So, for stuff like xgen or yeti hair I can certainly export the final renderable curves into an alembic which should work as is in gaffer. But for foliage type assets, if we are using instancers like MASH or xgen or Houdini then are you saying that we can do that entirely in the gaffer scene graph itself? Or do we bake out all the instances from a 3rd party app into an alembic cache? In the latter case do we need to retain the instances as instances in the alembic file or will gaffer auto-magically de-duplicate geometry when we import it using the SceneReader?

The workflows you mentioned sound exciting and I'm open to ideas and using gaffer the way its intended to rather than shoe-horning our existing workflows into it.

Cheers,
Sachin

Daniel Dresser

unread,
Jun 14, 2018, 3:05:42 AM6/14/18
to gaffer-dev
It's definitely a goal that you should be able to set up instancing in Gaffer.

The current Instancer node available in Gaffer is a bit rudimentary - John's been working on a better one, but I don't think that's available quite yet.  The current Instancer should allow you to do some basic testing with setting up instancing in Gaffer - bring some points in and instance geo onto them.

If you do need to use operators, trying to hack the ass file sounds quite painful compared to what should be a fairly straightforward piece of work to support it properly on the C++ side.  Let me know if this is something you need, and I'll see if I can scrounge up some time on the weekend.

-Daniel

John Haddon

unread,
Jun 14, 2018, 4:42:45 AM6/14/18
to gaffe...@googlegroups.com
On Thu, Jun 14, 2018 at 5:22 AM, Andrew Kaufman <and...@gafferhq.org> wrote:
Now, your example of making shader assignments within an XGen procedural is indeed a case where Arnold operators might make sense in Gaffer. I’m curious to hear John’s thoughts on that use case.
 
My personal view is that the operations that Arnold's operators perform belong in the 3d application, not the renderer. But from a more pragmatic perspective I think this use case is pretty reasonable, and I don't see any reason we wouldn't want to allow it.

Andrew Kaufman

unread,
Jun 14, 2018, 6:34:33 PM6/14/18
to gaffe...@googlegroups.com
for foliage type assets, if we are using instancers like MASH or xgen or Houdini then are you saying that we can do that entirely in the gaffer scene graph itself?

That is one approach you could take, though you'll quickly discover that the current Gaffer toolset is somewhat lacking when it comes to production ready instancing workflows. We're aiming to get there in the future, but I can't say how soon that will become viable purely in Gaffer. In the mean time, you should continue using MASH or Houdini or whatever you prefer to generate the instancing setup.


Or do we bake out all the instances from a 3rd party app into an alembic cache? 

You could bake that all out to an Alembic, but that might be quite expensive. One alternative is to use a bit of Cortex API to extract the point cloud from your MASH instancer and bake just that without the other geometry:

import IECoreMaya
import IECoreAlembic
time = maya.cmds.currentTime( q=True ) / 24.0 # or whatever fps
points = IECoreMaya.LiveScene().child( "|MASH_trees" ).readObject( time )
scene = IECoreAlembic.AlembicScene( "/tmp/mashPoints.abc", IECore.IndexedIO.OpenMode.Write )
child = scene.createChild( "mashPoints" )
child.writeObject( points, time )
# the file writes to disk when we drop all references to it
del scene, child

and then re-create your final setup in Gaffer, using SceneReaders (or your AssetBox) to load the final geo as well as the point cloud.

One big caveat with this approach, as Dan pointed out, is that the current public Instancer node in Gaffer isn't quite production ready (at Image Engine we have proprietary alternatives). John has started on a more full featured public node and Image Engine is planning to prioritize that work soon, as it will have several pros over our internal solutions.

I'd hope we can get that new Instancer released in the near term, but actually authoring an interesting point cloud to instance onto is more of a long term goal at this point.


> do we need to retain the instances as instances in the alembic file or will gaffer auto-magically de-duplicate geometry when we import it using the SceneReader?

Gaffer will indeed auto-magically de-duplicate geometry, both when passing it through its own graph, and when translating it to Arnold. It does this by comparing hashes of the geometry and reusing what it can. If you set your ArnoldRender node to SceneDescription and take a look in the .ass file, you should see one polymesh and one ginstance for each unique mesh. If you have any identical meshes, you'll end up with more ginstances than polymeshes. This happens even for "non-instanced" geo, as you can see in the script I've pasted below, which produces an .ass file with 2 polymeshes and 11 ginstances, without the user every declaring anything as being instanced.

Cheers,
Andrew



import Gaffer
import GafferArnold
import GafferDispatch
import GafferScene
import IECore
import imath

Gaffer.Metadata.registerNodeValue( parent, "serialiser:milestoneVersion", 0, persistent=False )
Gaffer.Metadata.registerNodeValue( parent, "serialiser:majorVersion", 46, persistent=False )
Gaffer.Metadata.registerNodeValue( parent, "serialiser:minorVersion", 2, persistent=False )
Gaffer.Metadata.registerNodeValue( parent, "serialiser:patchVersion", 0, persistent=False )

__children = {}

__children["Sphere"] = GafferScene.Sphere( "Sphere" )
parent.addChild( __children["Sphere"] )
__children["Sphere"].addChild( Gaffer.V2fPlug( "__uiPosition", defaultValue = imath.V2f( 0, 0 ), flags = Gaffer.Plug.Flags.Default | Gaffer.Plug.Flags.Dynamic, ) )
__children["ArnoldRender"] = GafferArnold.ArnoldRender( "ArnoldRender" )
parent.addChild( __children["ArnoldRender"] )
__children["ArnoldRender"].addChild( Gaffer.V2fPlug( "__uiPosition", defaultValue = imath.V2f( 0, 0 ), flags = Gaffer.Plug.Flags.Default | Gaffer.Plug.Flags.Dynamic, ) )
__children["Duplicate"] = GafferScene.Duplicate( "Duplicate" )
parent.addChild( __children["Duplicate"] )
__children["Duplicate"].addChild( Gaffer.V2fPlug( "__uiPosition", defaultValue = imath.V2f( 0, 0 ), flags = Gaffer.Plug.Flags.Default | Gaffer.Plug.Flags.Dynamic, ) )
__children["PathFilter"] = GafferScene.PathFilter( "PathFilter" )
parent.addChild( __children["PathFilter"] )
__children["PathFilter"].addChild( Gaffer.V2fPlug( "__uiPosition", defaultValue = imath.V2f( 0, 0 ), flags = Gaffer.Plug.Flags.Default | Gaffer.Plug.Flags.Dynamic, ) )
__children["MapOffset"] = GafferScene.MapOffset( "MapOffset" )
parent.addChild( __children["MapOffset"] )
__children["MapOffset"].addChild( Gaffer.V2fPlug( "__uiPosition", defaultValue = imath.V2f( 0, 0 ), flags = Gaffer.Plug.Flags.Default | Gaffer.Plug.Flags.Dynamic, ) )
__children["Sphere"]["__uiPosition"].setValue( imath.V2f( 17.0500011, 17.4140606 ) )
__children["ArnoldRender"]["dispatcher"]["farm"]["retryFailed"].setValue( True )
__children["ArnoldRender"]["in"].setInput( __children["MapOffset"]["out"] )
__children["ArnoldRender"]["mode"].setValue( 1 )
__children["ArnoldRender"]["fileName"].setValue( '/tmp/sphere.ass' )
__children["ArnoldRender"]["__uiPosition"].setValue( imath.V2f( 17.0500011, -6.01406288 ) )
__children["Duplicate"]["in"].setInput( __children["Sphere"]["out"] )
__children["Duplicate"]["target"].setValue( '/sphere' )
__children["Duplicate"]["copies"].setValue( 10 )
__children["Duplicate"]["transform"]["translate"].setValue( imath.V3f( 2, 0, 0 ) )
__children["Duplicate"]["__uiPosition"].setValue( imath.V2f( 17.0500011, 9.24999809 ) )
__children["PathFilter"]["paths"].setValue( IECore.StringVectorData( [ '/sphere2' ] ) )
__children["PathFilter"]["__uiPosition"].setValue( imath.V2f( 30.0500031, 7.16796684 ) )
__children["MapOffset"]["in"].setInput( __children["Duplicate"]["out"] )
__children["MapOffset"]["filter"].setInput( __children["PathFilter"]["out"] )
__children["MapOffset"]["offset"].setValue( imath.V2f( 10, 10 ) )
__children["MapOffset"]["__uiPosition"].setValue( imath.V2f( 17.0500011, 2.14999962 ) )


del __children

Sachin Shrestha

unread,
Jun 15, 2018, 10:57:03 AM6/15/18
to gaffer-dev
Thanks guys for all the responses and for showing patience with all my questions :)

As I’m trying out gaffer everyday, I get what you guys are saying about having the entire scene context available to the user at all times instead of relying on render time procedurals or overrides. In that sense the gaffer scene itself is the procedural interface for the user. For instance, I’m able to set path filters, prune objects, assign overrides to very specific sets, etc. all of which in a procedural’s case would need to be handled specially either via things like operators or the procedural itself would need to support such behaviour.

So yeah, the more I use it the more I think that one should be able to manage a lot without needing support for something like operators. I guess it would be good to still have it for the sake of completeness since it’s a core Arnold feature and not on its dcc translator side. I think I’ll take a stab at it to get some practice at extending gaffer as well. If it’s a bit over my head then I’ll bother you Daniel :)

Thanks for also sharing your existing workflows and ideas. Appreciate the open source nature of this project!

Mathew Mackereth

unread,
Aug 2, 2019, 6:51:37 PM8/2/19
to gaffer-dev
Hey Sachin, 

I'm wanting to reconstruct scenes generated in other processes in Gaffer so i can test features.  I'd also find that support for operators, mainly  graph include and materialX would certainly help me share shaders/ properties cross platform and test things across DCC's as i can package these out of an existing, non-gaffer shading pipe.  Did you end up with a way of including these or any tips?  in a larger facility we like the idea of generating looks in one place(dept/dcc/process/bespoke tool) in an open format, and applying them somewhere else.

Mathew


Reply all
Reply to author
Forward
0 new messages