Motion Blur
Motion blur is computed by the hydra delegate of your choice using either the interpolated position data(deformation/xforms) or by making use of velocity/acceleration data.
You can find all the .hip files of our shown examples in our USD Survival Guide - GitHub Repo.
As noted in our Motion Blur - Computing Velocities and Accelerations, we can also easily derive the velocity and acceleration data from our position data, if the point count doesn't change.
Depending on the delegate, you will likely have to set specific primvars that control the sample rate of the position/acceleration data.
We can also easily derive velocities/accelerations from position data, if our point count doesn't change:
Motionblur | Compute | Velocity/Acceleration | Click to expand
import numpy as np
from pxr import Sdf, Usd, UsdGeom
MOTION_ATTRIBUTE_NAMES_BY_TYPE_NAME = {
UsdGeom.Tokens.Mesh: (UsdGeom.Tokens.points, UsdGeom.Tokens.velocities, UsdGeom.Tokens.accelerations),
UsdGeom.Tokens.Points: (UsdGeom.Tokens.points, UsdGeom.Tokens.velocities, UsdGeom.Tokens.accelerations),
UsdGeom.Tokens.BasisCurves: (UsdGeom.Tokens.points, UsdGeom.Tokens.velocities, UsdGeom.Tokens.accelerations),
UsdGeom.Tokens.PointInstancer: (UsdGeom.Tokens.positions, UsdGeom.Tokens.velocities, UsdGeom.Tokens.accelerations)
}
# To lookup schema specific names
# schema_registry = Usd.SchemaRegistry()
# schema = schema_registry.FindConcretePrimDefinition("Mesh")
# print(schema.GetPropertyNames())
def compute_time_derivative(layer, prim_spec, attr_name, ref_attr_name, time_code_inc, multiplier=1.0):
ref_attr_spec = prim_spec.attributes.get(ref_attr_name)
if not ref_attr_spec:
return
attr_spec = prim_spec.attributes.get(attr_name)
if attr_spec:
return
time_codes = layer.ListTimeSamplesForPath(ref_attr_spec.path)
if len(time_codes) == 1:
return
center_time_codes = {idx: t for idx, t in enumerate(time_codes) if int(t) == t}
if not center_time_codes:
return
attr_spec = Sdf.AttributeSpec(prim_spec, attr_name, Sdf.ValueTypeNames.Vector3fArray)
time_code_count = len(time_codes)
for time_code_idx, time_code in center_time_codes.items():
if time_code_idx == 0:
time_code_prev = time_code
time_code_next = time_codes[time_code_idx+1]
elif time_code_idx == time_code_count - 1:
time_code_prev = time_codes[time_code_idx-1]
time_code_next = time_code
else:
time_code_prev = time_codes[time_code_idx-1]
time_code_next = time_codes[time_code_idx+1]
time_interval_scale = 1.0/(time_code_next - time_code_prev)
ref_prev = layer.QueryTimeSample(ref_attr_spec.path, time_code_prev)
ref_next = layer.QueryTimeSample(ref_attr_spec.path, time_code_next)
if not ref_prev or not ref_next:
continue
if len(ref_prev) != len(ref_next):
continue
ref_prev = np.array(ref_prev)
ref_next = np.array(ref_next)
value = ((ref_next - ref_prev) * time_interval_scale) / (time_code_inc * 2.0)
layer.SetTimeSample(attr_spec.path, time_code, value * multiplier)
def compute_velocities(layer, prim_spec, time_code_fps, multiplier=1.0):
# Time Code
time_code_inc = 1.0/time_code_fps
prim_type_name = prim_spec.typeName
if prim_type_name:
# Defined prim type name
attr_type_names = MOTION_ATTRIBUTE_NAMES_BY_TYPE_NAME.get(prim_type_name)
if not attr_type_names:
return
pos_attr_name, vel_attr_name, _ = attr_type_names
else:
# Fallback
pos_attr_name, vel_attr_name, _ = MOTION_ATTRIBUTE_NAMES_BY_TYPE_NAME[UsdGeom.Tokens.Mesh]
pos_attr_spec = prim_spec.attributes.get(pos_attr_name)
if not pos_attr_spec:
return
# Velocities
compute_time_derivative(layer,
prim_spec,
vel_attr_name,
pos_attr_name,
time_code_inc,
multiplier)
def compute_accelerations(layer, prim_spec, time_code_fps, multiplier=1.0):
# Time Code
time_code_inc = 1.0/time_code_fps
prim_type_name = prim_spec.typeName
if prim_type_name:
# Defined prim type name
attr_type_names = MOTION_ATTRIBUTE_NAMES_BY_TYPE_NAME.get(prim_type_name)
if not attr_type_names:
return
_, vel_attr_name, accel_attr_name = attr_type_names
else:
# Fallback
_, vel_attr_name, accel_attr_name = MOTION_ATTRIBUTE_NAMES_BY_TYPE_NAME[UsdGeom.Tokens.Mesh]
vel_attr_spec = prim_spec.attributes.get(vel_attr_name)
if not vel_attr_spec:
return
# Acceleration
compute_time_derivative(layer,
prim_spec,
accel_attr_name,
vel_attr_name,
time_code_inc,
multiplier)
### Run this on a layer with time samples ###
layer = Sdf.Layer.CreateAnonymous()
time_code_fps = layer.timeCodesPerSecond or 24.0
multiplier = 5
def traversal_kernel(path):
if not path.IsPrimPath():
return
prim_spec = layer.GetPrimAtPath(path)
compute_velocities(layer, prim_spec, time_code_fps, multiplier)
compute_accelerations(layer, prim_spec, time_code_fps, multiplier)
with Sdf.ChangeBlock():
layer.Traverse(layer.pseudoRoot.path, traversal_kernel)