## Perspective Projection The Closer the Bigger

Until now we have always used an orthographic projection, meaning that no matter how far an object is from the near clipping plane, it will always have the same size on the screen. Our eyes show us a different picture of the world. The further away an object is, the smaller it appears to us. This is called perspective projection, and we've already talked about it a little in Chapter 4.

The difference between an orthographic projection and a perspective projection can be explained by the shape of the view frustum. In an orthographic projection, we have a box. In a perspective projection, we have a pyramid with a cut off top as the near clipping plane; the pyramid's base as the far clipping plane; and its sides as the left, right, top, and bottom clipping planes. Figure 10-3 shows a perspective view frustum through which we can view our scene.

Figure 10-3. A perspective view frustum confining our scene (left); the frustum viewed from above (right)

The perspective view frustum is define by four parameters:

■ The distance from the camera to the near clipping plane

■ The distance from the camera to the far clipping plane

■ The aspect ratio of the viewport, which is embedded in the near clipping plane given by viewport width divided by viewport height

■ The field of view, specifying how wide the view frustum is and therefore how much of the scene it shows

While we've talked about a camera, there's no such concept involved here yet. We just pretend there is a camera sitting fixed at the origin looking down the negative z-axis, as in Figure 10-3.

The near and far clipping plane distances are no strangers to us. We just need to set them up so that the complete scene is contained in the view frustum. The field of view is also easily understandable when looking at the right image in Figure 10-3.

The aspect ratio of the viewport is a little less intuitive. Why is it needed? It makes sure that our world doesn't get stretched in case the screen we render to has an aspect ratio that's not equal to 1.

Previously we used glOrthof() to specify the orthographic view frustum in the form of a projection matrix. For the perspective view frustum we could use a method called glFrustumf(). However, there's an easier way.

Traditionally OpenGL comes with a utility library called GLU. It contains a couple of helper functions for things like setting up projection matrices and implementing camera systems. That library is also available on Android in the form of a class called GLU. It features a few static methods we can invoke without needing a GLU instance. The method we are interested in is called gluPerspective():

GLU.gluPerspective(GL10 gl, float fieldOfView, float aspectRatio, float near, float far);

This method will multiply the currently active matrix (e.g., projection or model-view matrix) with a perspective projection matrix, similar to glOrthof(). The first parameter is an instance of GL10, usually the one we use for all other OpenGL ES-related business. The second parameter is the field of view given in angles, the third parameter is the aspect ratio of the viewport, and the last two parameters specify the distance of the near and far clipping plane from the camera position. Since we don't have a camera yet, those values are given relative to the origin of the world, forcing us to look down the negative z-axis, as shown in Figure 10-3. That's totally fine for our purposes at the moment; we will make sure that all the objects we render stay within this fixed and immovable view frustum. As long as we only use gluPerspective(), we can't change the position or orientation of our virtual camera. We'll always only see a portion of the world looking down the negative z-axis.

Let's modify the last example so it uses perspective projection. I just copied over all code from Vertices3Test to a new class called PerspectiveTest, and also renamed Vertices3Screen to PerspectiveScreen. The only thing we need to change is the present() method. Listing 10-3 shows the code.

Listing 10-3. Excerpt from PerspectiveTest.java: Perspective Projection

^Override public void present(float deltaTime) { GL10 gl = glGraphics.getGL(); gl.glClear(GL10. GL_COLOR_BUFFER_BIT); gl.glViewport(0, 0, glGraphics.getWidth(), gl.glMatrixMode(GL10.GL_PROJECTION); gl.glLoadIdentity(); GLU.gluPerspective(gl, 67, glGraphics.getWidth() / 0.1f, 10f); gl.glMatrixMode(GL10.GL_MODELVIEW); gl.glLoadIdentity(); vertices.bind();

vertices.draw(GL10.GL_TRIANGLES, 0, 6); vertices.unbind();

The only difference from the previous present() method is that we are now using GLU.gluPerspective() instead of glOrtho(). We use a field of view of 67 degrees, which is close to the average human field of view. By increasing or decreasing this value, you can see more or less to the left and right. The next thing we specify is the aspect ratio, which is just the screen's width divided by its height. Note that this will be a floatingpoint number, so we have to cast one of the values to a float before dividing. The final arguments are the near and far clipping plane distance. Given that our virtual camera is located at the origin looking down the negative z-axis, anything with a z-value smaller than -0.1 and bigger than -10 will be between the near and far clipping planes, and thus be potentially visible. Figure 10-4 shows the output of this example.

glGraphics.getHeight()); (float)glGraphics.getHeight(),

Figure 10-4. Perspective (mostly correct)

Now we are actually doing proper 3D graphics. As you can see, we still have a problem with the rendering order of our triangles. Let's fix that by using the almighty z-buffer.