Writes the world-space picking ray through the normalized device coordinate
(ndcX, ndcY) into outOrigin and outDirection (§71).
NDC runs [-1, 1] on both axes with +Y up (§7a), so (-1, -1) is the
bottom-left of the viewport and (0, 0) its centre; converting a pointer
position in CSS pixels into NDC is the pointer source's job (§72), not this
function's. The ray starts on the near plane and points into the scene,
outDirection normalized, and it is correct for both projections: a
perspective camera's rays fan out from the eye, an orthographic camera's are
all parallel and start at different places on the near plane.
The camera's world matrix is resolved on demand
(resolveWorldTransform), so a camera that has just moved — or that hangs
off a spring arm which has just moved — produces a correct ray without the
caller sequencing a resolve pass first. That walk is O(depth) and
version-cached, so on the normal path (the frame already resolved the scene)
it costs one comparison per ancestor.
The camera's projection is taken as-is. inverseProjectionMatrix is
written by Camera.updateProjectionMatrix, which §47/WP-3.1 make explicit:
after changing a field like aspect on a resize, call it. Picking cannot do
that itself without guessing the depthRange the renderer used.
depthRange (plan D8) says which clip-space depth convention that projection
was built with, and only affects where along the ray the origin lands —
the near plane's NDC depth is -1 under "negative-one-to-one" and 0
under "zero-to-one". Passing the wrong one still yields the same ray
line, so hits are unaffected and only reported distances shift by a
constant; passing the right one makes them distances from the near plane. It
defaults to "negative-one-to-one" to match updateProjectionMatrix, so
WebGL 2 callers never pass it and WebGPU callers pass "zero-to-one"
(decision, WP-3a.1 — a camera does not store its depth range, because it
belongs to the renderer that draws the camera, and the same camera may feed
two backends).
Allocates nothing. A degenerate projection (a zero-sized view volume,
near === far) leaves outDirection zero-length rather than NaN; see
pick, which treats such a ray as hitting nothing.
Writes the world-space picking ray through the normalized device coordinate
(ndcX, ndcY)intooutOriginandoutDirection(§71).NDC runs
[-1, 1]on both axes with +Y up (§7a), so(-1, -1)is the bottom-left of the viewport and(0, 0)its centre; converting a pointer position in CSS pixels into NDC is the pointer source's job (§72), not this function's. The ray starts on the near plane and points into the scene,outDirectionnormalized, and it is correct for both projections: a perspective camera's rays fan out from the eye, an orthographic camera's are all parallel and start at different places on the near plane.The camera's world matrix is resolved on demand (
resolveWorldTransform), so a camera that has just moved — or that hangs off a spring arm which has just moved — produces a correct ray without the caller sequencing a resolve pass first. That walk is O(depth) and version-cached, so on the normal path (the frame already resolved the scene) it costs one comparison per ancestor.The camera's projection is taken as-is.
inverseProjectionMatrixis written byCamera.updateProjectionMatrix, which §47/WP-3.1 make explicit: after changing a field likeaspecton a resize, call it. Picking cannot do that itself without guessing thedepthRangethe renderer used.depthRange(plan D8) says which clip-space depth convention that projection was built with, and only affects where along the ray the origin lands — the near plane's NDC depth is-1under"negative-one-to-one"and0under"zero-to-one". Passing the wrong one still yields the same ray line, so hits are unaffected and only reported distances shift by a constant; passing the right one makes them distances from the near plane. It defaults to"negative-one-to-one"to matchupdateProjectionMatrix, so WebGL 2 callers never pass it and WebGPU callers pass"zero-to-one"(decision, WP-3a.1 — a camera does not store its depth range, because it belongs to the renderer that draws the camera, and the same camera may feed two backends).Allocates nothing. A degenerate projection (a zero-sized view volume,
near === far) leavesoutDirectionzero-length rather thanNaN; see pick, which treats such a ray as hitting nothing.