Three.js - How to Pivot Camera around a Vector3 point?
Asked Answered
P

2

6

Ok, I will try and be as concise as possible. I am not very good with math and what might seem obvious to you will most likely be rocket science to me.

I am using Three.js w/CSS3DRenderer to create a virtual gallery space.

I need a first person perspective, like an FPS game.

I have gotten the camera to move forwards, backwards, left and right all successfully.

However, when I go to rotate the camera, I get the result pictured here

The camera is rotating its local axis, but what I need is for the viewport, not the camera, to rotate, like

pictured here

So what I need is for the camera to orbit around a pivot point/vector, and then refocus by using Object3d.lookAt()

I am aware that I can solve my problem by parenting the camera as a child of another object, and then rotating the axis of the object. However, I would rather do the math myself.

In short, I want to understand how to rotate one vector point around another, and how to represent that relationship mathematically.

I do not want to utilize a script, e.g., three.js pointer_lock controls, to get the job done. I want to get my hands dirty with the actual math.

Any advice or links to tutorials would be much appreciated!!!

Plattdeutsch answered 22/5, 2016 at 3:30 Comment(5)
When you move the camera left and right, do you rotate the camera (lookAt) to keep pointing to the same point? Do you want to circle the camera around this point?Sundberg
When I look left/right I want to have the camera's vector3 position incrementally orbit around a vector3 pivot point. I will call Object3d.lookAt() after each increment around the pivot point. Otherwise my camera would orbit the pivot, but remain facing the same direction throughout, which wouldn't simulate the FPS perspective I am hoping to achieve. Also I should mention that whenever the camera moves forward, backward, left, or right, my pivot point will move the same distance forward, backward, left or right. So the pivot point is dynamic and not a fixed coordinate in the world spacePlattdeutsch
what you need to learn is polar coordinate systemEnterprise
If the pivot point is dynamic, then it's not a pivot point. A pivot is fixed. If you move the camera 10 units to the right, how does the pivot point move? Is a target moving? What (if anything) is fixed?Sundberg
The pivot point is only fixed in as much as the functions that handle the forward, backward, left and right movement of the camera also move the pivot point, and these functions are seperate from the functions handling rotation/turning left or right. Perhaps it is not fixed, in the sense that it (the pivot) never moves, but it is fixed in the sense that it always remains the exact same distance from my camera. If my camera moves two units of distance left, then so does the pivot pointPlattdeutsch
G
9

Below is an example of a camera being rotated around a box.

It works by the fact that applying a rotation matrix always rotates an object around the origin. This is in contrast to just modifying the rotation property, which rotates the object around its own centre as you found out. The trick here is moving the camera 200 units away from the origin, such that when we apply the rotation matrix, it then rotates around the origin in a circle.

Since the box is at the origin, this will rotate the camera around the box. Then lookAt is used to point the camera towards the box.

Here is a low-level explanation on the subject if you're looking to learn more: http://webglfundamentals.org/webgl/lessons/webgl-scene-graph.html

var canvas = document.getElementById('canvas');
var scene = new THREE.Scene();
var renderer = new THREE.WebGLRenderer({canvas: canvas, antialias: true});
var camera = new THREE.PerspectiveCamera(45, canvas.clientWidth / canvas.clientWidth, 1, 1000);

var geometry = new THREE.BoxGeometry(50, 50, 50);
var material = new THREE.MeshBasicMaterial({color: '#f00'});
var box = new THREE.Mesh(geometry, material);
scene.add(box);

// important, otherwise the camera will spin on the spot.
camera.position.z = 200;

var period = 5; // rotation time in seconds
var clock = new THREE.Clock();
var matrix = new THREE.Matrix4(); // Pre-allocate empty matrix for performance. Don't want to make one of these every frame.
render();

function render() {
  requestAnimationFrame(render);
  if (canvas.width !== canvas.clientWidth || canvas.height !== canvas.clientHeight) {
    // This stuff in here is just for auto-resizing.
    renderer.setSize(canvas.clientWidth, canvas.clientHeight, false);
    camera.aspect = canvas.clientWidth /  canvas.clientHeight;
    camera.updateProjectionMatrix();
  }

  // Create a generic rotation matrix that will rotate an object
  // The math here just makes it rotate every 'period' seconds.
  matrix.makeRotationY(clock.getDelta() * 2 * Math.PI / period);

  // Apply matrix like this to rotate the camera.
  camera.position.applyMatrix4(matrix);

  // Make camera look at the box.
  camera.lookAt(box.position);

  // Render.
  renderer.render(scene, camera);
}
html, body, #canvas {
  margin: 0;
  padding: 0;
  width: 100%;
  height: 100%;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r77/three.js"></script>
<canvas id="canvas"></canvas>
Glimmering answered 22/5, 2016 at 12:54 Comment(3)
Thank you!! this looks useful. Tell me, does the makeRotationY() Matrix4 method always rotate objects in relation to origin/worldspace? There is no way you can apply this specific method to change the objects local axis?Plattdeutsch
A rotation matrix will always rotate around the origin. However, there is a trick to rotate things around another point in space. 1. Translate the object, such that the point you want to rotate around is at the origin. 2. Then apply the rotation matrix. 3. Undo the translation in step 1. This will make it look like you just rotated around a specific point instead of the origin. This is called a "change of basis".Glimmering
@Plattdeutsch No it isn't , because this solution is basically bad idea. Why, i explained in my answer bellow.Demetra
D
3

Standard method is to create var camera_pivot = new object3D() to the same location as the object. Put camera as a child, move camera back to make observable distance and rotate the object.

You can move object3D() and rotate by your needs. It is optimal solution for easy code and for performance.

On object3D you can use camera_pivot.translateX(), camera_pivot.rotateX(), camera_pivot.lookAt(obj) etc...

Other-way is difficult to do any actions without affect on another object in the scene.

Demetra answered 23/5, 2016 at 12:47 Comment(4)
-1. OP specifically said "I am aware that I can solve my problem by parenting the camera as a child of another object, and then rotating the axis of the object. However, I would rather do the math myself."Glimmering
Camera is always child of another object. If you have defined individual object only for a camera, you can use operations with this object like with camera. It is best practice and usual solution of your problem. THREE JS is designed for working with objects more likely than to use own calculations in render. For example is not possible to change camera animation on demand, using TWEEN etc...Demetra
+1. This is currently the third result from googling "three.js pivot camera". The OP might care about the math but I don't - I just want a clean and practical solution.Meridethmeridian
I like both solutions. It's good to have more lowlevel math in it.Rollick

© 2022 - 2024 — McMap. All rights reserved.