0
votes

I'm using Three.js to develop a player for 360° pictures. My scene contains a sphere (BasicMeshMaterial) that has a 360° picture as texture. The camera is in the middle of that sphere, so that the user sees the picture, that is applied to the sphere's "walls".

I recently added the possibility to click on some spots of the sphere, to rotate the camera towards that spot. The camera smoothly moves to this spot, untill it has this spot in the center of the screen.

My problem is that sometimes the camera easing (tween animation) that is supposed to focus the spot clicked uses a very strange path and it's all very ugly. This seems to happen when the initial position of the camera and the target position cross a certain point of the sphere. I have almost no clue what happens. Do you ? Could it have a link with the sphere, or the camera is only playing with quaternions and so it's not affected by others objects ? Then what could make it chose wrong path, and how come it happens only with a certain angle of the sphere ? anything would be help !

The player is testable at http://isiko.io/, after scrolling a bit. I can show you the code but don't really know what to pick. Tell me if you need the camera's rotation,or the sphere's initiation..

Thank you for your feedbacks.

1

1 Answers

0
votes

There is another approach with tweening an angle around an axis:

var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.set(0, 0, 0);
var renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

var sphere = new THREE.Mesh(new THREE.SphereGeometry(10, 32, 24), new THREE.MeshBasicMaterial({
  color: "yellow",
  wireframe: true
}));
scene.add(sphere);

var raycaster = new THREE.Raycaster();
var mouse = new THREE.Vector2();
var center = new THREE.Vector3();
var vectorStart = new THREE.Vector3(0, 0, -10); // in front of the camera
var vectorEnd = new THREE.Vector3();
var angle = {
  value: 0
};
var angleEnd = {
  value: 0
};
var normal = new THREE.Vector3();
var lookAt = new THREE.Vector3();
var isMoving = false;
window.addEventListener("mousedown", onMouseDown, false);

function onMouseDown(event) {

  if (isMoving) return;

  mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
  mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
  raycaster.setFromCamera(mouse, camera);
  let newPosition = raycaster.ray.at(10);
  setPoint(newPosition);

  // prepare the values we need for tweening
  vectorEnd.copy(newPosition);
  angle.value = 0; // we will tween this value
  angleEnd.value = vectorStart.angleTo(vectorEnd); // to this one
  normal.copy(vectorStart).cross(vectorEnd).normalize(); // around this axis
  
  // tween the angle
  var tween = new TWEEN.Tween(angle).to(angleEnd, 1000).delay(250).easing(TWEEN.Easing.Cubic.InOut).onStart(function(){
    isMoving = true;
  }).onUpdate(
    function() {
      camera.lookAt(lookAt.copy(vectorStart).applyAxisAngle(normal, angle.value));
    }
  ).onComplete(function() {
    isMoving = false;
    vectorStart.copy(vectorEnd);
  }).start();
}

function setPoint(position) {
  let point = new THREE.Mesh(new THREE.SphereGeometry(0.125, 4, 2), new THREE.MeshBasicMaterial({
    color: "red",
    wireframe: true
  }));
  point.position.copy(position);
  scene.add(point);
}

render()

function render() {
  requestAnimationFrame(render);
  TWEEN.update(); // don't forget to put this line into the animation loop, when you use Tween.js
  renderer.render(scene, camera);
}
body {
  overflow: hidden;
  margin: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/92/three.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/tween.js/17.2.0/Tween.min.js"></script>