0
votes

I have a 3d scene with a camera setup to look always at the center of that scene.

How can I change the camera's coordinate to rotate and zoom around that center by updating the camera's x, y, z ?

Rotation should at least cover Roll/Yaw/Zoom

I'm using webGL/THREE but I believe this should be applicable to any language/engine

3

3 Answers

2
votes

I'd argue the simplest way is to use a matrix hierarchy.

Make a node at the origin. Make a child node where you want the camera. Rotate the node at the origin. Ask the child where it is and put the camera there. Use lookAt to orient the camera

This is pretty much ALWAYS easier than computing a circle. You can achieve all kinds of effects by creatively putting nodes in a scene, moving them around and using them set both the position and the target for a camera.

var canvas = document.querySelector("#c");

var camera = new THREE.PerspectiveCamera(70, 1, 1, 1000);
var scene = new THREE.Scene();

// make some cubes
var geometry = new THREE.BoxBufferGeometry(20, 20, 20);
var material = new THREE.MeshLambertMaterial({ color: 0xFFEECC });

for (var z = -1; z <= 1; ++z) {
  for (var x = -1; x <= 1; ++x) { 
    var mesh = new THREE.Mesh(geometry, material);
    mesh.position.x = x * 30;
    mesh.position.z = z * 30;
    scene.add(mesh);    
  }
}

// add some lights
var light = new THREE.DirectionalLight(0xE0E0FF, 1);
light.position.set(200, 500, 200);
scene.add(light);
var light = new THREE.DirectionalLight(0xFFE0E0, 0.95);
light.position.set(-200, -500, -200);
scene.add(light);


// now make nodes for camera
var cameraCenterNode = new THREE.Object3D();
var cameraPositionNode = new THREE.Object3D();
scene.add(cameraCenterNode);
cameraCenterNode.add(cameraPositionNode);
cameraPositionNode.position.x = 100;
cameraPositionNode.position.y = 40;

var renderer = new THREE.WebGLRenderer({canvas: canvas});

function resizeIfChanged() {
  var width = canvas.clientWidth;
  var height = canvas.clientHeight;
  if (canvas.width != width || canvas.height != height) {
    camera.aspect = width / height;
    camera.updateProjectionMatrix();

    renderer.setSize(width, height, false);
 }
}

function render(time) {
  time *= 0.001; // make it seconds
  
  resizeIfChanged();
  
  // Rotate camera center
  cameraCenterNode.rotation.y = time;
  
  // get cameraPositionNode's world position and put it in camera
  cameraPositionNode.getWorldPosition(camera.position);
  
  // point camera at center
  camera.lookAt(cameraCenterNode.position);

  renderer.render(scene, camera);

  requestAnimationFrame(render);
}
requestAnimationFrame(render);
body { margin: 0 }
canvas { width: 100vw; height: 100vh; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r78/three.min.js"></script>
<canvas id="c"></canvas>

As for zooming it really depends on what you want to achieve.

For example, you could compute a point between cameraCenterNode and cameraPositionNode. Example:

var out = cameraPositionNode.getWorldPosition();
var in  = cameraCenternode.getWorldPosition();
in.lerp(out, zoomAmount);  // where 0.0 = in and 1.0 = out

// now set the camera there
camera.position.copy(in);

Or can always just move the camera forward along its -zAxis

var worldMatrix = camera.getWorldMatrix();

var zoomVector = new THREE.Vector3(
   wm.elememts[8], wm.elememts[9], wm.elememts[10]);

camera.position.sub(zoomVector.multiplyScalar(zoomAmount));

It's not clear from your question how you really want the camera to work. If you always want it to point to the origin and always rotate around OR if you want it more free floating like a typical modeling package. Regardless though you have to pick some point to rotate around. Whether it's a fixed point or a new point depending on where the camera is the same type of calculations will work.

1
votes

If I understand you correctly, you just need to "rotate" camera position and set it to look at the origin of the scene:

camera.position.x = R * Math.cos(bearing) * Math.cos(pitch);
camera.position.y = R * Math.sin(bearing) * Math.cos(pitch);
camera.position.z = R * Math.sin(pitch);
camera.lookAt(new THREE.Vector3(0, 0, 0));

Here R is a distance from the camera to the origin, bearing is the rotation in the horizontal plane, pitch – in the vertical plane.

0
votes

The mathematics behind is a bit complicated.

Best to use http://threejs.org/examples/misc_controls_orbit.html

You can define max and min angles and setup autorotation as well.