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>