Karma AOVs won't work inside a subnet (and therefore HDA)

   855   4   2
User Avatar
Member
81 posts
Joined: July 2018
Online
Trying to build out a set of material-based AOVs. Ultimately I'd like this to live as an HDA, so I can drop down a complete set of AOVs quickly.

I set them up with the karma AOV VOP, which works fine... until I drop them into a subnet. Once I do that, it seems that whatever the karma AOV node is doing under the hood to create the Render Vars breaks and my AOVs nolonger work.

There's not a lot of documentation on exactly what the Karma AOV node is doing. Anyone have an ideas or why it would stop working from within a subnet?

The project below shows the problem. If you switch between the outputs of the Karma AOV node and subnet, which have the Karma AOV node inside, the Render Vars appear and disappear.

Attachments:
kma_aovs.hiplc (345.3 KB)

madebygeoff.com
User Avatar
Member
81 posts
Joined: July 2018
Online
Maybe I should back up here...

We're trying to explore introducing Karma as a production renderer and I'm trying to figure out how to build a set up for rendering out separate Beauty and Utilities passes, where the Utilities passes contain a number of custom AOVs that remain consistent across a given project. These AOVs are a mix of AOVs that are generated from geometry attributes/primvars, some that are material-based, and a number that are included in the standard Karma AOVs.

The problem(s) I keep running into is: 1. USD doesn't seem to recognize material-based AOVs as a concept and so a lot this feels very hack-y 2. Karma (as far as I can tell) doesn't support scene-wide AOV material overrides.

Here's what I've explored:

1. Use the Karma AOV node as a way around the lack of material-based AOVs in USD. This works, but would require a fairly large network to be included in every material in a scene. To make matters worse, you can't roll that network up into a subnet/hda or the Karma AOV nodes stop working (see post above). I also don't love that this requires me to definite AOVs in 2 places, once in the material network and again in the render settings (but I can live with that). The bigger problem is the inability to roll this up into something easy to lay down and repeat and the general unwieldiness of it all.

2. I would prefer a setup where I can globally override all the materials in a scene with a standard AOV material and separate render settings where I can set all my standard and custom AOVs. The problem here is that this would require an override for just the surface shader, not the displacement settings and any per-material render properties.

-- 2a. I can do this per material with a edit material network node, but not globally.

-- 2b. I could try a sublayer to override the materials, but this would override the entire material, not just the surface shader settings and I lose all my displacement settings.

Anyone using Karma in a studio setting and figured out a way to get around this?
Edited by made-by-geoff - Aug. 6, 2024 08:56:51
madebygeoff.com
User Avatar
Member
81 posts
Joined: July 2018
Online
For anyone else searching...

I couldn't find a way to do it with references and overrides, but I found this handy bit of code here [www.sidefx.com]:

You'd probably want to adapt this with an interface for the prim pattern, but right now it runs over all prims and swaps out the given surface shader in any material.

from pxr import UsdShade
node = hou.pwd()
ls = hou.LopSelectionRule()

primpattern = "*"
if primpattern != "":

    ls.setPathPattern('%matfromgeo({})'.format(primpattern))
    paths = ls.expandedPaths(node.inputs()[0])
    stage = node.editableStage()
    for path in paths:
    
        basemtl = UsdShade.Material.Get(stage, path)
    
        utilShader = UsdShade.Shader.Get(stage, '/materials/karmtl_util/util')
        for output in basemtl.GetSurfaceOutputs():
            if "outputs:kma:surface" in output.GetFullName():
                output.ConnectToSource(utilShader.ConnectableAPI(), "surface")
madebygeoff.com
User Avatar
Staff
1454 posts
Joined: July 2005
Offline
The subnet in the kma_aovs.hiplc file needs a "Render Mask" spare parm to help Solaris figure out the correct shader translator. See attached fixed file.

The Material Library LOP traverses VOPs starting from the terminal (eg, subnet outputs in this case), and for each VOP it tries to use an appropriate shader translator based on the render mask intrisic property of an HDA or based on 'shader_rendermask' parameter. Karma networks consist of MaterialX and Karma-only nodes, so the translators can switch from node to node.

