Creating a 3D Camera

Explaining the Math of a 3D camera

A Bad Introduction To Trigonometry

In this guide, I will explain how to go about creating a modern 3D camera with Java and OpenGL(using the LWJGL library). In order to do that, I
will also explain a little bit of trigonometry and how the functions work, instead of just giving you code to use.

The trigonometric functions we will be using in this guide is sin(sine) and cos(cosine). There are other functions like tan(tanget), cot(cotanget), sec(secant), and csc(cosecant), but we won’t be needing them to write a basic camera.

The functions sin and cos can help us find angles in a right triangle, calculate polar - cartesian coordinates, and more. The phrase “SOH CAH TOA” will help you remember what sin and cos do.

Sin(a) = Opposite / Hypotenuse
Cos(a) = Adjacent / Hypotenuse
Tan(a) = Opposite / Adjacent

We will only focus on the first two. What this is saying is that the sine of an angle is the opposite side over the hypotenuse(the side opposite the RIGHT angle). The opposite is the side opposite the angle. It might be confusing but let’s focus on what we’re going to use it for.

You might have seen that sin and cos are used to convert polar coordinates into cartesian coordinates. Polar coordinates are defined by a length and an angle. Do you see how it works when you think of it as a triangle? Think of it as going from 0 to the x coordinate as the adjacent and the 0 to the y coordinate as the opposite side. Then the length of the line will be the hypotenuse. An example of converting a polar coordinate to a cartesian coordinate is like this:

Polar coordinate (45 degrees, 1.414 length)
X = cos(45) * 1.414; = 1
Y = cos(45) * 1.414; = 1

As you can see, the polar coordinate yields the cartesian coordinate (1, 1).

What I think the sin and cos functions do(I’ve never really received a general definition) is that they convert an angle measure to a normalized directional vector. Let’s apply this to how we would use it in a 3D camera.

Creating a 3D Camera

In a 3D camera, there are 2 components I can think of right away. Those are the pitch and yaw values and the position of the camera.

The pitch of a camera is the vertical orientation of the camera. The yaw of a camera is the horizontal orientation of the camera. You might’ve heard about roll, and that is the “Z-axis” orientation of the camera. The pitch rotates on the x axis, and the yaw rotates on the y-axis.
Basically in general, the pitch and yaw is how much you have rotated in a horizontal and vertical direction.

To incorporate pitch and yaw into a 3D game, you have to do something like this:


glRotatef(-pitch, -yaw, 0);

To receive pitch and yaw values, you have to keep the mouse in the center of the screen all the time. Whenever you detect there is a change, the amount of change in X will be added to yaw, and the amount of change in Y will be added to pitch. Once the yaw exceeds 359 degrees, reset
it to 0. Also make sure that you lock the pitch values so it can’t be less than -90 or greater than 90.

The next component is the position. This is described as a 3-dimensional coordinate being (x, y, z).

To incorporate position into a 3D game, do this


glTranslatef(-pos.x, -pos.y, -pos.z);

So what we need to solve is:

  • How do we represent movement in a camera?

If you have developed a 2D game before(which I’m sure you have), you can easily move the player in the x and y axis depending on what key you press. But since a 3rd dimension is being added, there is more to maintain. If your yaw is say, 90 degrees, and you press the forward button, you would originally be moving positively in the Z direction. That’s wrong. Since you’re yaw is 90, you should be moving in the positive X direction. The trigonometric functions sin and cos will help us solve this problem.

Relative to your current direction, forward is 0 degrees, backwards is 180 degrees, left is -90 or +270 degrees, right is +90 or -270 degrees.
If we incorporate this with our knowledge of sin and cos, we can figure out the correct direction to move in.

That being said, here is the function:


void moveCamera(float direction) { // direction is in degrees
	pos.x += Math.sin(Math.toRadians(yaw + direction)) * SPEED;
	pos.z += Math.cos(Math.toRadians(yaw + direction)) * SPEED;
}

As you can see, X is using the sin function, and Z is using the cos function. But why is this? I said earlier that cos is for X and sin is for Y, right? Well, you see, since we’re in the 3rd dimension now, Y is the UPWARD direction. If we think of XZ as a normal coordinating system as of XY (in a 2D plane), then it works.

Want some examples of this being used?

If yaw = 90 and move left:
sin(90 - 90) = sin(0) = 0
cos(90 - 90) = cos(0) = 1

It checks off.

If yaw = 180 and move backwards:
sin(180 + 180) = sin(360) = sin(0) = 0;
cos(180 + 180) = cos(260) = cos(0) = 1;

Same thing. :slight_smile:

If yaw = 270 and move backwards:
sin(270 + 180) = sin(450) = 1
cos(270 + 180) = cos(450) = 0

This tutorial was mostly intended for explaining the math behind a camera, and not how to implement. Should I explain how to implement cameras with LWJGL?

And any tips, comments, or suggestions on how to make this guide better? :slight_smile: Thanks for reading.

OpenGL works with degrees, but the trig functions on Math use radians.

You also introduced a shift of axes to Y=up, which may be true for several engines such as idTech and Unreal, but it’s not the intuition that applies to the camera out of the box (and what most tutorials will assume) which is the camera looking down Z

Fixed.

Its easier to visualize the upward direction being Y, well, because Y has always meant up and down to me. :slight_smile: Z has always meant “depth” to me, so it all makes sense.

Polar coordinate (45 degrees, 1.414 length)
X = cos(45) * 1.414; = 1
Y = cos(45) * 1.414; = 1

is it typo?

mb
Y = sin(45) * 1.414; =~1;

sin(45) = cos(45) = sqrt(2) / 2

Yeah, I think I should’ve said sqrt(2) sorry.

This might interest you a bit. Speaking of polar coords, why not use the 3d version of the polar coordinate system (spherical)?

Very, very old article I wrote:

http://www.phatcode.net/articles.php?id=216


camLookAT.x = SIN(Phi) * COS(Theta) 
camLookAT.z = SIN(Phi) * SIN(Theta)
camLookAT.y = COS(Phi)