WebXR transform issue with Volume Shader

Hi Guys,
There seems to be an issue with the Volume Shader and WebXR.

I tried the three.js examples
example with WebXR by adding the VR button and changing the renderer.render() loop as recommended and there seems to be a transform missing between the headset and the scene inside VR. The objects moves (both rotate and translate) as the headset moves.

There is an article here that hints at this issue but there does not seem to be a solution:

Here is the modified volume shader example with XR. I have padded the extra lines with //—XR— so it is easy for someone looking into it.

I am using a HP Reverb (Windows Mixed Reality) on chrome.

Thanks
-AM

<title>three.js webgl - volume rendering example</title>

<meta charset="utf-8">

<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">

<link type="text/css" rel="stylesheet" href="main.css">
<div id="info">

    <a href="https://threejs.org" target="_blank" rel="noopener">three.js</a> - Float volume render test (mip / isosurface)

</div>

<div id="inset"></div>



<script type="module">  

    import * as THREE from 'https://cdn.skypack.dev/three';

    import { GUI } from 'https://cdn.skypack.dev/three/examples/jsm/libs/dat.gui.module.js';

    import { OrbitControls } from 'https://cdn.skypack.dev/three/examples/jsm/controls/OrbitControls.js';

    import { NRRDLoader } from 'https://cdn.skypack.dev/three/examples/jsm/loaders/NRRDLoader.js';

    import { VolumeRenderShader1 } from 'https://cdn.skypack.dev/three/examples/jsm/shaders/VolumeShader.js';

    import { WEBGL } from 'https://cdn.skypack.dev/three/examples/jsm/WebGL.js';

    //-----XR-----

    import { VRButton } from 'https://cdn.skypack.dev/three/examples/jsm/webxr/VRButton.js';        

    //-----XR-----

    if ( WEBGL.isWebGL2Available() === false ) {

        document.body.appendChild( WEBGL.getWebGL2ErrorMessage() );

    }

    let renderer,

        scene,

        camera,

        controls,

        material,

        volconfig,

        cmtextures;

    init();

    

    //-----XR-----

    animate();

    //-----XR-----

    function init() {

        scene = new THREE.Scene();

        // Create renderer

        renderer = new THREE.WebGLRenderer();

        renderer.setPixelRatio( window.devicePixelRatio );

        renderer.setSize( window.innerWidth, window.innerHeight );

        

        //-----XR-----

        renderer.xr.enabled = true;

        //-----XR-----

        document.body.appendChild( renderer.domElement );

        // Create camera (The volume renderer does not work very well with perspective yet)

        const h = 512; // frustum height

        const aspect = window.innerWidth / window.innerHeight;

        camera = new THREE.OrthographicCamera( - h * aspect / 2, h * aspect / 2, h / 2, - h / 2, 1, 1000 );

        camera.position.set( 0, 0, 128 );

        camera.up.set( 0, 0, 1 ); // In our data, z is up

        // Create controls

        controls = new OrbitControls( camera, renderer.domElement );

        controls.addEventListener( 'change', render );

        controls.target.set( 64, 64, 128 );

        controls.minZoom = 0.5;

        controls.maxZoom = 4;

        controls.update();

        // scene.add( new AxesHelper( 128 ) );

        // Lighting is baked into the shader a.t.m.

        // let dirLight = new DirectionalLight( 0xffffff );

        // The gui for interaction

        volconfig = { clim1: 0, clim2: 1, renderstyle: 'iso', isothreshold: 0.15, colormap: 'viridis' };

        const gui = new GUI();

        gui.add( volconfig, 'clim1', 0, 1, 0.01 ).onChange( updateUniforms );

        gui.add( volconfig, 'clim2', 0, 1, 0.01 ).onChange( updateUniforms );

        gui.add( volconfig, 'colormap', { gray: 'gray', viridis: 'viridis' } ).onChange( updateUniforms );

        gui.add( volconfig, 'renderstyle', { mip: 'mip', iso: 'iso' } ).onChange( updateUniforms );

        gui.add( volconfig, 'isothreshold', 0, 1, 0.01 ).onChange( updateUniforms );

        // Load the data ...

        //new NRRDLoader().load( "models/nrrd/stent.nrrd", function ( volume )

        new NRRDLoader().load( "../models/stent.nrrd", function ( volume ) {

            // Texture to hold the volume. We have scalars, so we put our data in the red channel.

            // THREEJS will select R32F (33326) based on the THREE.RedFormat and THREE.FloatType.

            // Also see https://www.khronos.org/registry/webgl/specs/latest/2.0/#TEXTURE_TYPES_FORMATS_FROM_DOM_ELEMENTS_TABLE

            // TODO: look the dtype up in the volume metadata

            const texture = new THREE.DataTexture3D( volume.data, volume.xLength, volume.yLength, volume.zLength );

            texture.format = THREE.RedFormat;

            texture.type = THREE.FloatType;

            texture.minFilter = texture.magFilter = THREE.LinearFilter;

            texture.unpackAlignment = 1;

            // Colormap textures

            cmtextures = {

                //viridis: new THREE.TextureLoader().load( 'textures/cm_viridis.png', render ),

                //gray: new THREE.TextureLoader().load( 'textures/cm_gray.png', render )

                viridis: new THREE.TextureLoader().load( '../node_modules/three/examples/jsm/textures/cm_viridis.png', render ),

                gray: new THREE.TextureLoader().load( '../node_modules/three/examples/jsm/textures/cm_gray.png', render )

            };

            // Material

            const shader = VolumeRenderShader1;

            const uniforms = THREE.UniformsUtils.clone( shader.uniforms );

            uniforms[ "u_data" ].value = texture;

            uniforms[ "u_size" ].value.set( volume.xLength, volume.yLength, volume.zLength );

            uniforms[ "u_clim" ].value.set( volconfig.clim1, volconfig.clim2 );

            uniforms[ "u_renderstyle" ].value = volconfig.renderstyle == 'mip' ? 0 : 1; // 0: MIP, 1: ISO

            uniforms[ "u_renderthreshold" ].value = volconfig.isothreshold; // For ISO renderstyle

            uniforms[ "u_cmdata" ].value = cmtextures[ volconfig.colormap ];

            material = new THREE.ShaderMaterial( {

                uniforms: uniforms,

                vertexShader: shader.vertexShader,

                fragmentShader: shader.fragmentShader,

                side: THREE.BackSide // The volume shader uses the backface as its "reference point"

            } );

            // THREE.Mesh

            const geometry = new THREE.BoxGeometry( volume.xLength, volume.yLength, volume.zLength );

            geometry.translate( volume.xLength / 2 - 0.5, volume.yLength / 2 - 0.5, volume.zLength / 2 - 0.5 );

            const mesh = new THREE.Mesh( geometry, material );

            scene.add( mesh );

            //-----XR-----

            //render();

            //-----XR-----

        } );

        window.addEventListener( 'resize', onWindowResize );

        document.body.appendChild( VRButton.createButton( renderer ) );

    }

    function updateUniforms() {

        material.uniforms[ "u_clim" ].value.set( volconfig.clim1, volconfig.clim2 );

        material.uniforms[ "u_renderstyle" ].value = volconfig.renderstyle == 'mip' ? 0 : 1; // 0: MIP, 1: ISO

        material.uniforms[ "u_renderthreshold" ].value = volconfig.isothreshold; // For ISO renderstyle

        material.uniforms[ "u_cmdata" ].value = cmtextures[ volconfig.colormap ];

        //-----XR-----

        //render();

        //-----XR-----

    }

    function onWindowResize() {

        renderer.setSize( window.innerWidth, window.innerHeight );

        const aspect = window.innerWidth / window.innerHeight;

        const frustumHeight = camera.top - camera.bottom;

        camera.left = - frustumHeight * aspect / 2;

        camera.right = frustumHeight * aspect / 2;

        camera.updateProjectionMatrix();

        //-----XR-----

        //render();

        //-----XR-----

    }

    //-----XR-----

    function animate() {

            renderer.setAnimationLoop( render );

    }

    //-----XR-----

    function render() {

        renderer.render( scene, camera );

    }

</script>