Hello!
I am sharing my solution here. I use python script lop to get the matrix4 for projection camera and use that as a value override for mtlx constant node. Add python script anywhere after material network node and point it to the mtlxconstant node. In python script lop set attribute name to be value
Python script (I do not take credit for this, AI wrote this)
import hou
from pxr import Usd, UsdGeom, UsdShade, Gf, Sdf
node = hou.pwd()
stage = node.editableStage()
time = Usd.TimeCode(hou.frame())
CAM_PATH = node.parm("cam_path").eval()
SHADER_PATH = node.parm("shader_path").eval()
ATTR_NAME = node.parm("attr_name").eval()
# ── Camera ──────────────────────────────────────────────────────────────────
cam_prim = stage.GetPrimAtPath(CAM_PATH)
if not cam_prim.IsValid():
raise RuntimeError(f"Camera prim not found: {CAM_PATH}")
usd_cam = UsdGeom.Camera(cam_prim)
gf_cam = usd_cam.GetCamera(time)
frustum = gf_cam.frustum
# ── ViewProjection matrix — NO transpose, MaterialX uses row-major too ──────
view = frustum.ComputeViewMatrix()
proj = frustum.ComputeProjectionMatrix()
vp = view * proj
# ── Aspect ratio ─────────────────────────────────────────────────────────────
aspect = gf_cam.horizontalAperture / gf_cam.verticalAperture
# ── Write to shader ──────────────────────────────────────────────────────────
shader_prim = stage.GetPrimAtPath(SHADER_PATH)
if not shader_prim.IsValid():
raise RuntimeError(f"Shader prim not found: {SHADER_PATH}")
shader = UsdShade.Shader(shader_prim)
inp = shader.GetInput(ATTR_NAME)
if not inp:
inp = shader.CreateInput(ATTR_NAME, Sdf.ValueTypeNames.Matrix4d)
inp.Set(vp, time)
print(f"CAM_PATH: {CAM_PATH}")
print(f"SHADER_PATH: {SHADER_PATH}")
print(f"ATTR_NAME: {ATTR_NAME}")
print(f"Aspect: {aspect:.4f} (should be ~1.777 for 16:9)")
print(f"Matrix OK: {vp}")