Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WebXRManager: Added depth sensing support #27154

Closed
wants to merge 278 commits into from
Closed

Conversation

cabanier
Copy link
Contributor

@cabanier cabanier commented Nov 9, 2023

First pass at adding support for WebXR GPU based depth sensing.
Also updated the XR samples to request depth.

We can tweak shaders and parameters later.

This contribution is funded by Meta

Copy link

github-actions bot commented Nov 9, 2023

📦 Bundle size

Full ESM build, minified and gzipped.

Filesize dev Filesize PR Diff
668.2 kB (165.8 kB) 674.9 kB (167 kB) +6.71 kB

🌳 Bundle size after tree-shaking

Minimal build including a renderer, camera, empty scene, and dependencies.

Filesize dev Filesize PR Diff
449.4 kB (108.9 kB) 455.7 kB (109.9 kB) +6.37 kB

Comment on lines 513 to 514
parameters.occlusion ? '#define USE_OCCLUSION' : '#define USE_OCCLUSION',

Copy link
Contributor

Choose a reason for hiding this comment

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

Both sides are equal; the right-hand side should be ''.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I know :-)
That is why it's WIP and still a draft PR

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@hybridherbst
Copy link
Contributor

hybridherbst commented Nov 9, 2023

Super cool!

Out of curiosity, have you done a perf/quality comparison between:

  • passing depth into each material as in this PR
  • drawing a fullscreen pass that renders depth only and sets z buffer values so others test against that, for example right from the background material

I’d think writing the z buffer directly loses the fuzziness but should be faster (can use early z discard, no extra texture fetch per fragment, etc)

@cabanier
Copy link
Contributor Author

cabanier commented Nov 9, 2023

Super cool!

Out of curiosity, have you done a perf/quality comparison between:

  • passing depth into each material as in this PR
  • drawing a fullscreen pass that renders depth only and sets z buffer values so others test against that, for example right from the background material

I’d think writing the z buffer directly loses the fuzziness but should be faster (can use early z discard, no extra texture fetch per fragment, etc)

I have not done a perf comparison. My naive way seems to integrate nicely with three, although it's a bit special since it's a renderer feature and not per material or scene.
We can revisit more optimal approaches later. I can also investigate how this is done in unity and unreal.

@mrdoob
Copy link
Owner

mrdoob commented Nov 9, 2023

@hybridherbst

drawing a fullscreen pass that renders depth only and sets z buffer values so others test against that, for example right from the background material

I feel like this approach is much more straight forward and easier to maintain, regardless of the performance implications.

I think I would try implementing this approach first before modifying all the shaders for all materials.


