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, which was discussed 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, there is a box. In a perspective projection, there is 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–4 shows a perspective view frustum through which you can view your scene.

images

Figure 10–4. A perspective view frustum containing the scene (left); the frustum viewed from above (right).

The perspective view frustum is defined by four parameters:

  1. The distance from the camera to the near clipping plane
  2. The distance from the camera to the far clipping plane
  3. The aspect ratio of the viewport, which is embedded in the near clipping plane given by viewport width divided by viewport height
  4. 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. Just pretend that there is a camera sitting fixed at the origin looking down the negative z-axis, as shown in Figure 10–3.

The near and far clipping plane distances are no strangers to us. You 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–4.

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

Previously, you used glOrthof() to specify the orthographic view frustum in the form of a projection matrix. For the perspective view frustum, you 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 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 in which we are interested is called gluPerspective():

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

This method will multiply the currently active matrix (that is, projection or model-view matrix) with a perspective projection matrix, similar to glOrthof(). The first parameter is an instance of GL10, usually the one used 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 there is no 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–4. That's totally fine at the moment; you will make sure that all the objects you render stay within this fixed and immovable view frustum. As long as you only use gluPerspective(), you can't change the position or orientation of your virtual camera. You will always only see a portion of the world looking down the negative z-axis.

Let's modify the last example so that it uses perspective projection. You just copied over all code from Vertices3Test to a new class called PerspectiveTest, and you also renamed Vertices3Screen to PerspectiveScreen. The only thing you 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(), glGraphics.getHeight());
    gl.glMatrixMode(GL10.GL_PROJECTION);
    gl.glLoadIdentity();
    GLU.gluPerspective(gl, 67,
                       glGraphics.getWidth() / (float)glGraphics.getHeight(),
                       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 you are now using GLU.gluPerspective() instead of glOrtho(). The field of view is 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 specified is the aspect ratio, which is the screen's width divided by its height. Note that this will be a floating-point number, so you 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 the 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–5 shows the output of this example.

images

Figure 10–5. Perspective (mostly correct).

Now you are actually doing proper 3D graphics. As you can see, there is still a problem with the rendering order of our triangles. That can be fixed by using the almighty z-buffer.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset