Simple Camera System

In the last example we saw a hint of how we could implement a camera system in 3D. We used glTranslatef() to push down the complete world by 2 units on the y-axis. Since our camera is fixed to be at the origin, looking down the negative z-axis, this approach gives the impression that the camera itself was moved up by 2 units. All the objects are still defined with their y-coordinates set to zero.

It's like in the classic saying, "If the mountain will not come to the prophet, the prophet will go to the mountain." Instead of actually moving the camera, we move the world around. Say we want our camera to be at position (10,4,2). All we need to do is use glTranslatef() like this:

If we wanted our camera to be rotated around its y-axis by 45 degrees, we could do this:

We can also combine these two steps, just as we do for "normal" objects:

gl.glTranslatef(-10,-4,-2); gl.glRotatef(-45,0,1,0);

The secret is that we have to invert the arguments to the transformation methods. Let's think about it using the preceding example. We know that our "real" camera is doomed to sit at the origin of the world looking down the z-axis. By applying inverse camera transformations, we bring the world into the camera's fixed view. Using a virtual camera rotated around the y-axis by 45 degrees is the same as fixing the camera and rotating the world around the camera by -45 degrees. The same is true for translation. Our virtual camera could be placed at (10,4,2). But since our real camera is fixed at the origin of the world, we just need to translate all objects by the inverse of that position vector, which is (-10,-4,-2).

When we modify the following three lines of the last example's present() method:

gl.glMatrixMode(GL10.GL_MODELVIEW); gl.glLoadIdentity(); gl.glTranslatef(0, -2, 0);

with these four lines:

gl.glMatrixMode(GL10.GL_MODELVIEW); gl.glLoadIdentity(); gl.glTranslatef(0, -3, 0); gl.glRotatef(45, 1, 0, 0);

We get the output in Figure 10-16.

gl.glMatrixMode(GL10.GL_MODELVIEW); gl.glLoadIdentity(); gl.glTranslatef(0, -3, 0); gl.glRotatef(45, 1, 0, 0);

Figure 10-16. Looking down at our world from (0,3,0)

Conceptually, our camera is now located at (0,3,0), and looks down at our scene at a -45 degree angle (which is the same as rotating the camera by -45 degrees around the x-axis. Figure 10-17 shows the setup of our scene with the camera.

Figure 10-17. How the camera is positioned and oriented in the scene

We could actually specify a very simple camera with four attributes:

■ Its position in world space.

■ Its rotation around its x-axis (pitch). This is equivalent to tilting your head up and down.

■ Its rotation around its y-axis (yaw). This is equivalent to turning your head left and right.

■ Its rotation around its z-axis (roll). This is equivalent to tilting your head to the left and right.

Given these attributes we can use OpenGL ES methods to create a camera matrix. This is called an Euler rotation camera. Many first-person shooter games use this kind of camera to simulate the tilting of a head. Usually you'd leave out the roll and only apply the yaw and pitch . The order in which the rotations are applied is important. For a firstperson shooter, you'd first apply the pitch rotation and then the yaw rotation:

gl.glTranslatef(-cam.x,- cam.y,-cam.z); gl.glRotatef(cam.yaw, 0, 1, 0); gl.glRotatef(cam.pitch, 1, 0, 0);

Many games still use this very simplistic camera model. If we had included the roll rotation, we might observe an effect called gimbal lock. This effect will cancel out one of the rotations given a certain configuration.

NOTE: Explaining gimbal lock with text or even images is very difficult. Since we'll only use yaw and pitch, we don't have this problem. To get an idea of what gimbal lock actually is, I suggest looking it up on your favorite video site on the Web. We can't solve this problem with Euler rotations. The actual solution is mathematically complex and we won't go into that in this book.

A second approach to a very simple camera system is the use of the GLU.glLookAt() method.

GLU.gluLookAt(GL10 gl, float eyeX, float eyeY, float eyeZ, float centerX, float centerY, float centerZ, float upX, float upY, float upZ);

Like the GLU.gluPerspective() method, it will multiply the currently active matrix with a transformation matrix. In this case it's a camera matrix, which will transform the world:

■ gl is just the GL10 instance we use throughout our rendering.

■ eyex, eyey, and eyez specify the camera's position in the world.

■ centerx, centery, and centerz specify a point in the world that the camera looks at.

■ upX, upY, and upZ specify the so-called up vector. Think of it as an arrow coming out at the top of your skull pointing upward. Tilt your head to the left or right and the arrow will point in the same direction the top of your head does.

The up vector is usually set to (0,1,0) even if that's not entirely correct. The gluLookAt() method can renormalize this up vector in most cases. Figure 10-18 shows our scene with the camera at (3,3,0) looking at (0,0,-5), as well as its "real" up vector.

Figure 10-18. A camera at position (3,3,0), looking at (0,0,-3)

We can replace the code in the HierarchyScreen.present() method we changed before with the following code snippet:

gl.glMatrixMode(GL10.GL_M0DELVIEW); gl.glLoadIdentity();

This time I also commented out the call to sun.update(), so the hierarchy will look like in Figure 10-18. Figure 10-19 shows the result of using the camera.

Figure 10-19. The camera in action

This kind of camera is great when we want to follow a character or want better control over how we view our scene by only specifying the camera's position and look-at point. For now that's all we need to know about cameras. In the next chapter we'll write two simple classes for a first-person shooter-type camera and a look-at camera that can follow an object.

You should now know the basics of 3D graphics programming with OpenGL ES. You learned how to set up a perspective view frustum, how to specify 3D vertex positions, and what the z-buffer is. You also saw how the z-buffer can both be friend and foe, depending on whether we use it correctly. We created our first 3D object: a texture cube, which turned out to be really easy. Finally we talked a little bit more about matrices and transformations, and created a hierarchical and very simple camera system. You'll be happy to know that this was not even the tip of the iceberg. In the next chapter we'll revisit a couple of topics from Chapter 7 in the context of 3D graphics programming. We'll also introduce a few new tricks that will come in handy when we write our final game. I highly recommend playing around with the examples in this chapter. Create new shapes and go crazy with transformations and the camera systems.

Figure 10-19. The camera in action

0 0

Post a comment