Houdini - LOPS & USD

Solaris is the umbrella name for Houdini’s scene building, layout, lighting, and rendering tools based on the Universal Scene Description (USD) framework. LOP (lighting operator) nodes generate USD describing characters, props, lighting, and rendering.

Tips

Lighting

  • Automatic headlight Creation
    • The default behaviour when there are no lights present in the scene is to create a default headlight. To disable the default light uncheck the following checkbox
      Karma render settings (node) > Rendering > Geometry and Shading > Shading > Automatic Headlight Creation : Disable

Python

Scene Graph Tree

Lets select some prims in the Scene Graph Tree using Python. Note that the selection is stored on the LOP Network. In this example below the lop network is inside the “geo1” sop network. If it we are using the lop “Stage” context the path would be “/stage”

# get the selection of a lopnetwork inside a geo sop network

hou.node('/obj/geo1/lopnet1').selection()

# get the selection of the "root" stage context

hou.node('/stage').selection()

# set selection

paths = ['/scene/geo/sub/sphere', '/scene/geo/sub/box']
hou.node('/stage').setSelection(paths)

Get prim

We are in the Stage context in houdini.

  • In the LOP network we have 3 nodes.
    • First we have a null named “null_0”
    • Then we have a Material Library named “materiallibrary” connected
      • An inside this material library we have a material called “green”
    • Lastly we have a null named “null_1” connected at the end
  • Lets try to get the material prim
    • If we try to get it through the first null we are out of luck since the prim does not yet exists in “usd” file.
    • But we can get it if we access it through the material library or the last null node
# Try accessing through null_0

lop_node = hou.node('/stage').node('null_0')
# <hou.LopNode of type null at /stage/null_0>

stage = lop_node.stage()
# Usd.Stage.Open(rootLayer=... ...LOP:rootlayer-session.usda'))

stage.GetPrimAtPath('/materials/green')
# invalid null prim


# Try accessing through null_1

lop_node = hou.node('/stage').node('null_1')
# <hou.LopNode of type null at /stage/null_1>

stage = lop_node.stage()
# Usd.Stage.Open(rootLayer=... ...LOP:rootlayer-session.usda'))

stage.GetPrimAtPath('/materials/green')
# Usd.Prim(</materials/green>)

Camera

# camera in the lop context named camera1

# translation: (1, 2, 3)

# rotation: (-270, 0, 0)


lop_cam = hou.node('/obj/lopnet1/camera1')
stage = lop_cam.stage()
primpath = lop_cam.evalParm("primpath")
cam = stage.GetPrimAtPath(primpath)

property_names = cam.GetPropertyNames() 
# print(property_names)

# ['clippingPlanes', 'clippingRange', 'exposure', 'focalLength', ...


focus_dist_attr = cam.GetAttribute('focusDistance')
focus_dist = focus_dist_attr.Get()
#print(focus_dist)

# 9.644547462463379

#print(type(focus_dist))

# <class 'float'>


transform_attr = cam.GetAttribute('xformOp:transform')
transform = transform_attr.Get()
#print(transform)

# ( (1, 0, 0, 0), (0, 0, 1, 0), (0, -1, 0, 0), (1, 2, 3, 1) )

#print(type(transform))

# <class 'pxr.Gf.Matrix4d'>

#print('\n'.join(dir(transform)))


pos = transform.GetRow3(3)
#print(pos)

# (1, 2, 3)

#print(type(pos))

# <class 'pxr.Gf.Vec3d'>

pos_tuple = (pos[0], pos[1], pos[2])


z_axis = transform.GetRow3(2)
#print(z_axis)

# (0, -1, 0)

z_axis_tuple = (z_axis[0], z_axis[1], z_axis[2])

print(pos_tuple)
print(z_axis_tuple)

Docs

Vex

Materials

I am using an Assign material node with the primpattern parm set to All Mesh Primitives (%type:Mesh) and using vex to specify the material (see snippet below).
I am checking if a shop_materialpath attr exists, if it does use that to construct the material name, if it does not exist set the “material name” to “no_shopmatpath”. Then I am checking to see if the constructed path to the material exists if it does, assign the material (by returning it) if it does not return the path to a material that will be used when materials are missing in the lop material library. This is probably not the best way to do this, but it is a way.

string shop = usd_attrib(0, @primpath, 'shop_materialpath');
if(shop==''){
    shop = 'no_shopmatpath';
}

string mat_name = '/materials/' + shop;
int mat_exists = usd_isprim(0, mat_name);

if (mat_exists){
    return mat_name;
}

else {
    return '/materials/missing_in_lop';
}
usd_assign_mat_with_vex_01.hiplc
Assign material with vex (with fallback for missing material) using sop attrib

misc

// material binding
string mat = usd_boundmaterialpath(0, @primpath);

// set attrib
usd_setattrib(0, @primpath, 'yolo', 42);

Nodes

Rendering

Camera

Latlong cubemap in Karma
I needed to render out a cube map using Karma so this led me to dig deeper into lens shaders. Paul Ambrosiussen and Matt Estela has some good information on the subject.
There seems to be a bug when Karma is rendering with Hython so the lensshader using cvex errors out (will maybe be fixed in Houdini 19.5). A workaround is to use the vrlens shader that you can create in a shopnet context.

karma_lens_shader_01.hiplc
Latlong / Polar CVEX Lens shader

Settings

Applies renderer-specific geometry settings to geometry in the scene graph.

unshaded_color_01.hiplc
Using a mtlxuniform_edf and a render geometry settings node to render an unshaded color material

Materials

Read more about material assignment in lops cgwiki. Below is a hip file where I try out varius ways of editing material attributes in lops.

lop_assign_material_01.hiplc
setting materials in lops using attributes from sops