• No results found

Moving Objects Around with a Model Matrix

Let’s add the following matrix definition to the top of the class:

AirHockey3D/src/com/airhockey/android/AirHockeyRenderer.java private final float[] modelMatrix = new float[16];

4. http://grepcode.com/file_/repository.grepcode.com/java/ext/com.google.android/android/4.0.4_r1.2/android/

opengl/Matrix.java/?v=source

Switching to a Projection Matrix

107

We’ll use this matrix to move the air hockey table into the distance. At the end of onSurfaceChanged(), add the following code:

AirHockey3D/src/com/airhockey/android/AirHockeyRenderer.java setIdentityM(modelMatrix, 0);

translateM(modelMatrix, 0, 0f, 0f, -2f);

This sets the model matrix to the identity matrix and then translates it by -2 along the z-axis. When we multiply our air hockey table coordinates with this matrix, they will end up getting moved by 2 units along the negative z-axis.

Multiplying Once Versus Multiplying Twice

We now have a choice: we still need to apply this matrix to each vertex, so our first option is to add the extra matrix to the vertex shader. We multiply each vertex by the model matrix to move it 2 units along the negative z-axis, and then we multiply each vertex by the projection matrix so that OpenGL can do the perspective divide and transform the vertices into normalized device coordinates.

Instead of going through all of this trouble, there’s a better way: we can multiply the model and projection matrices together into a single matrix and then pass this matrix into the vertex shader. That way we can stay with one matrix in the shader.

Matrix Multiplication

Matrix-matrix multiplication works much like matrix-vector multiplication.

For example, let’s say that we had two generic matrices, as follows:

To get the first element of the result, we multiply the first row of the first matrix by the first column of the second matrix and add together the results:

Chapter 6. Entering the Third Dimension

108

Then for the second element of the result, we multiply the second row of the first matrix by the first column of the second matrix and add together the results:

This continues for each subsequent element of the result matrix.

Order of Multiplication

Now that we know how to multiply two matrices together, we need to be careful to make sure that we multiply them in the right order. We can either multiply with the projection matrix on the left side and the model matrix on the right side, or with the model matrix on the left side and the projection matrix on the right side.

Unlike with regular multiplication, the order matters! If we get the order wrong, things might look weird or we might not see anything at all! The fol-lowing is an example of two matrices multiplied in one particular order:

Here are the same two matrices multiplied in the reverse order:

With a different order, the results are also different.

Selecting the Appropriate Order

To figure out which order we should use, let’s look at the math when we only use a projection matrix:

vertexclip = ProjectionMatrix * vertexeye

vertexeye represents the position of the vertex in our scene before multiplying it with a projection matrix. Once we add a model matrix to move the table, the math looks like this:

vertexeye = ModelMatrix * vertexmodel vertexclip = ProjectionMatrix * vertexeye

Switching to a Projection Matrix

109

vertexmodel represents the position of the vertex before we use the model matrix to push it out into the scene. Combine the two expressions, and we end up with this:

vertexclip = ProjectionMatrix * ModelMatrix * vertexmodel

To replace these two matrices with one, we have to multiply the projection matrix by the model matrix, with the projection matrix on the left side and the model matrix on the right side.

Updating the Code to Use One Matrix

Let’s wrap up the new matrix code and add the following to onSurfaceChanged() after the call to translateM():

AirHockey3D/src/com/airhockey/android/AirHockeyRenderer.java final float[] temp = new float[16];

multiplyMM(temp, 0, projectionMatrix, 0, modelMatrix, 0);

System.arraycopy(temp, 0, projectionMatrix, 0, temp.length);

Whenever we multiply two matrices, we need a temporary area to store the result. If we try to write the result directly, the results are undefined!

We first create a temporary floating-point array to store the temporary result;

then we call multiplyMM() to multiply the projection matrix and model matrix together into this temporary array. Next we call System.arraycopy() to store the result back into projectionMatrix, which now contains the combined effects of the model matrix and the projection matrix.

If we run the application now, it should look like Figure 32, Using a projection and a model matrix, on page 111. Pushing the air hockey table into the distance brought it into our frustum, but the table is still standing upright. After a quick recap, we’ll learn how to rotate the table so that we see it from an angle rather than upright.

A Quick Recap

Let’s take a quick recap of what we’ve just covered in the past few sections:

• We learned how to use an extra matrix to move the air hockey table into the screen before passing it through the projection matrix.

• We learned how to multiply two matrices together.

• We then learned how to combine the projection and model matrices so that we don’t have to modify the vertex shader to accept an additional matrix and multiply by both matrices every single time.

Chapter 6. Entering the Third Dimension

110

Figure 32—Using a projection and a model matrix

6.8 Adding Rotation

Now that we have a projection matrix configured and a model matrix in place to move our table around, all we need to do is rotate the table so that we’re looking at it from across an angle. We’ll be able to do this with one line of code using a rotation matrix. We haven’t worked with rotations yet, so let’s spend some time to learn more about how these rotations work.