一.Defining the Structure of Our Air Hockey Table
The first step in that chain is to define the structure of our table in a form that OpenGL understands. In OpenGL, the structure of everything begins with a vertex.
1.Introducing Vertices.
A vertex is simply a point representing one corner of a geometric object, with various attributes associated with that point. The most important attribute is the position, which
represents where this vertex is located in space.
2.Defining Vertices in Code
In OpenGL, we can only draw points, lines, and triangles. We define our air hockey with two triangles.
Since we have two components per vertex, let’s first create a constant tocontain that fact.
private static final int POSITION_COMPONENT_COUNT = 2; //2 dimensions
float[] tableVerticesWithTriangles = {
// Triangle 1
0f, 0f,
9f, 14f,
0f, 14f,
// Triangle 2
0f, 0f,
9f, 0f,
9f, 14f
// Line 1
0f, 7f,
9f, 7f,
// Mallets
4.5f, 2f,
4.5f, 12f
};
二.Making the Data Accessible to OpenGL
The main problem is that the environment where our code runs and the environment where OpenGL runs don’t speak the same language. There are two main concepts
that we need to understand:
1) When we compile and run our Java code in the emulator or on a device, it doesn’t run directly on the hardware. Instead, it runs through a special environment known as
the Dalvik virtual machine. Code running in this virtual machine has no direct access to the native environment other than via special APIs.
2) The Dalvik virtual machine also uses garbage collection. This means that when the virtual machine detects that a variable, object, or some other piece of memory is no
longer being used, it will go ahead and release that memory so that it can be reused. It might also move things around so that it can use the space more efficiently. The
native environment does not work the same way, and it will not expect blocks of memory to be moved around and freed automatically.
1.Calling Native Code from Java
Copying Memory from Java’s Memory Heap to the Native Memory Heap
private static final int BYTES_PER_FLOAT = 4;
private final FloatBuffer vertexData;
We’ve added a constant, BYTES_PER_FLOAT, and a FloatBuffer. A float in Java has 32 bits of precision, while a byte has 8 bits of precision. This might seem like an
obvious point to make, but there are 4 bytes in every float. We’ll need to refer to that in many places down the road. The FloatBuffer will be used to store data in native
memory.
vertexData = ByteBuffer
.allocateDirect(tableVerticesWithTriangles.length * BYTES_PER_FLOAT)
.order(ByteOrder.nativeOrder())
.asFloatBuffer();
vertexData.put(tableVerticesWithTriangles);
First we allocated a block of native memory using ByteBuffer.allocateDirect(); this memory will not be managed by the garbage collector.
The next line tells the byte buffer that it should organize its bytes in native order.it is important that we use the same order as the platform. We do this by calling
order(ByteOrder.nativeOrder()).
Finally, we’d rather not deal with individual bytes directly.We want to work with floats, so we call asFloatBuffer() to get a FloatBuffer that reflects the underlying bytes.
We then copy data from Dalvik’s memory to native memory by calling vertexData.put(tableVerticesWithTriangles). The memory will be freed when the process gets
destroyed, so we don’t normally need to worry about that. If you end up writing code that creates a lot of ByteBuffers and does so over time, you may want to read up on
heap fragmentation and memory management techniques.
三.Introducing the OpenGL Pipeline
Before we can draw our hockey table to the screen, we need to send it through the OpenGL pipeline, and to do this we need to use small subroutines known as shaders.
shaders tell the graphics processing unit (GPU) how to draw our data. There are two types of shaders, and we need to define both of them before we can draw anything to
the screen.
1. A vertex shader generates the final position of each vertex and is run once per vertex. Once the final positions are known, OpenGL will take the visible set of vertices and
assemble them into points, lines, and triangles.
2. A fragment shader generates the final color of each fragment of a point, line, or triangle and is run once per fragment. A fragment is a small, rectangular area of a single
color, analogous to a pixel on a computer screen.
Once the final colors are generated, OpenGL will write them into a block of memory known as the frame buffer, and Android will then display this frame buffer on the
screen.
1.Creating Our First Vertex Shader
Create a new file:simple_vertex_shader.glsl,These shaders are defined using GLSL, OpenGL’s shading language.
attribute vec4 a_Position;
void main()
{
gl_Position = a_Position;
}
is defined to be a vec4. Remember that we talked about how a vertex can have several attributes, such as a color and a position? The attribute keyword is how we feed
these attributes into our shader.
We then define main(), the main entry point to the shader. All it does is copy the position that we’ve defined to the special output variable gl_Position. Ourshader must
write something to gl_Position. OpenGL will use the value stored in gl_Position as the final position for the current vertex and start assembling vertices into points, lines,
and triangles.
2.Creating our first fragment shader
We still need to create a subroutine for generating the final color of each fragment.Before we do that, let’s take some time to learn more about what a fragment is and how
one is generated.
Create a new file:simple_fragment_shader.glsl. And add the following code:
precision mediump float;
uniform vec4 u_Color;
void main()
{
gl_FragColor = u_Color;
}
The first line at the top of the file defines the default precision for all floating point data types in the fragment shader. We can choose between lowp, mediump, and highp.
To the vertex shaders, accuracy is more important , so it is defined defaultly highp.u_Color is also a four-component vector, and in the context of a color, its four
components correspond to red, green, blue, and alpha(for transparency).
We then define main(), the main entry point to the shader. It copies the color that we’ve defined in our uniform to the special output variable gl_FragColor. Our shader must
write something to gl_FragColor. OpenGL will use this color as the final color for the current fragment.
三.The OpenGL Color Model
OpenGL uses the additive RGB color model.