3
votes

I have a little game where a ball can roll around a square level. I want the camera position to follow the ball, but always look at 0, 0, 0. Moving the camera x and z to the ball position gets me close, however, as you can see in the below animation (sorry for any motion sickness):

rotating level

When using camera.lookAt( new THREE.Vector3( 0, 0, 0 ) ), the camera rolls around its y axis (I think?) causing the illusion that the level is rotating.

What I want to achieve is moving the camera and looking at 0,0,0 without the camera roll. As in, when the camera moves, the bottom edge of the level should stay relatively parallel with the bottom edge of the screen. Is this possible / does it make sense? I'm having trouble visualizing it.

I created a CodePen demo that illustrates the problem. I've extracted the lookAt function into a standalone function so that I can modify the quaternion once its returned:

function lookAtVector( sourcePoint, destPoint ) {

And I basically rotate and position the camera by hand:

var cameraPosition = new THREE.Vector3(
    radius * Math.sin( Date.now() * speed ),
    radius * Math.cos( Date.now() * speed ),
    5
);

var lookAtQuaternion = lookAtVector(
    cameraPosition,
    new THREE.Vector3( 0, 0 ,0 )
);
camera.position.copy( cameraPosition );
camera.quaternion.copy( lookAtQuaternion );

How can I modify the camera quaternion to not roll this way, while still looking at 0,0,0?. I've tried:

  • I've tried setting the x, y, z, and w fields of the quaternion to 0, Math.PI / 2 etc to try to force no rotation, but the results are unexpected and I haven't found a combination of manual field setting that works.

  • I've tried manually setting the camera up vector (camera.up = new THREE.Vector3(0,0,1);) as in this question but the results are the same.

Edit: Basically I want to remove the camera roll like any traditional 3d software camera, and like Three's OrbitControls. The perspective from bottom left and bottom right edges should look like:

bottom left bottom right

Notice how the camera isn't rolled in these pictures.

2
You want the center of the view to be as it is right now. You want the ball to move around as it does right now. You want the ball to be "centered" horizontally as it is right now. Is that all correct? What would you like different?Amit
I want the bottom edge of the level (the yellow boxes) to stay parallel with the bottom edge of the screen, as in undo the camera roll that's occurring.Andy Ray
So you want to replace the roll with what? Pan? You'd lose the "always look at 0, 0, 0"Amit
I edited the questionAndy Ray
Sounds like you want to change the position of the camera, not the rotation.Shomz

2 Answers

2
votes

This was resolved by changing the up vector. Unfortunately the codepen didn't show the right example code (I'm using a slightly different axis system in my local game). But If I changed THREE.Object3D.DefaultUp to new THREE.Vector3( 0, 0, -1 ) then I get the desired results:

enter image description here

The full lookAt function with fixed up vector:

function lookAtVector( sourcePoint, destPoint ) {

    return new THREE.Quaternion().setFromRotationMatrix(
        new THREE.Matrix4()
            .lookAt( sourcePoint, destPoint, new THREE.Vector3( 0, 0, -1 ) )
    );

}
0
votes

You should only modify the position of the camera (panning), not the rotation. The position you want is the original Y and Z you have, and copy the X value from the ball (assuming that the left-right direction is the X axis).

A diagram:

|   |
|   |
|O  |
+---+

 C


|   |
|   |
| O |
+---+

  C


|   |
|   |
|  O|
+---+

   C

O - ball
C - camera

What I want to achieve is moving the camera and looking at 0,0,0 without the rotation

How would you do that? That means the camera can move only on the axis of its direction (Y).