Part I A Simple Game of Air Hockey 2. Defining Vertices and Shaders
4. Adding Color and Shade
4.6 Exercises 75 5. Adjusting to the Screen’s Aspect Ratio
See if you can add some color interpolation to the line cutting across the middle of the screen. For a more challenging exercise, how would you change the triangles that make up the air hockey table so that the edges are less visible? Hint: You can try adding more triangles to the fan.
Once you’ve completed these exercises, we’ll start to learn about vectors and matrices and learn how we can fix a pesky problem that appears when we rotate from portrait to landscape.
A Review • 75
CHAPTER 5
Adjusting to the Screen’s Aspect Ratio
You might not have noticed it yet, but we currently have an aspect ratio problem with our air hockey table. To see what’s happening, open the latest project from the previous chapter and go ahead and run the app on your device or in the emulator. Once it’s running, rotate your device from portrait to landscape (if using the emulator, press CTRL-F12).
The app should look like the next figure in portrait mode, and the subsequent figure in landscape mode:
Figure 20—Air hockey in portrait
Figure 21—Air hockey in landscape
Our table is squashed in landscape mode! The reason why this is happening is because we’ve been passing our coordinates into OpenGL directly, without compensating for the aspect ratio of the screen. Every 2D and 3D application shares one big problem: how does it decide what to display on the screen, and how do they adjust for the screen dimensions? This problem also has a common solution: in OpenGL, we can use a projection to map a part of our world onto the screen, and we can map it in such a way that it looks correct across different screen sizes and orientations. With the wide variety of devices out there, it’s important to be able to adjust to all of them.
In this chapter, we’re going to learn why our table appears squashed and how we can use a projection to fix the problem. Here’s our game plan:
• First we’ll review some basic linear algebra and learn how to multiply a matrix and a vector together.
• Then we’ll learn how to define and use a projection with a matrix, which will let us compensate for the screen’s orientation so that our table doesn’t appear squashed.
As in the last chapter, let’s start off by copying the project from the last chapter over into a new project. Let’s call this new project ‘AirHockeyOrtho’.
If you need a quick refresher, please follow the sequence in Section 2.2, Don't Start from Scratch, on page 21.
5.1 We Have an Aspect Ratio Problem
We’re now pretty familiar with the fact that everything we render in OpenGL gets mapped to a range of [-1, 1] on both the x- and y-axes; this is also true of the z-axis. Coordinates in this range are known as normalized device coor- dinates and are independent of the actual size or shape of the screen.
Chapter 5. Adjusting to the Screen’s Aspect Ratio • 78
Unfortunately, because they are independent of the actual screen dimensions, we can run into problems if we use them directly, such as a squashed table in landscape mode.
Let’s say that our actual device resolution is 1280 x 720 in pixels, which is a common resolution on new Android devices. Let’s also pretend for a moment that we’re using the whole display for OpenGL, as it will make this discussion easier.
If our device is in portrait mode, then [-1, 1] will range over 1280 pixels of height but only 720 pixels of width. Our image would appear flattened along the x-axis. The same problem happens along the y-axis if we’re in landscape mode.
Normalized device coordinates assume that the coordinate space is a square, as seen in the following image:
However, since the actual viewport might not be a square, the image will get stretched in one direction and squashed in the other. An image defined in normalized device coordinates would be squashed horizontally when seen on a portrait device:
The same image would be squashed the other way when in landscape mode:
We Have an Aspect Ratio Problem • 79
Adjusting to the Aspect Ratio
We need to adjust the coordinate space so that it takes the screen shape into account, and one way that we can do this is to keep the smaller range fixed to [-1, 1] and adjust the larger range in proportion to the screen dimensions.
For example, in portrait, the width is 720 while the height is 1280, so we can keep the width range at [-1, 1] and adjust the height range to [-1280/720, 1280/720] or [-1.78, 1.78]. We can also do the same thing in landscape mode, with the width range set to [-1.78, 1.78] and the height range set to [-1, 1].
By adjusting the coordinate space that we have, we will end up changing the space that we have available:
This way, objects will look the same in both portrait and landscape modes.