Search code examples
javascriptthree.js3drenderingquaternions

Force camera to be parallel to ground


I want my fly camera to always be parallel to the ground. In other words I strictly want my camera's right vector to be always (1,0,0).

I created a custom Camera Controller in three.js to simulate some custom camera movements and pointerlock behaviour.

Here, is the main rotation code in update method.

this.tmpQuaternion: THREE.Quaternion;
this.camera: THREE.PerspectiveCamera;
...
this.tmpQuaternion
 .set(
   -this.verticalRotationSpeed * this.mouseMovement.movementY,
   -this.horizontalRotationSpeed * this.mouseMovement.movementX,
   0,
   1
 )
 .normalize();
this.camera.quaternion.multiply(this.tmpQuaternion);

Here, verticalRotationSpeed and horizontalRotationSpeed are just positive constants, and mouseMovement.movementX and mouseMovement.movementY are just the accumulated mouse movement values after the last rendered frame.

Now, when I move my mouse on both the X and Y axis simultaneouly or in other others diagonally, the camera rolls a bit on the z axis, which I dont want at all.

Can, you kindly explain me the cause behind this behaviour, or at least provide a corrected version of my code, which would keep the camera's right vector to be always facing (1,0,0).

Remark: I want a threejs specific answer. Or in other words what can be achieved with the three.js library.

Thank you in advance.


Solution

  • WestLangley comment solved my problem.

    Ref: https://stackoverflow.com/a/67856474/10638802

    Posting a fresh answer for future viewers.

    this.upVector: THREE.Vector3 = new THREE.Vector3(0.0, 1.0, 0.0);
    this.camera: THREE.PerspectiveCamera;
    ...
        this.camera.rotateX(
          -this.verticalRotationSpeed * this.mouseMovement.movementY
        );
    
        this.camera.rotateOnWorldAxis(
          upVector,
          -this.horizontalRotationSpeed * this.mouseMovement.movementX
        );
    

    So, for rotation around the x-axis we rotate around the object's own x-axis using rotateX().

    But, to maintain the camera horizontal parallel to the ground, we rotate the camera around the world up(+Y) axis, using rotateOnWorldAxis().

    If, we had rotated the camera vertically around the object's own up(+y) axis, we would end up with unnecessary rotations on the z and x axis too, as the object's coordinate system might not align with the world coordinate system (The final scene is rendered around the world coordinate system).

    Rotating the camera arount the world vertical(y+) axis ensures that the camera right vector is also parallel to the ground, or in other words the camera stays parallel to the ground while rotating vertically.