WebXR: camera's position not changing when using OrbitControls

I am trying to change the position of the camera using

camera.position.set(a, 0, 0);
controls.update();

with “a” being a Number changing each frame from 0 to 20, then from 20 to 0 and so on.

The camera’s position is changing when the immersive-vr session is still off:

However when the vr button is triggered, the immersive-vr session opens, but the scene isn’t changing position.

Using the webxr dev tool from the browser, I can change the orientation of the headset so the image isn’t frozen.

Any idea?

The html:

<!DOCTYPE html>
<html lang="en">
	<head>
		<title>three.js ar - cones</title>
		<meta charset="utf-8">
		<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
		<link type="text/css" rel="stylesheet" href="main.css">
	</head>
	<body>

		<!-- <div id="info">
			<a href="https://threejs.org" target="_blank" rel="noopener">three.js</a> ar - cones<br/>(Chrome Android 81+)
		</div> -->

		<!-- Import maps polyfill -->
		<!-- Remove this when import maps will be widely supported -->

		<script async src="https://unpkg.com/es-module-shims@1.3.6/dist/es-module-shims.js"></script>
		<script type="importmap">
			{
				"imports": {
					"three": "/node_modules/three/build/three.module.js"
				}
			}
		</script>

		<script type="module" src="immersion4.js"></script>
	</body>
</html>

The immersion4.js script :

import * as THREE from '/node_modules/three/build/three.module.js';
import { GLTFLoader } from '/node_modules/three/examples/jsm/loaders/GLTFLoader.js';
import { ARButton } from '/node_modules/three/examples/jsm/webxr/ARButton.js';
import { VRButton } from '/node_modules/three/examples/jsm/webxr/VRButton.js';
import { OrbitControls } from '/node_modules/three/examples/jsm/controls/OrbitControls.js';

let camera, scene, renderer, views, frame, refSpace, pos;
let controls;
let a=0, b=null;

init();
animate();

function init() {

  const container = document.createElement( 'div' );
  document.body.appendChild( container );

  scene = new THREE.Scene();

  camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 0.01, 20 );

  const light = new THREE.HemisphereLight( 0xffffff, 0xbbbbff, 1 );
  light.position.set( 0.5, 1, 0.25 );
  scene.add( light );

  let loader = new GLTFLoader()
  loader.load(
      'media/gltf/space/space.gltf',
      function ( gltf ) {
          gltf.scale = [0.1, 0.1, 0.1];
          scene.add(gltf.scene);
      },
    // called while loading is progressing
    function ( xhr ) {

      console.log( ( xhr.loaded / xhr.total * 100 ) + '% loaded' );

    },
    // called when loading has errors
    function ( error ) {

      console.log( 'An error happened: ' + error);

    });

  //

  renderer = new THREE.WebGLRenderer( { antialias: true, alpha: true } );
  renderer.setPixelRatio( window.devicePixelRatio );
  renderer.setSize( window.innerWidth, window.innerHeight );
  renderer.xr.enabled = true;
  renderer.xr.setReferenceSpaceType('local'); // thought this could change something
  container.appendChild( renderer.domElement );

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

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

}

function animate() {

  renderer.setAnimationLoop( render );

}

function render() {

  // To make the back and fourth motion
  if ( b == null ) {
    a += 0.1;
  } else if ( b == 'back' ) {
    a -= 0.1;
  }

  if ( a > 20 && b == null ) {
    b = 'back';
  } else if ( a < 0 ) {
    b = null;
  }


  frame = renderer.xr.getFrame();
  // frame = null when none XRSession got requested
  if ( frame != null ) {
    refSpace = renderer.xr.getReferenceSpace();

    views = frame.getViewerPose(refSpace).views;

    pos = views[0].transform.position;
    //console.log([pos.x, pos.y, pos.z]);
    //console.log(pos);
    //camera.position.set(pos.x + a, pos.y, pos.z);

    // SCENE ISN'T CHANGING WHEN XRSession IS OPENED
    camera.position.set(a, 0, 0);
    controls.update();
    console.log(camera.position);
  }

  // SCENE IS CHANGING IF NO XRSession IS OPENED
  camera.position.set(a, 0, 0);
  controls.update();
  console.log(camera.position);
  

  renderer.render( scene, camera );

}

