Skip to content

Comments

Ensure camera clip planes are correctly applied during XR sessions#8464

Open
slimbuck wants to merge 1 commit intoplaycanvas:mainfrom
slimbuck:xr-dev
Open

Ensure camera clip planes are correctly applied during XR sessions#8464
slimbuck wants to merge 1 commit intoplaycanvas:mainfrom
slimbuck:xr-dev

Conversation

@slimbuck
Copy link
Member

Summary

  • When nearClip or farClip are set while an XR session is active, the new values are now forwarded to _xrProperties and the XR manager's _setClipPlanes, allowing clip plane changes to take effect at runtime during XR
  • fillShaderParams now reads from the public nearClip/farClip getters instead of the private _nearClip/_farClip backing fields, ensuring shader uniforms receive the XR-aware clip distances when XR is active

Details

Previously, setting camera.nearClip or camera.farClip during an active XR session had two issues:

  1. The new values were only stored in the private backing fields (_nearClip/_farClip) and were not propagated to _xrProperties or the XR session's depth configuration. This meant the XR runtime continued using stale clip plane values.
  2. fillShaderParams read 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

  • Verify that changing camera.nearClip / camera.farClip during an active WebXR session correctly updates the XR depth planes
  • Verify that depth-dependent shader effects (e.g., fog, depth-based post-processing) use the correct clip distances during XR
  • Confirm no regression in non-XR rendering paths

@slimbuck slimbuck requested review from a team, Maksims and Copilot February 20, 2026 09:56
@slimbuck slimbuck self-assigned this Feb 20, 2026
@slimbuck slimbuck added the area: xr XR related issue label Feb 20, 2026
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 / farClip updates into _xrProperties during active XR and update the XR session depth clip planes.
  • Update fillShaderParams to use the public nearClip / farClip getters so shader uniforms receive XR-aware values.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 334 to +338
this._nearClip = newValue;
this._projMatDirty = true;
}
if (this._xr?.active) {
this._xrProperties.nearClip = newValue;
Copy link

Copilot AI Feb 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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).

Copilot uses AI. Check for mistakes.
}
if (this._xr?.active) {
this._xrProperties.nearClip = newValue;
this._xr._setClipPlanes(newValue, this._xrProperties.farClip);
Copy link

Copilot AI Feb 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
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);
}

Copilot uses AI. Check for mistakes.
this._farClip = newValue;
this._projMatDirty = true;
}
if (this._xr?.active) {
Copy link

Copilot AI Feb 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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).

Suggested change
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;
}

Copilot uses AI. Check for mistakes.
}
if (this._xr?.active) {
this._xrProperties.farClip = newValue;
this._xr._setClipPlanes(this._xrProperties.nearClip, newValue);
Copy link

Copilot AI Feb 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_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.

Suggested change
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);
}

Copilot uses AI. Check for mistakes.
@Maksims
Copy link
Collaborator

Maksims commented Feb 23, 2026

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?
There is a case, which need to be tested: WebXR specs dictate that clipping is applied to planes if they don't follow min/max rules. Which might affect depth based APIs, like Depth Sensing. So if in PlayCanvas we set camera.farClip to 100, and expect that to be true when data by WebXR is provided, it might be actually not aligned.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: xr XR related issue

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants