-
-
Notifications
You must be signed in to change notification settings - Fork 35.9k
Description
Description
I would like to be able to get a Texture
class that references the camera feed from an XR session.
Solution
User API
Proposal would be to be able to request the camera texture from the WebXRManager class, however this glosses over a lot of implementation detail and may not map to the WebXR access patterns very well.
const texture = renderer.xr.getCameraTexture();
Proposed Internal changes
I am currently having a stab implementing this, I have little experience with the internals of three.js so everything is open to change.
Would require the creation of a new THREE.js Texture class that receives the underlying WebGLTexture instead of initialising it. Lets call it ExternalTexture
. In an ExternalTexture
THREE.js is not responsible for creating / deleting the GPU data, only binding it and tracking its state. As this could introduce a lot of footguns it may be best to keep this class for internal use for now.
const glTexture = gl.createTexture();
const texture = new ExternalTexture(glTexture);
Additionally, the texture provided by the WebXR api has some limitations. It is what the WebXR specs call an opaque texture. In summary it is:
- Only a valid texture from within the
requestAnimationFrame()
callback of theXRSession
- Calling
deleteTexture
on this texture will result in anINVALID_OPERATION
error. - Must be unbound and detached from all WebGLShader objects at the end of the
requestAnimationFrame()
- Changes to dimensions or format is not allowed and any gl call that could change these parameters will fail with an
INVALID_OPERATION
error.
This will probably require a second class that extends from ExternalTexture
, lets call it ExternalOpaqueTexture
. To navigate the requirement that the texture is only valid from within an XRSession's requestAnimationFrame
callback, from what I see there are two options.
Option 1: Manually passing in the new opaque texture each frame.
const threeOpaqueTexture = new ExternalOpaqueTexture(null);
// Later on, user manually updates it.
const animate = () => {
const glOpaqueTexture = ...;
threeOpaqueTexture.image.source = glOpaqueTexture;
threeOpaqueTexture.needsUpdate = true;
}
Option 2: Passing in a getter for the opaque texture in the constructor.
const getGLOpaqueTexture = () => {
// ... interact with the XR session to get the camera feed texture
return glOpaqueTexture;
}
const threeOpaqueTexture = new ExternalOpaqueTexture(getGLOpaqueTexture);
// Three.js internals will automatically handle re-fetching it when needed.
User API Usage
There are two ways the api might have to be used based on the limitations of an opaque texture (see above). These access patterns map to option1 and option 2 of how the underlying ExternalOpaqueTextureClass might work.
- User must call
getCameraTexture
withinrequestAnimationFrame()
of the XR session or it will error. This object is valid only for therequestAnimationFrame
that it was called within and will error/warn if used otherwise. - User can call it at any time and receive a ExternalOpaqueTexture that will be managed by three.js, Three.js internals will call the function to get the texture inside of
textures.setTexture2D
. If this is called from outside of an XR sessionsrequestAnimationFrame
it will be black.
Edited for formatting and a bit of extra information
Alternatives
I have attempted to get the camera access texture using raw webgl binds and use three.resetState to clean the three state but it doesn't work.
Additional context
Draft proposal: WebXR Raw Camera Access Module
Raw WebGL Example
Raw WebGL Example, source code
#26452
Our sales person sold a project that depends on this feature so I am going to try implement it to this spec (on r151 however). I would appreciate any advice, I know almost nothing about how three.js tracks the state internally so any help or advice would be much appreciated. If i can get it working for our project I can PR on the latest threejs version in my free time.