Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rotating a Camera to a Point

Tags:

java

math

opengl

I'm making a game that uses an entire planet for its map. I've tessellated the spherical planet using this technique, and am now adding in camera controls.

The sphere is of dimensions 1 to -1, so each point on the sphere is also a normalized vector. At any one time, one of the hexagonal tiles that make up the sphere is the 'selected' tile. The player can then move the selection to neighbouring tiles using the d-pad. They can also independently rotate the camera around using the analogue stick

I need to do two things with relation to the selected tile and the camera. Firstly, I need to be able to switch the selection to the tile that is nearest the camera. Secondly, I need to centre the camera on the highlighted tile

The sphere sits on the origin, and the camera sits at point (0,0,1). The graphics engine only allows me to rotate the camera around the X and Y axes, so to solve the first problem, I use quaternions to rotate the point (0,0,1) around the x and then y axes to find the point in 3D space where the camera is:

    private Quaternion quat = new Quaternion(0,0,0,0);
    private double[] output = new double[4];
    private double[] cam = new double[3];

    private double camX = 0;
    private double camY = 0;
    private double camZ = 1;


    private double[] getCamPosition(){

        quat.setAxisAngle(1, 0, 0, Math.toRadians(-graphicsEngine.getRotationX()));
        quat.RotateVector(camX, camY, camZ, output);            
        cam[0] = output[0];
        cam[1] = output[1];
        cam[2] = output[2];   

        quat.setAxisAngle(0, 1, 0, Math.toRadians(-graphicsEngine.getRotationY()));
        quat.RotateVector(cam[0], cam[1], cam[2], output);          
        cam[0] = output[0];
        cam[1] = output[1];
        cam[2] = output[2];

        return cam;
    }

I then compare the distances between the centroid of each tile and the camera position, and take the tile that is closest to be the newly selected tile.

However, to solve the second problem, I want to do the reverse. I want to take the centroid (which is already in the form of a normalized vector), and find out the rotation around the X and rotation around the Y needed to get the camera to centre on it

At the moment, I'm rotating the camera back to (0,0,1), then getting the angle in the X axis and Y axis between (0,0,1) and the centroid and using that to rotate the camera a second time:

private double[] outputArray = new double[2];

/**
 * Cam is always (0,0,1)
 */
public void centreOnSelected(double camX, double camY, double camZ){        

    selectedTile.getCentroidAngles(outputArray);

    outputArray[0] -= Math.atan2(camZ, camY);
    outputArray[1] -= Math.atan2(camX, camZ);

    // this determines if the centroid is pointing away from the camera
    // I.e. is on the far side of the sphere to the camera point (0,0,1)
    if(!selected.getCentroidDirectionY(camX, camZ)){
        outputArray[0] = -Math.PI - outputArray[0];         
    }

    graphicsEngine.rotateCam(Math.toDegrees(outputArray[0]), Math.toDegrees(outputArray[1]));


}

and in selected (the tile class)

void getCentroidAngles(double[] outputArray){
    outputArray[0] = Math.atan2(centroidZ, centroidY);
    outputArray[1] = Math.atan2(centroidX, centroidZ);

}

The problem is this doesn't work, (the x axis always seems to be out), and I'm pretty sure it is to do with the maths of getting the angles and doing the rotating

Note: the graphics engine rotates first around the X axis, then around the Y:

        gl.glRotatef(mRotateX, 1, 0, 0);
        gl.glRotatef(mRotateY, 0, 1, 0);

The centroids are all in the correct place, and the camera definitely rotates by the correct amounts, so I'm sure the problem is not with the graphics engine. It's also not the re-positioning of the camera back to (0,0,1) as I've checked this works by stepping through the program

I've also made a video to illustrate the problem:

http://www.youtube.com/watch?v=Uvka7ifZMlE

This has been bugging me for a number of days, so any help in getting this fixed would be very much appreciated!

Thanks James

like image 825
James Coote Avatar asked Feb 16 '13 15:02

James Coote


People also ask

What is rotation photography?

Camera rotation and kinetic photography Camera rotation is one type of kinetic photography which involves rotating your camera all the way around in a circular motion to create a photograph with interesting kaleidoscopic, geometric patterns.

