More Transformations

Besides the glTranslatef() method, OpenGL ES also offers us two methods for transformations: glRotatef() and glScalef().


Here's the signature of glRotatef():

GL10.glRotatef(float angle, float axisX, float axisY, float axisZ);

The first parameter is the angle in degrees we want to rotate our vertices by. But what do the rest of the parameters mean?

When we rotate something, we rotate it around an axis. What is an axis? Well, we already know three axes: the x-axis, the y-axis, and the z-axis. We can express these three axes as so-called vectors. The positive x-axis would be described as (1,0,0), the positive y-axis would be (0,1,0) and the positive z-axis would be (0,0,1). As you can see, a vector actually encodes a direction, in our case in 3D space. Bob's direction is also a vector, but in 2D space. Vectors can also encode positions, like Bob's position in 2D space.

To define the axis around which we want to rotate the model of Bob, we need to go back to 3D space, actually. Figure 7-21 shows the model of Bob (with a texture applied for orientation) as defined in the previous code in 3D space.

Figure 7-21. Bob in 3D

Since we haven't defined z-coordinates for Bob's vertices, he is embedded in the x-y plane of our 3D space (which is actually the model space, remember?). If we want to rotate Bob, we can do it around any axis we can think of: the x-, y-, or z-axis, or even a totally crazy axis like (0.75,0.75,0.75). However, for our 2D graphics programming needs, it makes sense to rotate Bob in the x-y plane. Hence, we'll use the positive z-axis as our rotation axis, which can be defined as (0,0,1). The rotation will be counterclockwise around the z-axis. A call to glRotatef(), like this, would cause the vertices of Bob's model to be rotated as shown in Figure 7-22:

Figure 7-21. Bob in 3D

Figure 7-22. Bob, rotated around the z-axis by 45 degrees


We can also scale Bob's model with glScalef(), like this: glScalef(2, 0.5f, 1);

which, given Bob's original model pose, would result in the new orientation depicted in Figure 7-23.

Figure 7-23. Bob, scaled by a factor of 2 on the x-axis and a factor of 0.5 on the y-axis. Ouch.

Combining Transformations

Now, we also discussed that we can combine the effect of multiple matrices by multiplying them together to form a new matrix. All the methods—glTranslatef(), glScalef(), glRotatef(), and glOrthof()—actually do just that. They multiply the current active matrix by the temporary matrix they create internally based on the parameters we pass to them. So let's combine the rotation and scaling of Bob:

gl.glRotatef(45, 0, 0, 1); gl.glScalef(2, 0.5f, 1);

This would make Bob's model look like Figure 7-24 (remember, we are still in model space).

Figure 7-24. Bob, first scaled and then rotated (still not looking happy)

What would happen if we applied the transformations the other way around, like this:

Figure 7-25 gives you the answer.

Figure 7-25. Bob, first rotated, and then scaled

Wow, this is not the Bob we used to know. What happened here? If you look at the code snippets, you'd actually expect Figure 7-24 to look like Figure 7-25, and Figure 7-25 to look like Figure 7-24. In the first snippet we apply the rotation first, and then scale Bob, right?

Wrong. The way OpenGL ES multiplies matrices with each other dictates the order in which the transformations the matrices encode are applied to a model. The last matrix we multiply the currently active matrix with will be the first that gets applied to the

Figure 7-24. Bob, first scaled and then rotated (still not looking happy)

Figure 7-25. Bob, first rotated, and then scaled

vertices. So if we want to scale, rotate, and translate Bob, in that exact order, we have to call the methods like this:

glTranslatef(bobs[i].x, bobs[i].y, 0); glRotatef(45, 0, 0, 1); glScalef(2, 0.5f, 1);

If we changed the loop in our BobScreen.present() method to the following code:

gl.glMatrixMode(GL10.GL_MODELVIEW); for(int i = 0; i < NUM_BOBS; i++) { gl.glLoadIdentity();

gl.glTranslatef(bobs[i].x, bobs[i].y, 0); gl.glRotatef(45, 0, 0, 1); gl.glScalef(2, 0.5f, 0); bobModel.draw(GL10.GL_TRIANGLES, 0, 6);

the output would look like Figure 7-25.

the output would look like Figure 7-25.

Figure 7-26. A hundred Bobs, scaled, rotated, and translated (in that order) to their positions in world space

I always mixed up the order of these matrix operations when I started out with OpenGL on the desktop. To remember how to do it correctly, I eventually arrived at a mnemonic device called it the LASFIA principle: last specified, first applied. (Yeah, my mnemonics aren't all that great huh?)

The easiest way to get comfortable with model-view transformations is to use them heavily. I suggest you take the source file, modify the inner loop for some time, and observe the effects. Note that you can specify as many transformations as you want for rendering each model. Add more rotations, translations, and scaling. Go crazy.

With this last example we basically know everything we need to know about OpenGL ES to write 2D games. Or do we?

0 0

Post a comment