Skip to content

[FIX] Fix WebXR stereo frustum culling#8393

Merged
willeastcott merged 2 commits into
mainfrom
xr-cull-fix
Jan 21, 2026
Merged

[FIX] Fix WebXR stereo frustum culling#8393
willeastcott merged 2 commits into
mainfrom
xr-cull-fix

Conversation

@willeastcott

@willeastcott willeastcott commented Jan 20, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Fix incorrect frustum culling in WebXR stereo rendering that caused objects on the right edge of the right eye to be culled early
  • Add Frustum.add() method to combine frustums by keeping the outermost plane for each boundary

Details

The previous implementation used only the first view's (left eye) frustum for culling, causing objects visible in the right eye but outside the left eye's view to be incorrectly culled.

The fix computes a combined frustum that encompasses all XR views by selecting the plane with the largest distance for each of the 6 frustum boundaries (left, right, top, bottom, near, far).

Why this works for WebXR

This approach assumes frustums have parallel orientation (same plane normals). This is valid for WebXR stereo rendering because modern VR/AR headsets use parallel projection - both eyes look in the same direction with only a horizontal IPD offset, not toe-in convergence. With parallel views, frustum plane normals are identical and only distances differ, making plane merging by distance comparison correct.

Public API Addition

// Frustum.add(other) - expands frustum to contain another
frustumA.add(frustumB);

Fixes #5787
Closes #7411

Checklist

  • I have read the contributing guidelines
  • My code follows the project's coding standards
  • This PR focuses on a single change

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

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 a critical bug in WebXR stereo rendering where objects visible in the right eye were being incorrectly culled because only the left eye's frustum was used for culling decisions.

Changes:

  • Added Frustum.add() method to combine multiple frustums by keeping the most permissive plane for each boundary
  • Modified renderer to compute a combined frustum encompassing all XR views instead of using only one view's frustum
  • Removed the per-view frustum update that was overwriting the frustum in the view loop

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Description
src/core/shape/frustum.js Added add() method to merge frustums by selecting the outermost plane for each of the 6 boundaries
src/scene/renderer/renderer.js Updated XR frustum culling to combine all view frustums, imported Frustum class, added tempFrustum variable, and removed incorrect per-view frustum update

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

Comment thread src/core/shape/frustum.js
* @param {Frustum} other - The other frustum to add.
* @returns {Frustum} Self for chaining.
*/
add(other) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Such a simple solution, I did not think about.

@Maksims Maksims left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

No more phantom disappearing objects in my right eye!

@Maksims

Maksims commented Jan 21, 2026

Copy link
Copy Markdown
Collaborator

Tested on Quest 3 - works flawlessly.

Way to test:

  1. Open in VR this link: https://playcanvas.vercel.app/#/xr/xr-hands
  2. Move your hand to top right corner, so boxes of joints start to be out of frame, and they will be culled too early.

And test from this PR (https://engine-nihpvle3p-playcanvas.vercel.app/#/xr/xr-hands) works well - early culling is gone.

@willeastcott willeastcott merged commit b398732 into main Jan 21, 2026
7 checks passed
@willeastcott willeastcott deleted the xr-cull-fix branch January 21, 2026 16:34
willeastcott added a commit that referenced this pull request Jan 26, 2026
* [FIX] Fix WebXR stereo frustum culling

* Improve docs
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.

WebXR Frustum Culling - Issue

4 participants