if (( depthData.depthNear != session.renderState.depthNear ) || ( depthData.depthFar != session.renderState.depthFar )) {

session.updateRenderState( {depthNear: depthData.depthNear, depthFar: ( depthData.depthFar > 10000 ? 10000 : depthData.depthFar ) } );
Copy link
Contributor Author

Choose a reason for hiding this comment

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

this seems like a WebXR bug

Copy link
Contributor

Choose a reason for hiding this comment

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

Spec looks like it allows infinity:

If activeState’s depthFar is greater than session’s maximum far clip plane set activeState’s depthFar to session’s maximum far clip plane.

And

The maximum far clip plane SHOULD be greater than 1000.0 (and MAY be infinite).

So if it's not working then it's a Chromium implementation issue. Idea is to allow depthFar: Infinity, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

So if it's not working then it's a Chromium implementation issue. Idea is to allow depthFar: Infinity, right?

Yes. If I pass infinity, I get an error saying the value is non-finite

@cabanier cabanier force-pushed the occlusion branch 7 times, most recently from 546c1af to e0b2926 Compare November 10, 2023 22:16
@@ -1600,7 +1615,7 @@ class WebGLRenderer {

const lightsStateVersion = lights.state.version;

const parameters = programCache.getParameters( material, lights.state, shadowsArray, scene, object );
const parameters = programCache.getParameters( material, lights.state, shadowsArray, scene, object, _occlusion );
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@mrdoob is there a better way than passing in a renderer variable?

@cabanier cabanier marked this pull request as ready for review November 10, 2023 22:44
@cabanier
Copy link
Contributor Author

@mrdoob @Mugen87 would you mind taking a look?

@snowzurfer
Copy link
Contributor

fwiw it looks like on Unity, at least for custom shaders, they suggest modifying the shaders and calculating the occlusion in the fragment shader:

half4 frag(v2f i) {
   UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(i);

   // this is something your shader will return without occlusions
   half4 fragmentShaderResult = someColor;

   // calculate UV for the depth texture lookup for occlusions
   float2 uv = i.positionNDC.xy / i.positionNDC.w;

   // pass UV and the current depth of the texel
   float occlusionValue = CalculateEnvironmentDepthOcclusion(uv, i.vertex.z);

   // consider early rejection to not write to depth if it's an opaque shader
   if (occlusionValue < 0.01) {
       discard;
   }

   // premultiply color and alpha by occlusion value
   // when it's 1 - color is not affected - virtual covers real
   // when it's 0 - texel is invisible - virtual is under real
   // when it's in between - texel is semi transparent
   fragmentShaderResult *= occlusionValue;

   return result;
}

@cabanier
Copy link
Contributor Author

fwiw it looks like on Unity, at least for custom shaders, they suggest modifying the shaders and calculating the occlusion in the fragment shader

We can play with the shaders in follow up patches. It might even be interesting to vary which depth values are honored, or provide a color to overlay real world objects

@cabanier cabanier force-pushed the occlusion branch 3 times, most recently from 5c10730 to f0b6a7c Compare November 17, 2023 17:59
gkjohnson and others added 8 commits November 23, 2023 18:16
* Linting, add maxGeometryCount to BatchedMesh, remove undocumented functions

* BatchedMesh: Update documentation
* Introduction to fragmentNode

* revision
…#27233)

* Fix .clear() using RenderTarget

* webgpu_rtt: improve a little

* cleanup

* cleanup

* revisions

* revision

* wip flipY

* update example

* cleanup
* WebGPURenderer: Depth Pixel & Logarithmic Depth Buffer

* Update puppeteer.js
renovate bot and others added 8 commits January 14, 2024 19:42
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
…were possible (mrdoob#27551)

* remove redundant code

* use cached descriptor properties for clear()

* remove unused import

---------

Co-authored-by: aardgoose <angus.sawyer@email.com>
…rdoob#27544)

* WebXR: add onLoad callback for XRHandMeshModel

* add onLoad for XRControllerModelFactory
@cabanier
Copy link
Contributor Author

@mrdoob @Mugen87 depth sensing is now on by default in the Quest browser. Can we merge this PR?

RenaudRohlinger and others added 9 commits January 16, 2024 20:57
* add lengthSq to math nodes

* forgot lengthSq doesn't exist in glsl

* cleanup

---------
* WebGPURenderer: PassNode + PostProcessing

* update sky color

* cleanup

* a bit more realistic
* WebGPURenderer improve copyTextureToBuffer

* remove unecessary comment

* correct comment
* camera proj inverse was missing

* apply mat4 to projectionMatrixInverse
@hybridherbst
Copy link
Contributor

@cabanier I was able to try this out (finally!) but still had to turn the option on on v60. Looks very jaggy/pixelated even on Quest 3 but for some experiences this might be acceptable. Is there already a newer version than v60 that has it enabled by default?

Also, some questions:

  1. can the depth transition "softness" be controlled per object?
  2. can the depth effect be turned off per object/per material? From the code it looks like this would just be material.occlusion = false;, right?

@cabanier
Copy link
Contributor Author

@cabanier I was able to try this out (finally!) but still had to turn the option on on v60. Looks very jaggy/pixelated even on Quest 3 but for some experiences this might be acceptable.

True. There are better shaders than the simple ones I added. I was hoping that we experiment with them after landing the fundamentals.

Is there already a newer version than v60 that has it enabled by default?

It should be enabled by default in 31.1. Is that not the version you have?
OS v60 has support for depth sensing.

  1. can the depth transition "softness" be controlled per object?

Not at the moment

  1. can the depth effect be turned off per object/per material? From the code it looks like this would just be material.occlusion = false;, right?

Yes, for some content it might be desired that it's not occluded. (ie hands or the controller)

@mrdoob mrdoob modified the milestones: r161, r162 Jan 31, 2024
@Mugen87 Mugen87 removed this from the r162 milestone Feb 2, 2024
@Mugen87
Copy link
Collaborator

Mugen87 commented Feb 2, 2024

Closing in favor of #27586.

@Mugen87 Mugen87 closed this Feb 2, 2024
@hybridherbst
Copy link
Contributor

@Mugen87 I'm not sure if this issue should be closed, the topic of "how to allow the shader-based version in a sample" is still very much open. Many effects can't be achieved with the v2 implementation and vice versa, both have their place.

@Mugen87
Copy link
Collaborator

Mugen87 commented Feb 3, 2024

At least at the moment the project is not willing to support both paths in WebGLRenderer, especially since this PR required in-depth changes in the material system. Unless @mrdoob does not decide different, three.js will only offer what has been implement in #27586 for now.

My hope is it will be easier to support changes like this with WebGPURenderer.

@hybridherbst
Copy link
Contributor

hybridherbst commented Feb 3, 2024

I mean like this – v2 in core, v1 as sample:
#27586 (comment)

Of course @cabanier or @mrdoob can open separate PRs for a sample that uses custom shaders, I just think it may get lost when this PR here is closed.

@cabanier
Copy link
Contributor Author

cabanier commented Feb 6, 2024

I mean like this – v2 in core, v1 as sample: #27586 (comment)

Of course @cabanier or @mrdoob can open separate PRs for a sample that uses custom shaders, I just think it may get lost when this PR here is closed.

I will make an attempt to extend the WebXRManager and write a test that uses a custom shader.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.