OPENGL|ES 第二天,A Simple Game of Air Hockey

来源:互联网 发布:君智咨询 知乎 编辑:程序博客网 时间:2024/05/16 11:51

一.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

We define our vertex data using a sequential list of floating point numbers so that we can store positions with decimal points.Center Line and Two Mallets are also defined.


float[] tableVerticesWithTriangles = {// Triangle 10f, 0f,9f, 14f,0f, 14f,// Triangle 20f, 0f,9f, 0f,9f, 14f// Line 10f, 7f,9f, 7f,// Mallets4.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;}

This vertex shader will be called once for every single vertex that we’ve defined. When it’s called, it will receive the current vertex’s position in the a_Position attribute, which

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.