This script when placed in a Python LOP will create an AOV for each light in your scene. Works perfectly for me so far, but I've only been testing for about a week:
from pxr import UsdRender, Sdf
import hou
import re
stage = hou.pwd().editableStage()
# --------------------------------------------------
# Settings
# --------------------------------------------------
render_var_root = "/Render/Products/Vars"
skip_dome_lights = True
def safe_name(name):
name = re.sub(r"[^A-Za-z0-9_]+", "_", name)
name = re.sub(r"_+", "_", name)
return name.strip("_")
def is_light(prim):
type_name = prim.GetTypeName()
if not type_name:
return False
if skip_dome_lights and type_name == "DomeLight":
return False
return "Light" in type_name
def set_attr(prim, name, type_name, value):
attr = prim.CreateAttribute(name, type_name)
attr.Set(value)
return attr
def apply_husk_render_var_api(prim):
prim.SetMetadata(
"apiSchemas",
Sdf.TokenListOp.CreateExplicit(["HuskRenderVarAPI"])
)
def create_light_lpe_render_var(light_group):
aov_name = "light_" + light_group
lpe = "C.*<L.'{}'>".format(light_group)
path = "{}/{}".format(render_var_root, aov_name)
# IMPORTANT:
# Remove the old prim first so previous script attributes do not remain.
if stage.GetPrimAtPath(path):
stage.RemovePrim(path)
prim = stage.DefinePrim(path, "RenderVar")
rv = UsdRender.Var(prim)
apply_husk_render_var_api(prim)
# --------------------------------------------------
# Standard RenderVar attrs
# --------------------------------------------------
rv.CreateDataTypeAttr("color4f")
rv.CreateSourceTypeAttr("lpe")
rv.CreateSourceNameAttr(lpe)
# --------------------------------------------------
# Arnold attrs matching your manual example
# --------------------------------------------------
set_attr(prim, "arnold:cryptomatte_filter:filter", Sdf.ValueTypeNames.Token, "gaussian")
set_attr(prim, "arnold:cryptomatte_filter:noop", Sdf.ValueTypeNames.Bool, False)
set_attr(prim, "arnold:cryptomatte_filter:rank", Sdf.ValueTypeNames.Int, -1)
set_attr(prim, "arnold:diff_filter:filter_weights", Sdf.ValueTypeNames.Token, "box")
set_attr(prim, "arnold:farthest_filter:domain", Sdf.ValueTypeNames.Token, "first_hit")
set_attr(prim, "arnold:filter", Sdf.ValueTypeNames.Token, "gaussian_filter")
set_attr(prim, "arnold:heatmap_filter:maximum", Sdf.ValueTypeNames.Float, 1.0)
set_attr(prim, "arnold:heatmap_filter:minimum", Sdf.ValueTypeNames.Float, 0.0)
set_attr(prim, "arnold:variance_filter:filter_weights", Sdf.ValueTypeNames.Token, "box")
set_attr(prim, "arnold:variance_filter:scalar_mode", Sdf.ValueTypeNames.Bool, False)
set_attr(prim, "arnold:width", Sdf.ValueTypeNames.Float, 2.0)
# --------------------------------------------------
# Driver attrs matching your manual example
# --------------------------------------------------
set_attr(prim, "driver:parameters:aov:format", Sdf.ValueTypeNames.Token, "color4f")
set_attr(prim, "driver:parameters:aov:husk:channel_lower_rgb", Sdf.ValueTypeNames.Bool, False)
set_attr(prim, "driver:parameters:aov:husk:channel_prefix", Sdf.ValueTypeNames.String, aov_name)
set_attr(prim, "driver:parameters:aov:husk:clearValue", Sdf.ValueTypeNames.Float, 0.0)
set_attr(prim, "driver:parameters:aov:husk:format", Sdf.ValueTypeNames.Token, "color4f")
set_attr(prim, "driver:parameters:aov:husk:multiSampled", Sdf.ValueTypeNames.Bool, True)
# Your manual example uses $OS here.
set_attr(prim, "driver:parameters:aov:husk:name", Sdf.ValueTypeNames.String, aov_name)
set_attr(prim, "driver:parameters:aov:husk:OpenEXR:compression", Sdf.ValueTypeNames.String, "zips")
set_attr(prim, "driver:parameters:aov:husk:OpenEXR:dwa_compression", Sdf.ValueTypeNames.Int, 85)
set_attr(prim, "driver:parameters:aov:husk:output_colorspace", Sdf.ValueTypeNames.String, "")
set_attr(prim, "driver:parameters:aov:husk:source_colorspace", Sdf.ValueTypeNames.String, "")
set_attr(prim, "driver:parameters:aov:name", Sdf.ValueTypeNames.String, aov_name)
return path
# --------------------------------------------------
# Assign Arnold light groups and create matching AOV vars
# --------------------------------------------------
light_groups = []
for prim in stage.Traverse():
if not is_light(prim):
continue
light_group = safe_name(prim.GetName())
if not light_group:
continue
# The light group name must match the name used inside the LPE.
set_attr(
prim,
"primvars:arnold:aov",
Sdf.ValueTypeNames.String,
light_group
)
light_groups.append(light_group)
# Remove duplicate light group names while preserving order.
light_groups = list(dict.fromkeys(light_groups))
created = []
for light_group in light_groups:
created.append(create_light_lpe_render_var(light_group))
print("Created Arnold light LPE RenderVars:")
for path in created:
print(" " + path)