What axis does a camera rotate on?

A camera movement that involves rotation will rotate about one of the current axes of the camera’s coordinate system. If a camera tilts, the rotation is about the u axis. If a camera pans, the rotation is about the v axis.

How do you rotate a camera in MATLAB?

The basic steps are: Calculate the camera’s u axis. Move the camera to the origin. This moves the eye point to the origin and it moves the center point to some location relative to the origin. You translate by (-eye_x, -eye_y, -eye_z). This is required because all rotation is about the origin.

How to move the camera to the center point?

The basic steps are: Calculate the camera’s u axis. Move the camera to the origin. This moves the eye point to the origin and it moves the center point to some location relative to the origin.

How to manipulate the position and orientation of a camera?

Manipulate the position and orientation of a camera. Push the tilt button below to "tilt" from the camera's current position and orientation. The left canvas shows the relative location of the scene objects and the camera. The right canvas shows the scene from the camera's vantage point.


1 Answers

Unfortunately I don't completely understand what's going on here, and can't offer a full solution, but can at least point out some problems to look at.

I've never used glRotatef, but I'm a bit confused about the order and sign of the transformations here - for example, are you really doing the x rotation first given the order of your glRotatef calls?

Anyway, at least part of the problem here is coming from these equations

outputArray[0] = Math.atan2(centroidZ, centroidY);
outputArray[1] = Math.atan2(centroidX, centroidZ);

Firstly, did you mean (centroidY, centroidZ) in the first one? More seriously, the angles you get here can't be used to build up a rotation carrying (0,0,1) onto your centroid or vice-versa. For example, suppose you want to rotate your centroid vector to (0,0,1). Each of your 2 rotations individually could be used to rotate around a single axis setting one component to zero. For example a rotation by outputArray[0] around the x axis of an appropriate sign would set the y component to zero (assuming the arguments to atan2 were swapped). Alternatively a rotation by outputArray[1] of the right sign about the y axis could set the x component to 0. But after doing the x rotation first (say) to set the y component to 0 the centroid vector changes - now the rotation around the y axis that will set the x component to 0 is no longer described by outputArray[1].

The proper formulae for these things always have an atan2 for one angle and an acos or asin for the other. For example if you want the angles for an active rotation that would carry the vector (1,0,0) onto (x,y,z) you would use

first_angle_around_x  = -asin(y)
second_angle_around_y = atan2(x, z)

To carry (x,y,z) onto (1,0,0) you would use

first_angle_around_x  = atan2(y, z)
second_angle_around_y = -asin(x)

(These angles describe rotations around the x,y axes that are counterclockwise when the axis is "poking you in the eye".)

Another problem is here

outputArray[0] -= Math.atan2(camZ, camY);
outputArray[1] -= Math.atan2(camX, camZ);

Subtracting angles like this only works for rotations around a single axis. Once you're building composite transformations out of rotations around different axes the relationship becomes much more complicated - which is why matrices and quaternions come in handy. I guess this code might not matter if the camX-Z inputs are redundant as suggested by the comment preceding the method (although at the moment having the Z and Y components the way round they are here will give a non-zero result that may be compensating for them being the "wrong" way round in the first equations I mentioned; I'm not sure if this is intentional).

There's also a fundamental issue with the way you're moving the camera to the selected tile, though it's a bit subjective whether it's actually a problem. Because you only do rotations around the x and y axes you have a unique camera orientation for each point on your planet. The problem is that there's no way to pick such an orientation continuously - to see this imagine a vector field on the planet where the vector at each point is a unit vector pointing along the screen x direction when the camera is above that point. This field cannot be continuous by the Hairy Ball Theorem. What this means in practice is that there'll be some point on the planet where making what seems like a small adjustment of the camera position will make the planet wheel around by up to 180 degrees on the screen. If you want to avoid this you can choose the orientation becased on the current camera position instead, to minimise the amount of rotation. This does mean, for example, that the planet can end up "rolled" on the screen if the camera moves in a closed loop which might not be desirable in your game. It also requires that your engine be able to do rotations on all 3 axes.

like image 161
Mike Dinsdale Avatar answered Oct 27 '22 10:10

Mike Dinsdale