Ensure camera clip planes are correctly applied during XR sessions#8464
Ensure camera clip planes are correctly applied during XR sessions#8464slimbuck wants to merge 1 commit intoplaycanvas:mainfrom
Conversation
There was a problem hiding this comment.
Pull request overview
This PR fixes runtime clip plane updates during active WebXR sessions by ensuring camera near/far clip changes are propagated into XR-specific camera properties and reflected in shader parameters.
Changes:
- Forward
nearClip/farClipupdates into_xrPropertiesduring active XR and update the XR session depth clip planes. - Update
fillShaderParamsto use the publicnearClip/farClipgetters so shader uniforms receive XR-aware values.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| this._nearClip = newValue; | ||
| this._projMatDirty = true; | ||
| } | ||
| if (this._xr?.active) { | ||
| this._xrProperties.nearClip = newValue; |
There was a problem hiding this comment.
When XR is active, this setter can change the effective near clip (via _xrProperties.nearClip) even if _nearClip already equals newValue (e.g., _xrProperties.nearClip derived from the XR view). In that case _projMatDirty is not set, so projectionMatrix can remain stale even though the getters now return different values. Consider setting _projMatDirty = true whenever _xrProperties.nearClip changes while XR is active (or include _xrProperties.nearClip in the equality check).
| } | ||
| if (this._xr?.active) { | ||
| this._xrProperties.nearClip = newValue; | ||
| this._xr._setClipPlanes(newValue, this._xrProperties.farClip); |
There was a problem hiding this comment.
Same concern here as in farClip: calling the private XrManager method _setClipPlanes from Camera creates cross-module coupling. Prefer a public method on XrManager (or an event-based handoff) for updating the XR session depth near/far.
| this._xr._setClipPlanes(newValue, this._xrProperties.farClip); | |
| const xrManager = this._xr; | |
| if (typeof xrManager.setClipPlanes === 'function') { | |
| xrManager.setClipPlanes(newValue, this._xrProperties.farClip); | |
| } else if (typeof xrManager._setClipPlanes === 'function') { | |
| xrManager._setClipPlanes(newValue, this._xrProperties.farClip); | |
| } |
| this._farClip = newValue; | ||
| this._projMatDirty = true; | ||
| } | ||
| if (this._xr?.active) { |
There was a problem hiding this comment.
When XR is active, this setter can change the effective far clip (via _xrProperties.farClip) even if _farClip already equals newValue (e.g., if _xrProperties.farClip was initialized from the XR view projection). In that case _projMatDirty is not set, so projectionMatrix (which uses the getters) can remain stale. Consider marking _projMatDirty = true whenever _xrProperties.farClip changes while XR is active (or broaden the equality check to include _xrProperties.farClip).
| if (this._xr?.active) { | |
| if (this._xr?.active) { | |
| // when XR is active, the effective far clip comes from _xrProperties.farClip, | |
| // so mark the projection matrix dirty if that value changes | |
| if (this._xrProperties.farClip !== newValue) { | |
| this._projMatDirty = true; | |
| } |
| } | ||
| if (this._xr?.active) { | ||
| this._xrProperties.farClip = newValue; | ||
| this._xr._setClipPlanes(this._xrProperties.nearClip, newValue); |
There was a problem hiding this comment.
_setClipPlanes is documented/implemented as a private XrManager method. Calling it directly from Camera tightly couples these modules and makes future refactors harder. Consider adding a public XR API for updating clip planes (or routing this through an event / dedicated method) instead of calling a private underscore method.
| this._xr._setClipPlanes(this._xrProperties.nearClip, newValue); | |
| const xr = this._xr; | |
| if (typeof xr.setClipPlanes === 'function') { | |
| xr.setClipPlanes(this._xrProperties.nearClip, newValue); | |
| } else if (typeof xr._setClipPlanes === 'function') { | |
| xr._setClipPlanes(this._xrProperties.nearClip, newValue); | |
| } |
|
WebXR has min/max clipping for near/far planes, (maximum 0.1m for near plane and at least 1000m for far plane). Is this reflected in properties when setting them and reading them back, or the value will be provided as is? |
Summary
nearCliporfarClipare set while an XR session is active, the new values are now forwarded to_xrPropertiesand the XR manager's_setClipPlanes, allowing clip plane changes to take effect at runtime during XRfillShaderParamsnow reads from the publicnearClip/farClipgetters instead of the private_nearClip/_farClipbacking fields, ensuring shader uniforms receive the XR-aware clip distances when XR is activeDetails
Previously, setting
camera.nearCliporcamera.farClipduring an active XR session had two issues:_nearClip/_farClip) and were not propagated to_xrPropertiesor the XR session's depth configuration. This meant the XR runtime continued using stale clip plane values.fillShaderParamsread directly from the private backing fields, bypassing the getters that return XR-specific values when XR is active. This caused shader parameters (used for depth calculations) to be inconsistent with the actual projection matrices provided by the XR system.Test Plan
camera.nearClip/camera.farClipduring an active WebXR session correctly updates the XR depth planes