Add support for USD coordinate systems (coordSys) for camera projection - #2720
Open
cpichard wants to merge 21 commits into
Open
Add support for USD coordinate systems (coordSys) for camera projection#2720cpichard wants to merge 21 commits into
cpichard wants to merge 21 commits into
Conversation
# Conflicts: # libs/render_delegate/mesh.cpp
cpichard
marked this pull request as ready for review
August 6, 2026 12:46
Unregister the coordinate-system camera from the per-render aspect correction on both frustum-less paths, so a projector that stops resolving to a camera no longer has its screen window rewritten from a stale aperture ratio. Only rewrite a MaterialX "space" input when the value actually holds a string or token; VtValueGetString yields an empty string otherwise, which clobbered the OSL node's default space. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Changes proposed in this pull request
Add support for USD coordinate systems (
UsdShadeCoordSysAPI/ HydracoordSys) in the render delegate, so MaterialX/OSL shaders can transform into a named coordinate system — typically a camera projection (map_proj,map_proj:ndc,map_proj:raster,map_proj:screenas authored by Houdini/Solaris).Arnold's OSL render services resolve a named space by looking up a camera node whose name matches the part before the suffix (
<name>.camera,<name>.NDC,<name>.screen,<name>.raster), viaAiNodeLookUpByName. The implementation follows from that:HdArnoldCoordSyssprim (libs/render_delegate/coord_sys.{h,cpp}) — newHdCoordSysimplementation that creates an Arnold camera node per coordinate system, named uniquely from the sprim id so several coordinate systems sharing a name never collide. When the coordinate system is bound to a camera prim, the bound camera's transform and frustum are mirrored (persp_cameraorortho_camera, recreating the node in place if the projection type changes), so the projective.NDC/.screen/.rasterspaces match the projector. The bound camera is resolved from the sprim id in the scene-index path, and by world-transform matching against thecamerasprims in the legacy delegate path (where the id only encodes the binding relationship), sohydraandhydra2agree.coordSysregistered as a supported sprim type and created inHdArnoldRenderDelegate::CreateSprim(render_delegate.cpp). Registering the token is also what makesUsdImagingDelegateanswer coord-sys binding queries.plugins/scene_index/coordSysSIP.{h,cpp}) —UsdImagingCreateSceneIndicesdoes not turn coordSys bindings into coordSys prims, so a renderer scene-index plugin appendsHdsiCoordSysPrimSceneIndexfor renderer "Arnold". This makes it work in any Hydra host (usdview, husk, the procedural), not just one. Correspondingly, the procedural's manualAppendSceneIndicesForRenderercall inreader.cppwas removed:HdRenderIndexalready applies renderer plugins at its terminal, so it ran every plugin twice — harmless for the pre-existing filtering plugins, but the prim-adding coordSys plugin produced a duplicate camera.libs/common/materials_utils.cpp) — MaterialX geometric nodes (ND_position_vector3, ...) become Arnold OSL nodes whosespaceinput is rewritten to Arnold's naming:map_proj:ndc→map_proj.NDC,:raster/:screen→ dotted, plain names →<name>.camera. Standard MaterialX/OSL spaces (model,object,world, ...) and already-dotted values are left untouched. Shared by the procedural and render-delegate paths.node_graph.{h,cpp}, and every rprim that assigns a material) — because Arnold resolves named spaces globally by node name, the material'sspacestrings must point at the camera bound by the specific rprim.HdArnoldNodeGraph::RemapCoordSysSpacesrewrites them, and the remap-awareGetCached*Shader(remap)overloads let a material shared by rprims with different bindings work: the first binding claims the base network (no overhead in the common case), and each conflicting binding gets its own re-translated variant with stable node names across re-syncs. The rprim side is the sharedHdArnoldGetCoordSysRemap()helper incoord_sys.{h,cpp}, used bymesh.cpp,basis_curves.cpp,points.cpp,native_rprim.cpp,gaussian_splat.cppandvolume.cpp; each of those re-assigns its material onDirtyCategories(which carries binding changes) and includes that bit in its initial dirty bits. Light-filter graphs stay on the plain accessor — lights have no coordSys bindings.render_delegate.{h,cpp},render_pass.cpp) — Arnold derives every camera's vertical fov from the render frame aspect ratio (AiWorldToScreenMatrix), not from the camera's own aperture, so a projector's vertical extent was wrong whenever the render resolution's aspect differed from the projector's aperture. Coordinate-system cameras register with the delegate andUpdateCoordSysCameraProjections()cancels the frame aspect from their vertical screen window inHdArnoldRenderPass::_Execute, afterxres/yresare final for the render (a sync-time read is stale). It only writes when the value changed, so it does not restart the render every frame. This matches Karma, which uses the projector's own aperture.config.{h,cpp}) — Arnold'sNDCconvention is Y-opposite to itsscreen/raster, so no single camera flip makes all four spaces agree.HDARNOLD_coordsys_flip_ndc_v(default on) routes.NDCthrough a second, extra-flipped camera node while.camera/.screen/.rasterkeep the primary one;HDARNOLD_coordsys_flip_v(default off) flips every space together. The defaults were chosen to agree with Karma out of the box for all four spaces.test_13221(perspective projectors),test_13221.1(orthographic projectors),test_13221.2(one shared material bound by two rprims to different cameras) andtest_13221.3(curves + points, i.e. non-mesh rprims), exercising.camera/.NDC/.raster/.screenon both thehydraandhydra2passes. CoordSys is implemented in the render delegate only, so these opt out of the proceduralusdpass through a per-pass skip mechanism:tools/test/testsuite.pynow populates the previously-unusedpass_<name>skip lists fromtestsuite/groups.Issues fixed in this pull request
Fixes #32 (ARNOLD-13221)
Additional context
Reference: Coordinate Systems in USD proposal
Known limitations / follow-ups, all deliberate:
usd) pass has no coordSys support — implemented in the render delegate only; the reader would need the same camera-node creation, frustum mirroring, name remap and aspect cancellation. Either factor the shared logic out ofcoord_sys.cpp, or let the reader route through Hydra..camerasuffix. The colon forms are unambiguous. Gating onGetCoordSysBindings()membership would change the sharedmaterials_utils.cppsignatures.nullptr(transform-only fallback) rather than pick wrong. The legacy delegate exposes no way to map the binding-relationship sprim id back to its backing camera prim.🤖 Generated with Claude Code