Thank youuu :slight_smile:

Function ANIMATE is a recursive function

function animate(){
//Your Action per Frame
requestAnimatonFrame(animate)/call next _possble_for_you_system frame/
}

Thanks for looking into it Stan!

If I do :

function animate(){
//Your Action per Frame
requestAnimatonFrame(animate)/ *call next _possble_for_you_system frame* /
}

Where should I use the

renderer.setAnimationLoop( render );

and the :

renderer.render(scene, camera);

?

After your message, I changed, the animate function by this but it’s still the same result :

function animate() {

  renderer.setAnimationLoop( render );

  var f = renderer.xr.getFrame();

  if (f != null) {

    f.session.requestAnimatonFrame( animate );

  }

}

Hello John
:thinking:
I meant that the function animate has to call upon itself, whereas in your case it is only called once.
Now I see that you don’t actually need to use the function animate
It’s possible to use either setAnimationLoop or requestAnimationFrame, but not both!

For a better explanation you can read this thread:
Is there a performance difference between requestAnimationFrame() and setAnimationLoop()? - Questions - three.js forum (threejs.org)

Best regards

1 Like

That is the problem : the function render() by frame-non-null returns the same result as frame-null

Try to write your function like this, maybe it will help:

function render() {

// To make the back and fourth motion
if ( b == null ) {
a += 0.1;
} else if ( b == ‘back’ ) {
a -= 0.1;
}

if ( a > 20 && b == null ) {
b = ‘back’;
} else if ( a < 0 ) {
b = null;
}

frame = renderer.xr.getFrame();
// frame = null when none XRSession got requested
if ( frame != null ) {
refSpace = renderer.xr.getReferenceSpace();

views = frame.getViewerPose(refSpace).views;

pos = views[0].transform.position;
console.log([pos.x, pos.y, pos.z]);
console.log(pos);
camera.position.set(pos.x + a, pos.y, pos.z);

} else {

// SCENE IS CHANGING IF NO XRSession IS OPENED
camera.position.set(a, 0, 0);
controls.update();
console.log(camera.position);
}

renderer.render( scene, camera );

}

Nice try Stan, but it was still blocking, and

I found out !

So with WebXR, when a session is opened via requestSession(), the renderer object is using another camera than the one given. So I wasn’t moving the camera used by the renderer, even if I was giving the camera object to renderer.render( scene, camera).

To get the camera of the session and change its position, in immersive-vr, you have to do this:

function render() {

  if ( b == null ) {
    a += 0.1;
  } else if ( b == 'back' ) {
    a -= 0.1;
  }

  if ( a > 20 && b == null ) {
    b = 'back';
  } else if ( a < 0 ) {
    b = null;
  }
  renderer.xr.getCamera().cameras[0].position.x = a;

  console.log(renderer.xr.getCamera().cameras[0].position);

  renderer.render( scene, renderer.xr.getCamera().cameras[0] );

Then you can launch the loop like this:

function animate() {

  renderer.setAnimationLoop( render );
  
}

This is my final render function :heart_eyes: :heart_eyes: :heart_eyes:

function render() {

  if ( b == null ) {
    a += 0.1;
  } else if ( b == 'back' ) {
    a -= 0.1;
  }

  if ( a > 20 && b == null ) {
    b = 'back';
  } else if ( a < 0 ) {
    b = null;
  }

  try {
    frame = renderer.xr.getFrame();

    refSpace = renderer.xr.getReferenceSpace();

    views = frame.getViewerPose(refSpace).views;

    pos = views[0].transform.position;

    var c = renderer.xr.getCamera().cameras[0].position;

    renderer.xr.getCamera().cameras[0].position.x = pos.x;
    renderer.xr.getCamera().cameras[0].position.y = pos.y;
    renderer.xr.getCamera().cameras[0].position.z = pos.z;
    socket.send(pos)
    renderer.render( scene, renderer.xr.getCamera().cameras[0] );

  } catch (e) {

    var c = 'yupi';

  }

  socket.send(c);

}

(The socket is a socket.io to get the data from my computer).
Don’t hesitate if you have any remarks or questions, I can answer.