Already every year ~~ Elderly ~~ The annual Houdini Advent Calendar. Well, I thought about what to write, but since I'm only touching Arnold this year, I'd like to write information that seems to be helpful in HtoA (Arnold for Houdini). Arnold is one of the renderers
In other words, it's a renderer that most major software supports, and it's the de facto de facto standard in the video industry. Moreover, it is cost-friendly that you can render any DCC with one batch render license. Furthermore, you can write shaders not only in C ++ but also in ** O ** pen ** S ** hading ** L ** anguage (OSL), and it also supports MaterialX (described later) quickly. Or is it USD next? I'm looking forward to it. Houdini's standard Mantra renderer is free for Houdini users and can be used as much as you like, and I like it because the shader is easy to write with the familiar VEX, but the disadvantage that it only works with Houdini is painful. Arnold can be rendered (** Kick Ass ) in an ass ( A ** rnold ** S ** cene ** S ** ource) file, which is a standardized scene description format. .. In other words, if you export any DCC to ass, you can render any DCC.
If you understand Arnold, it's better to study ass. Here is a recommended page for your reference. https://arnoldsupport.com/category/ass/
** Note that using a third-party renderer like Arnold with Houdini requires a Houdini Indie, Houdini Core, or Houdini FX license. Also, the Houdini Indie version cannot output ass files: sob: ** So, please study by outputting ass in Houdini Core / FX environment, Maya environment (student version, home license is also possible).
If you want to render a render scene built in Maya coexisting with a Houdini render scene on Houdini, just export ass from MtoA and read it in HtoA.
Execute ** Arnold menu → Export Stand In ** on MtoA, check the information you want to output, and export the ass file.
Simply add ** Arnold Procedural ** or ** Arnold Include ** in the HtoA / obj
context and specify the ass file to capture the Maya Arnold scene directly on Houdini.
For scenes using Maya-specific shaders, it is necessary to specify the path of that DLL in the Plugin Path
of Arnold ROP, but every time the version goes up, the DCC-specific shader is switched to the pure Arnold shader. It is becoming. Expectations for the future.
An example of importing a Maya scene into Houdini via an ass file and coexisting with Mr. Pig.
It's convenient.
However, if you want to edit another DCC object on Houdini, you will currently be importing it into Houdini in an intermediate format such as Alembic / FBX (hereafter USD), assigning an Arnold shader and rendering it.
On top of that, it may be useful to prepare on Maya, so I would like to introduce it.
Houdini files have a ** hip ** file extension Arnold files have a ** ass ** file extension ** hip and ass !!!**: thinking: Somehow it seems to be compatible: spy_tone1:
Houdini often uses Rest Position. Rest Position is the Japanese word for "rest position". Arnold refers to Pref (Position Reference), and Maya refers to Reference Object. The reason is that if you build a shader with a 2D / 3D projection of a texture onto a geometry (eg camera projection), the projected texture will appear slippery if the geometry deforms. In this case, we use the technique of returning the result of 2D / 3D projection to the untransformed geometry to the transformed geometry. If you're not sure, read here. https://houdini.prisms.xyz/wiki/index.php?title=Rest_Attrib To set the Rest Position in Maya, run ** Create Texture Reference Object ** in the ** Texture ** menu and in Arnold ** Export Reference Positions * in the Arnold property of the Maya shape to reflect that. Is to enable *. I hate this method because it's Maya-specific and references geometry in a reference format. And it can't be put out as an attribute purely in Alembic. Ideally, I would like to add Rest Position as a vertex attribute to the geometry and export that attribute to Alembic.
You can optionally add vertex / face attributes to your Maya shape ** by building a MEL **, so I'll show you how. ** Generally ** Maya vertex attributes <-> Houdini point attributes, Maya face attributes <-> Houdini primitive attributes should be recognized. This time, I would like to add Rest Position as a vertex attribute, but I would also like to explain how to add a vertex / face attribute by adding a face attribute that contains the shader name for each face.
The Pymel code is below. This is a script for Maya ** that adds a Rest Position and shop_materialpath to a shape that has an Arnold shader assigned to the SG **. Since we're getting the vertex coordinates in world space, we're assuming that the transform of the object we want to export to Alembic is frozen.
--A vertex attribute named rest
that stores the position information of the geometry of the current frame
--A face attribute named shop_materialpath
that contains the string / shop / maya / shader name
for the shader name assigned to the geometry
Can be added. If you apply this, you can add Velocity and Cd, so please try it.
rest and shop_PyMEL script to add materialpath
import pymel.core as pm
#Declare a dictionary where the key is the shape name and the value is a pair of a list of face attribute values for that shape
dictAttributes = {}
context = "/shop/maya/"
for eachSG in pm.ls(type="shadingEngine"):
members = pm.sets(eachSG,q=True,nodesOnly=False)
#Object to SG/Skip if no component is assigned
if len(members)==0:
continue
#If no shader is assigned to SG, skip it.
#Surface shader where the input connector of SG's aiSurfaceShader is prioritized in Arnold. If not, the surface shader of the input connector of surfaceShader is used.
shader = (pm.listConnections(eachSG+".aiSurfaceShader",p=False,c=False,s=True,d=False) or [""])[0]
if shader == "":
shader = (pm.listConnections(eachSG+".surfaceShader",p=False,c=False,s=True,d=False) or [""])[0]
if shader == "":
continue
shaderName = shader.name()
#Branch processing based on whether it is an object-level allocation or a component-level allocation.
#At the object level, prepare a list as large as the number of all faces and write the shader name to all items in the list.
#At the component level, write shader name information to the index of that face
for eachMember in members:
#Processing when SG is associated with an object
if type(eachMember) == pm.nodetypes.Mesh:
dictAttributes[eachMember]={"shader":[],"rest":[]}
dictAttributes[eachMember]["shader"] = [context+shaderName]*eachMember.numFaces()
for vtxIndex in range(eachMember.numVertices()):
position = pm.pointPosition(eachMember+".vtx["+str(vtxIndex)+"]",world=True).get()
dictAttributes[eachMember]["rest"].append( [position[0],position[1],position[2]] )
#Processing when SG is tied to the face
elif eachMember._ComponentLabel__ == "f":
listComponents = pm.ls(eachMember,flatten=True)
shape = eachMember._node
if shape not in dictAttributes:
dictAttributes[shape]={"shader":[],"rest":[]}
dictAttributes[shape]["shader"]= [""]*eachMember.totalSize()
for vtxIndex in range(shape.numVertices()):
dictAttributes[shape]["rest"].append( pm.pointPosition(shape+".vtx["+str(vtxIndex)+"]",world=True) )
#Write the shader name to the index of the corresponding list
for index in eachMember.indices():
dictAttributes[shape]["shader"][index] = context+shaderName
restAttributeName = "rest"
shaderAttributeName = "shop_materialpath"
for eachShape in dictAttributes:
#Added Rest Position attribute
if not pm.attributeQuery(restAttributeName+"_AbcGeomScope",node=eachShape,exists=True):
pm.addAttr(eachShape, dataType="string", longName=restAttributeName+"_AbcGeomScope")
pm.setAttr(eachShape+"."+restAttributeName+"_AbcGeomScope", "var")
if not pm.attributeQuery(restAttributeName,node=eachShape,exists=True):
pm.addAttr(eachShape,dataType="vectorArray",longName=restAttributeName)
pm.setAttr(eachShape+"."+restAttributeName,dictAttributes[eachShape]["rest"])
#shop_Added materialpath attribute
if not pm.attributeQuery(shaderAttributeName+"_AbcGeomScope",node=eachShape,exists=True):
pm.addAttr(eachShape, dataType="string", longName=shaderAttributeName+"_AbcGeomScope")
pm.setAttr(eachShape+"."+shaderAttributeName+"_AbcGeomScope", "uni")
if not pm.attributeQuery(shaderAttributeName,node=eachShape,exists=True):
pm.addAttr(eachShape,dataType="stringArray",longName=shaderAttributeName)
pm.setAttr(eachShape+"."+shaderAttributeName,dictAttributes[eachShape]["shader"])
When executed, custom attributes will be added as shown below.
I think that the value is displayed in the format of numeric / string field if it is a normal attribute, but since the added rest / shop_materialpath is an array value for each vertex / face, the value cannot be confirmed from the GUI. I wish I could check it in the Component Editor. .. ..
I've intentionally added an attribute name named attribute name_AbcGeomScope
here, which makes sense.
Did you notice that when you add an array value attribute to a shape, that's not enough information?
Is it a per-object array attribute? Is it a vertex-based attribute? Is it a face-based attribute?
You need to specify it explicitly.
Even in Houdini, when you touch the Wrangle SOP, the "Run Over" parameter will intentionally specify which attribute is Point / Vertex / Primitive / Detail.
** Maya is adding attributes on the shape ** Above, you have to specify which attribute is the vertex, face, or object as supplementary information.
Maya's Alembic Importer / Exporter has a _AbcGeomScope
suffix as the metadata that plays that role.
That information can be found in the code published on Alembic's Github.
https://github.com/alembic/alembic/blob/master/maya/AbcExport/AttributesWriter.cpp
It seems that you can specify vtx
, fvr
, ʻuni, and
var for the value of the
_AbcGeomScopestring. If no other value or
_AbcGeomScope is set, it will be treated as
const`, that is, it will be an attribute per object.
This time, Alembic written from Maya side wants to be recognized as Houdini's Point attribute in Houdini and shop_materialpath as Houdini's Primitive attribute in Houdini, so the above code is It is such a designation.
Now, prepare the model. Here, we have prepared three types: a deforming sphere, a sphere with a shader face assigned, and the ground. With attributes added to it and multiple geometries you want to export to Houdini selected, ** Run the Cache menu → Alembic Cache → Export Selection to Alembic ... **. Add an Attribute as shown below.
_AbcGeomScope
is only needed by Maya's Alembic importer / exporter to determine the type of attribute, so only rest
and shop_materialpath
are needed.
** Ogawa ** is required for ** File Format **. Arnold only supports ** Ogawa **.
** Write Face Sets ** is required if per-face attributes have been assigned.
When I imported the Alembic file from Maya into Houdini, the Rest Position was captured as a rest
Point attribute: relaxed:
The shop_materialpath
Primitive attribute is also loaded.
Placing the shader in this path eliminates the need for material reassignment.
Place the shader in the / shop
context.
You can use the Arnold Python API to analyze the shader information in the ass file exported from Maya and reproduce it as a shader graph in Houdini.
I'm sorry I can't publish the method. You can try it yourself by referring to the Arnold Python API described below: japanese_ogre:
(Added on December 13, 2019) From HtoA5.0.0 (Arnold6), the function to build an ass file as a shader graph has been added. https://docs.arnoldrenderer.com/display/A5AFHUG/HtoA+5.0.0
After placing the shader, when I try to render it with Arnold ROP, if I load Alembic as normal Houdini Geometry, it will be rendered, but if it remains packed, the shader will not be reflected. How to assign shaders to Packed Alembic
--Use material_attribute --Use MaterialX --Use aiOperator
It is possible by using any of these. The method using MaterialX and aiOperator currently only allows ** object level ** assignments.
The material_attribute function can recognize the shop_materialpath defined inside Packed Alembic and assign shaders. This is something I've always wanted to do, but it's made possible with HtoA 4.2.0 released this year. https://docs.arnoldrenderer.com/display/A5AFHUG/HtoA+4.2.0 In this release note,
Add per primitive shop_materialpath translation in the alembic procedural
Is written.
Yes, this is why I added the shop_materialpath
attribute in Maya.
Just check ʻExport Referenced Materials in Arnold ROP and it will look at the path of the
shop_materialpath` attribute inside Packed Alembic and assign a shader.
Both MtoA and HtoA have MaterialX exporters. I'm making my own exporter because it's so shabby, but here I'll show you how to shader assign to Packed Alembic using a standard MaterialX exporter.
What is MaterialX? For those who are interested, please refer to https://www.materialx.org/.
Actually, I have translated the specifications into Japanese. https://materialx.prisms.xyz/
With multiple geometries you want to output to MaterialX selected in Maya, execute ** Arnold menu Utilities → Export Selection to MaterialX **.
Simply set the MaterialX file (
.mtlx) output destination
to ** Filename ** and the appropriate name
to ** Look Name ** and ** Export **.
Then, an XML file like this will be generated.
Define the shader schema with the xi: include
element. If you added a custom shader, add a new shader schema here.
** Of note is the value of the geom
attribute of the<materialassign>
element. ** **
MaterialX can access the Packed Alembic's internal geometry hierarchy with the * wildcard * specified by this geom
attribute.
This value output by standard MaterialX is inconvenient. This time, let's rewrite the part of geom =" / Gonyogonyo "
to geom =" * / Gonyogonyo "
with a text editor.
Simply connect the MaterialX ROP to the Arnold ROP, set the Selection to the Alembic object path, the MaterialX File to the MaterialX file path, and the Look to the look name and render, and the shader graph and shader assignments will be processed automatically. ..
The nice thing about MaterialX is that you don't have to rebuild the shader graph on HtoA, and you don't have to be aware of the hierarchy because you can do shader assignments with wildcards.
As you can see from the rendering result, shader assignment for each face is not possible.
** material_attribute ** allows face-by-face assignments, but the actual shader graph needs to be built in the / shop context, which seems like a high hurdle.
** MaterialX ** is handy, but it can be hard for artists to edit its XML.
With ** aiOperator **, even an artist can edit with a little study, and Maya shader graphs can be imported and controlled as an ass file.
When I'm touching Arnold, I think the ass file is useful.
** The ass file is mainly used to describe the scene, but it is also prepared as a file that defines shader graphs, a file that defines lights, and a file that defines rendering settings. You can prepare it as. ** **
Describes how to use aiOperator to import Maya shader graphs via an ass file.
First, on Maya, output the ass file of only the shader.
** Run Arnold Menu → Stand In → Export Stand In **.
Check only Shaders
in the Export item and export the ass file.
Name this file, for example, ShadersOnly.ass
.
On Houdini, place a ** Arnold Include ** object in the / obj
context. Specify the ShadersOnly.ass
file here.
This allows you to import Maya shader graphs.
To assign a shader graph captured by ** Arnold Include ** to any object, add aiOperator's ** Set Parameter ROP ** in the / out
context and add it to ** Arnold ROP **. Connect
Then, in ** Selection **, enter the path of the object to which you want to assign the shader
, and in the ** Assignment_1 ** parameter, enter theshader [0] = "shader name"
described in the ass file.
When I rendered it with this, I was able to assign a shader.
You can assign shaders to other objects by connecting to Arnold ROP in the same way.
e? : ear: Is it troublesome?
Well, that's right.
So let's create a script that decrypts the ass file and sets up ** aiOperator ** automatically.
The ass file you need here is the shader information and the shape that binds that shader.
The ass file read by ** Arnold Include ** contains only shader information, not which shader to bind to which shape.
Therefore, execute ** Export StandIn ** again and
In the Export item, check Shapes
and Shaders
to export the ass file. For example, ShapesShaders.ass
.
Python script to automatically set up aiOperator
import arnold
arnold.AiBegin()
assFilePath = "F:/AdventCalendar2019/ShapesShaders.ass"
dicShapes={}
result = arnold.AiASSLoad(assFilePath)
nodeIter = arnold.AiUniverseGetNodeIterator(arnold.AI_NODE_SHAPE)
while not arnold.AiNodeIteratorFinished(nodeIter):
node = arnold.AiNodeIteratorGetNext(nodeIter)
nodeName = arnold.AiNodeGetName(node)
nodeEntry = arnold.AiNodeGetNodeEntry(node)
if nodeName == "root" or nodeName == "":
continue
dicShapes[nodeName]={}
parmIter = arnold.AiNodeEntryGetParamIterator(nodeEntry)
while not arnold.AiParamIteratorFinished(parmIter):
parm = arnold.AiParamIteratorGetNext(parmIter)
parmName = arnold.AiParamGetName(parm)
if parmName == "shader":
dicShapes[nodeName][parmName] = []
parmArray = arnold.AiNodeGetArray(node,parmName)
for arrayIndex in range(arnold.AiArrayGetNumElements(parmArray)):
dicShapes[nodeName][parmName].append(arnold.AiNodeGetName(arnold.AiArrayGetPtr(parmArray, arrayIndex)))
elif parmName == "visibility":
dicShapes[nodeName][parmName] = arnold.AiNodeGetInt(node,parmName)
arnold.AiParamIteratorDestroy(parmIter)
arnold.AiNodeIteratorDestroy(nodeIter)
subnet = hou.node("/out").createNode("subnet","setParms")
merge = subnet.createNode("merge","Merge")
for shapeIndex,eachShape in enumerate(dicShapes):
setParameterNode = subnet.createNode("set_parameter",eachShape)
merge.setInput(shapeIndex,setParameterNode)
setParameterNode.parm("selection").set("*/"+eachShape)
setParameterNode.parm("assignment").set(1)
setParameterNode.parm("assignment_1").set("visibility="+str(dicShapes[eachShape]["visibility"]))
for shaderIndex,eachShader in enumerate(dicShapes[eachShape]["shader"]):
setParameterNode.parm("assignment").set(shaderIndex+2)
setParameterNode.parm("assignment_"+str(shaderIndex+2)).set("shader["+str(shaderIndex)+"]=\""+str(dicShapes[eachShape]["shader"][shaderIndex])+"\"")
When I run this script, a setParms subnet is created in the / out
context, and Set Parameter ROPs are created and merged in it for the number of objects defined in the ass file.
Connect this subnet to the Arnold ROP.
Now you can render.
Like MaterialX, aiOperator only supports object-level shader assignments, so this is the result.
However, if your model doesn't have face-by-face shader assignments, you can use the Arnold Python API with an ass file to lookDev in Houdini, even if you're not in Maya.
Verification environment: Maya2019 MtoA3.3.0.2 Houdini17.5.425 HtoA4.4.1
--You can also add vertex / face attributes in Maya so you can bring them into Houdini via Alembic. --If you understand Arnold, you should understand ass --I taught you how to use the Arnold Python API --If you build a pipeline with ass, you can render between multiple DCCs.
End: hugging:
Recommended Posts