OPENGL|ES第五天,Adjusting to the Screen’s Aspect Ratio

来源:互联网 发布:焦土抗战 知乎 编辑:程序博客网 时间:2024/04/26 04:00

We currently have an aspect ratio problem with our air hockey table. Our table is squashed in landscape mode!

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.


5.1 We Have an Aspect Ratio Problem

Coordinates in this range are known as normalized device coordinates and are independent of the actual size or shape of the screen. 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.


5.2 Working with a Virtual Coordinate Space

To adjust the coordinate space so that we can take screen orientation into account, we need to stop working directly in normalized device coordinates and start working in a

virtual coordinate space. We then need to find some way of converting coordinates from our virtual space back into normalized device coordinates so that OpenGL can render

them correctly.


5.3 Linear Algebra 101

1.Vector

A vector is a one-dimensional array of elements. In OpenGL, a position is usually a four-element vector, as is a color.

2.Matrices

A matrix is a two-dimensional array of elements. In OpenGL, we generally use matrices to project vectors using an orthographic or perspective projection, and we can also

use them to do rotations, translations, and scaling of an object.

3.Matrix-Vector Multiplication

The following is an example of a complete matrix-vector multiply:

4.The Identity Matrix

An identity matrix looks like the following:


5.Translations Using a Matrix

let’s look at a very simple type of matrix that gets used quite often in OpenGL: the translation matrix.With this type of matrix, we can move one of our objects along a 

distance that we specify. This matrix looks just like an identity matrix, with three additional elements specified on the right-hand side:

Let’s look at an example with a position of (2, 2), with a default z of 0 and a default w of 1. We want to translate the vector by 3 along the x-axis and 3 along the y-axis, so

we’ll put 3 for xtranslation and 3 for ytranslation. Here’s the result:

=

The position is now at (5, 5), which is what we expected.

We’ve learned just enough about vector and matrix math to get us on our feet; let’s go ahead and learn how to define an orthographic projection.

5.4 Defining an Orthographic Projection

To define an orthographic projection, we’ll use Android’s Matrix class, which resides in the android.opengl package. In that class there’s a method called orthoM(), which

will generate an orthographic projection for us. We’ll use that projection to adjust the coordinate space,and as we’ll soon see, an orthographic projection is very similar to

a translation matrix.

method : orthoM(float[] m, int mOffset, float left, float right, float bottom, float top, float near, float far). When we call this method, it should produce the following orthographic

projection matrix:


5.5 Adding an Orthographic Projection

1.Updating the Shader

The first thing we need to do is update the shader so that it uses our matrix to transform our positions.

//AirHockeyOrtho/res/raw/simple_vertex_shader.glsluniform mat4 u_Matrix;attribute vec4 a_Position;attribute vec4 a_Color;varying vec4 v_Color;void main(){v_Color = a_Color;gl_Position = u_Matrix * a_Position;gl_PointSize = 10.0;}
We’ve added a new uniform definition, u_Matrix, and we’ve defined it as a mat4, meaning that this uniform will represent a 4 x 4 matrix. We’ve also updated the line that

assigns the position as follows: gl_Position = u_Matrix * a_Position;

The matrix will transform the coordinates from this virtual coordinate space back into normalized device coordinates. 

2.Adding the Matrix Array and a New Uniform

//AirHockeyOrtho/src/com/airhockey/android/AirHockeyRenderer.javaprivate static final String U_MATRIX = "u_Matrix";

This holds the name of the new uniform that we defined in our vertex shader.

We’ll also need a floating point array to store the matrix:

//AirHockeyOrtho/src/com/airhockey/android/AirHockeyRenderer.javaprivate final float[] projectionMatrix = new float[16];

We’ll also need an integer to hold the location of the matrix uniform:

//AirHockeyOrtho/src/com/airhockey/android/AirHockeyRenderer.javaprivate int uMatrixLocation;

Then we just need to add the following to onSurfaceCreated():

//AirHockeyOrtho/src/com/airhockey/android/AirHockeyRenderer.javauMatrixLocation = glGetUniformLocation(program, U_MATRIX);

3.Creating the Orthographic Projection Matrix

The next step will be to update onSurfaceChanged(). Add the following lines after the call to glViewport():

//AirHockeyOrtho/src/com/airhockey/android/AirHockeyRenderer.javafinal float aspectRatio = width > height ?(float) width / (float) height :(float) height / (float) width;if (width > height) {// LandscapeorthoM(projectionMatrix, 0, -aspectRatio, aspectRatio, -1f, 1f, -1f, 1f);} else {// Portrait or squareorthoM(projectionMatrix, 0, -1f, 1f, -aspectRatio, aspectRatio, -1f, 1f);}

This code will create an orthographic projection matrix that will take the screen’s current orientation into account. It will set up a virtual coordinate space the way we

describedThere’s more than one Matrix class in Android, so you’ll want to make sure that you’re importing android.opengl.Matrix.First we calculate the aspect ratio by

taking the greater of the width and height and dividing it by the smaller of the width and height. This value will be the same regardless of whether we’re in portrait or

landscape.

We then call orthoM(float[] m, int mOffset, float left, float right, float bottom, float top, float near, float far). If we’re in landscape mode, we’ll expand the coordinate space of

the width so that instead of ranging from -1 to 1, the width will range from-aspectRatio to aspectRatio. The height will stay from -1 to 1. If we’re in portrait 

mode, we expand the height instead and keep the width at -1 to 1.

4.Sending the Matrix to the Shader

The last change to do in AirHockeyRenderer is to send the orthographic projection
matrix to the shader. We do that by adding the following line of code to
onDrawFrame(), just after the call to glClear():

//AirHockeyOrtho/src/com/airhockey/android/AirHockeyRenderer.javaglUniformMatrix4fv(uMatrixLocation, 1, false, projectionMatrix, 0);