The subnet VOP can be quite problematic because it is common node use by many "languages", so deciding on a correct translator can be tricky. There are some heuristics trying to guess a best match, and it works most of the time. Here however, it looks like a wrong translator is picked which messes up the shader translation.

Because only Karma understands AOVs set up like that, it is crucial that the translation is performed using the karma shader translator throughout the whole network. And for that, the subnet needs to have that Render Mask spare parm set.

You can add such spare parm from RMB on the subnet > Parameters and Channels > Edit Parameter Interface..., then in the left-most panel choose Node Properties and search for "shader_". You will see a bunch of spare parms that help guide the translation of VOPs, and "Render Mask" is one of them.

Attachments:
kma_aovs-fixed.hiplc (344.5 KB)

User Avatar
Member
3 posts
Joined: Sept. 2017
Offline
I wrote something to connect different shaders to AOVs as a post process after a materiallibrary maybe it's useful. In this case it grabs the outputs from an HDA inside the materiallibrary (textureLoaders) and connects them to the surface shader as AOV inputs. It can also create the RenderVars at the same time.


from pxr import Sdf, Usd, UsdShade

node = hou.pwd()

# User Inputs
createRenderVars = "`chs("../create_rendervars")`"
renderVarsPrimpattern = "`chs("../primpattern_rendervars")`"

materialPrimpattern = "`chs("../primpattern_materials")`"
textureLoadersPrimpattern = "%descendants(MATERIALPRIMPATH) & **/textureLoaders"

materialPrimPaths = hou.LopSelectionRule(materialPrimpattern).expandedPaths(node.input(0))


# Add code to modify the stage.
stage = node.editableStage()

for materialPrimPath in materialPrimPaths:
    materialPrim = stage.GetPrimAtPath(materialPrimPath)
    material = UsdShade.Material(materialPrim)
    
    surfaceKarmaOutput = material.GetSurfaceOutputs()[1] # 0 is preview and 1 is kma - not the best way to grab it
    surfaceShaderPrim = UsdShade.ConnectableAPI.GetConnectedSource(surfaceKarmaOutput)[0].GetPrim()
    surfaceShader = UsdShade.Shader(surfaceShaderPrim)
    
    textureLoadersPrimPaths = hou.LopSelectionRule(textureLoadersPrimpattern.replace('MATERIALPRIMPATH', str(materialPrimPath))).expandedPaths(node.input(0))
    for textureLoadersPrimPath in textureLoadersPrimPaths:
        textureOutputs = UsdShade.NodeGraph(stage.GetPrimAtPath(textureLoadersPrimPath)).GetOutputs()
        for textureOutput in textureOutputs:
            outputBaseName = textureOutput.GetBaseName()
            outputPrim = UsdShade.ConnectableAPI.GetConnectedSource(textureOutput)[0].GetPrim()
            outputShader = UsdShade.Shader(outputPrim)
            
            aovName = "tex_" + outputBaseName
            input = surfaceShader.CreateInput("aov:" + aovName, Sdf.ValueTypeNames.Color3f)
            input.ConnectToSource(outputShader.GetOutput("out"))
            
            if createRenderVars == 'on':
                renderVarParentPath = Sdf.Path(renderVarsPrimpattern)
                textureRenderVarPrim = stage.DefinePrim(renderVarParentPath.AppendPath(aovName), 'RenderVar')
                textureRenderVarPrim.GetAttribute("sourceName").Set(aovName)
                textureRenderVarPrim.GetAttribute("sourceType").Set("raw")
                textureRenderVarPrim.GetAttribute("dataType").Set("color3f")
                textureRenderVarPrim.CreateAttribute("driver:parameters:aov:name", Sdf.ValueTypeNames.String)
                textureRenderVarPrim.GetAttribute("driver:parameters:aov:name").Set(aovName)
                textureRenderVarPrim.CreateAttribute("driver:parameters:aov:format", Sdf.ValueTypeNames.Token)
                textureRenderVarPrim.GetAttribute("driver:parameters:aov:format").Set("color3h")
Edited by ctacon - Sept. 20, 2024 10:45:57
  • Quick Links