THREE.js TrackballControls how to change center of rotation not to be in center of canvas?

Viewed 489

I'm using TrackballControls in THREE.js. Center of rotation is always in center of canvas. How can I move center of rotation up? This is what I tried:

  1. Move scene up (scene.translateY(1)) - not good, center of rotation is still in center of canvas

  2. Move camera up (camera.position.y = 1) - not good, I am looking "up" but center of rotation is still at center of canvas

  3. Change controls target (controls.target.y = 1) - not good, the whole scene just rotated so that target is in center of canvas.

window.addEventListener("DOMContentLoaded", function () {
var scene, camera, renderer, controls, sphere, red, blue, black;

scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.z = 3;
renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

geometry = new THREE.SphereGeometry(0.1);
material = new THREE.MeshBasicMaterial({
  color: 0xff0000
});
sphere1 = new THREE.Mesh(geometry, material);
scene.add(sphere1);

geometry = new THREE.SphereGeometry(0.1);
material = new THREE.MeshBasicMaterial({
  color: 0x00ff00
});
sphere2 = new THREE.Mesh(geometry, material);
scene.add(sphere2);
sphere2.position.x = 1;

geometry = new THREE.SphereGeometry(0.1);
material = new THREE.MeshBasicMaterial({
  color: 0x0000ff
});
sphere3 = new THREE.Mesh(geometry, material);
scene.add(sphere3);
sphere3.position.y = 1;

controls = new THREE.TrackballControls(camera, renderer.domElement);

function loop() {
  controls.update();
  renderer.render(scene, camera);
  requestAnimationFrame(loop);
}

// camera.position.y = 1;
// controls.target.y = 1;
// scene.position.y = 1;

loop();

})
canvas {
  box-sizing: border-box;
  border: 1px solid red;
  position: fixed;
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;
}
<script src="https://rawgit.com/mrdoob/three.js/master/build/three.min.js"></script>
<script src="https://rawgit.com/mrdoob/three.js/master/examples/js/controls/TrackballControls.js"></script>

I want to rotate around red sphere and I want red sphere to be not in center of canvas but in upper third.

1 Answers

What you're describing is kind of complicated mathematically because the camera always "looks at" the target, which means it's always going to be in the center. You also can't just rotate the camera to look down a few degrees because TrackballControls rotates around the z-axis, which makes it harder to figure out where "down" is.

Maybe you could change the viewport of the renderer with renderer.setViewport()? This squeezes the scene into the dimensions you give it, so you can position it more accurately wherever you want. In the demo below, I'm rendering the scene 2 times, one taking up the full screen, the second time only 200px tall with this code:

  // First render takes up the full screen
  renderer.setViewport(0, 0, window.innerWidth, window.innerHeight);
  renderer.render(scene, camera);

  // Second render is only 100px tall
  renderer.setViewport(0, 0, 100 * ratio, 100);
  renderer.render(scene, camera);

if this approach works for you, make sure you set renderer.autoClear = false; so it doesn't clear the canvas on each render() call.

window.addEventListener("DOMContentLoaded", function () {
var scene, camera, renderer, controls, sphere, red, blue, black;

scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.z = 3;
renderer = new THREE.WebGLRenderer();
renderer.autoClear = false;
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

geometry = new THREE.SphereGeometry(0.1);
material = new THREE.MeshBasicMaterial({
  color: 0xff0000
});
sphere1 = new THREE.Mesh(geometry, material);
scene.add(sphere1);

geometry = new THREE.SphereGeometry(0.1);
material = new THREE.MeshBasicMaterial({
  color: 0x00ff00
});
sphere2 = new THREE.Mesh(geometry, material);
scene.add(sphere2);
sphere2.position.x = 1;

geometry = new THREE.SphereGeometry(0.1);
material = new THREE.MeshBasicMaterial({
  color: 0x0000ff
});
sphere3 = new THREE.Mesh(geometry, material);
scene.add(sphere3);
sphere3.position.y = 1;

controls = new THREE.TrackballControls(camera, renderer.domElement);

var ratio = window.innerWidth / window.innerHeight;
function loop() {
  controls.update();

  // First render takes up the full screen
  renderer.setViewport(0, 0, window.innerWidth, window.innerHeight);
  renderer.render(scene, camera);
  
  // Second render is only 100px tall
  renderer.setViewport(0, 0, 100 * ratio, 100);
  renderer.render(scene, camera);
  requestAnimationFrame(loop);
}

// camera.position.y = 1;
// controls.target.y = 1;
// scene.position.y = 1;

loop();

})
canvas {
  box-sizing: border-box;
  border: 1px solid red;
  position: fixed;
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;
}
<script src="https://rawgit.com/mrdoob/three.js/master/build/three.min.js"></script>
<script src="https://rawgit.com/mrdoob/three.js/master/examples/js/controls/TrackballControls.js"></script>

Related