An overview of the
This is a new series, learn OpengGl Es, actually is “OpenGl Es Application Development Practice Guide Android volume” study notes, interested can directly read this book, of course, this will record their own understanding, the following only as notes, in case you forget later
The first nine chapters of the book will then be analyzed in turn
Android OpenGl Es learning (a) : create an OpenGl Es program
Android OpenGl Es learning (2) : define vertices and shaders
Android OpenGl Es learning (3) : build shaders
Android OpenGl Es learning (4) : add color
Android OpenGl Es learning (5) : adjust the aspect ratio
Android OpenGl Es Learning (vi) : Enter 3d
Android OpenGl Es learning (7) : Using textures
Android OpenGl Es Learning (8) : Build simple objects
Add touch feedback to Android OpenGl Es
The end result is a simple game of hockey, something like this
Load shader
Remember from the previous article that we wrote vertex shaders and fragment shaders, now we load them into the code and write the code
public class ReadResouceText {
public static String readResoucetText(Context context, int resouceId) {
StringBuffer body = new StringBuffer();
try {
InputStream inputStream = context.getResources().openRawResource(resouceId);
InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
String nextline;
while((nextline = bufferedReader.readLine()) ! =null) {
body.append(nextline);
body.append("\n"); }}catch (IOException e) {
e.printStackTrace();
}
returnbody.toString(); }}Copy the code
We write a new class for GLSL code in raw at home, and then load the shader code through this class
// Read the shader source code
String fragment_shader_source = ReadResouceText.readResoucetText(mContext, R.raw.fragment_shader);
String vertex_shader_source = ReadResouceText.readResoucetText(mContext, R.raw.vertex_shader);
Copy the code
Compiler shader
Let’s start by creating a method that compiles the shader
public static int compileShader(int type, String source) {
/ / create a shader
int shaderId = GLES20.glCreateShader(type);
if (shaderId == 0) {
Log.d("mmm"."Failed to create shader");
return 0;
}
// Upload shader source code
GLES20.glShaderSource(shaderId, source);
// Compile the shader source code
GLES20.glCompileShader(shaderId);
// Retrieve the compiled result
int[] compileStatus = new int[1];
// fetch shaderId's compileStatus and write it to index 0 of compileStatus
GLES20.glGetShaderiv(shaderId, GLES20.GL_COMPILE_STATUS, compileStatus, 0);
Log.d("MMM compile status", GLES20.glGetShaderInfoLog(shaderId));
if (compileStatus[0] = =0) {
GLES20.glDeleteShader(shaderId);
Log.d("mmm"."Failed to create shader");
return 0;
}
return shaderId;
}
Copy the code
call
// Compile the shader source code
int mVertexshader = ShaderHelper.compileShader(GLES20.GL_VERTEX_SHADER, vertex_shader_source);
int mFragmentshader = ShaderHelper.compileShader(GLES20.GL_FRAGMENT_SHADER, fragment_shader_source);
Copy the code
methods | describe |
---|---|
GLES20.glCreateShader(type) | Gles20.gl_fragment_shader fragment shader and gles20.gl_vertex_shader vertex shader |
GLES20.glShaderSource(shaderId, source) | Upload the source code for the Shader and associate it with an existing ShaderID |
GLES20.glCompileShader(shaderId); | Compile shader, need to upload the source code before compiling |
GLES20.glGetShaderiv(shaderId, GLES20.GL_COMPILE_STATUS, compileStatus, 0) | Fetch shaderId’s compileStatus and write it to compileStatus 0. If 0 is 0 then compilation failed |
GLES20.glDeleteShader(shaderId) | Delete shader |
GLES20.glGetShaderInfoLog(shaderId) | Gets a readable message, and if it has useful content for the shader, stores it in the shader’s information log |
Link shaders to programs
Opengl programs simply link a vertex shader and a fragment shader together to program a single object. Vertex shaders and fragment shaders always work together and cannot be separated, but that doesn’t mean they are paired one by one. We can also use the same shader in multiple programs
public static int linkProgram(int mVertexshader, int mFragmentshader) {
// Create a program object
int programId = GLES20.glCreateProgram();
if (programId == 0) {
Log.d("mmm".Failed to create program);
return 0;
}
// Attach the shader
GLES20.glAttachShader(programId, mVertexshader);
GLES20.glAttachShader(programId, mFragmentshader);
// Link program
GLES20.glLinkProgram(programId);
// Check the link status
int[] linkStatus = new int[1];
GLES20.glGetProgramiv(programId, GLES20.GL_LINK_STATUS, linkStatus, 0);
Log.d("mmm"."Link program" + GLES20.glGetProgramInfoLog(programId));
if (linkStatus[0] = =0) {
GLES20.glDeleteProgram(programId);
Log.d("mmm"."Link program failed");
return 0;
}
return programId;
}
Copy the code
call
int program = ShaderHelper.linkProgram(mVertexshader, mFragmentshader);
Copy the code
methods | describe |
---|---|
GLES20.glCreateProgram() | Creates a program object. If the return value is 0, the creation fails |
GLES20.glAttachShader(programId, shader) | Attach shaders and add vertex shaders and fragment shaders in turn to the program |
GLES20.glLinkProgram(programId); | Link program, link shader and program |
GLES20.glGetProgramiv(programId, GLES20.GL_LINK_STATUS, linkStatus, 0) | Check the program status, the result will be stored in the linkStatus array 0 index position, if 0, the link program failed |
GLES20.glDeleteProgram(programId) | Remove programs |
Validate the OpenL program object
We should verify before we use OpengL that our current program works with OpengL
public static boolean volidateProgram(int program) {
GLES20.glValidateProgram(program);
int[] validateStatus = new int[1];
GLES20.glGetProgramiv(program, GLES20.GL_VALIDATE_STATUS, validateStatus, 0);
Log.d("mmm"."Current OpenL status" + validateStatus[0] + "/" + GLES20.glGetProgramInfoLog(program));
return validateStatus[0] != 0;
}
Copy the code
call
// Validate opengL objects
ShaderHelper.volidateProgram(program);
// Use the program
GLES20.glUseProgram(program);
Copy the code
methods | describe |
---|---|
GLES20.glValidateProgram(program) | The validator |
GLES20.glGetProgramiv(program, GLES20.GL_VALIDATE_STATUS, validateStatus, 0) | Obtaining validation results |
GLES20.glGetProgramInfoLog(program) | If OpengL prints something, it’s going to display it here |
LES20.glUseProgram(program) | Tell OpengL to draw anything to the screen using the program defined here |
Get a UNIFORM location
Remember the fragment shader we defined in the last article
precision mediump float;
uniform vec4 u_Color;
void main() {
gl_FragColor = u_Color;
}
Copy the code
We define a UNIFORM variable in the fragment shader. When OpengL links the shader into a program, it actually uses a uniform position number to associate each uniform defined in the fragment color finder. These uniform positions are used by the shader to send data. And we need the position of u_Color so that we can draw the color when we want to draw
In the above shader, we defined a uniform of u_Color, and in the main method we assigned that uniform to gl_FragColor. We will use that uniform to set the color to be drawn. Now we get the uniform location in our Java code
int u_color = GLES20.glGetUniformLocation(program, "u_Color");
Copy the code
methods | describe |
---|---|
GLES20.glGetUniformLocation(program, “u_Color”) | Gets the location of the specified UNIFORM and saves it in the return value u_color variable for later use |
Gets the location of the property
Like Uniform, we need to get the location of properties before using them, so we can have OpengL automatically assign location numbers to properties to make it easier to manage
Once the shaders are linked together, we simply add the following code to get the location of the property
int a_position = GLES20.glGetAttribLocation(program, "a_Position");
Copy the code
methods | describe |
---|---|
GLES20.glGetAttribLocation(program, “a_Position”) | Gets the location of the property |
Associated properties and vertex data
// Bind a_position and verticeData vertex positions
/ * * * the first parameter, the second parameter is the shader properties * and how much weight each vertex, we have only to the component * the third parameter, the fourth parameter data types * and is only meaningful plastic, ignore * 5 parameters, an array has multiple attributes to be meaningful, we only have an attribute, Pass 0 * sixth argument, where does OpengL read from */
verticeData.position(0);
GLES20.glVertexAttribPointer(a_position, 2, GLES20.GL_FLOAT,
false.0, verticeData);
// Use vertices
GLES20.glEnableVertexAttribArray(a_position);
Copy the code
Each buffer has an internal pointer that can be moved by calling position(int). When OpengL reads data, it reads data from there. To ensure that data is read from scratch, We call verticeData.position(0) to move the data to the beginning
. Then we call GLES20 glVertexAttribPointer tell opengl () method, he can find a_Position corresponding data in the buffer verticeData, we look at the specific parameter meaning of this method
glVertexAttribPointer( int indx, int size, int type, boolean normalized, int stride, java.nio.Buffer ptr )
parameter | describe |
---|---|
int indx | This is the position of the property, passed in the a_position we got earlier |
int size | This is the data of every attribute count, how many weight for this property are associated with each vertex, in the previous section we define the vertices with the two component x, y, this means that each vertex need two component, we as the vertex set up two components, but a_Position defined as vec4, he has four components, without a specified value, So by default the third component is 0 and the fourth component is 1 |
int type | This is the data type, we’re floating point so glES20.gl_float |
boolean normalized | It only makes sense if you use plastic data, so we’ll ignore false for now |
int stride | It only makes sense if the array stores multiple properties, and in this chapter there is only one property, so ignore passing zeros |
java.nio.Buffer ptr | Tell OpengL where to read the data, |
Finally in the calling GLES20. GlEnableVertexAttribArray (a_position) use this attribute vertices, opengl knew where to find such data
methods | describe |
---|---|
GLES20.glEnableVertexAttribArray(a_position) | Using vertex |
Draw on the screen
// Specifies that the color of the shader u_color is white
GLES20.glUniform4f(u_color, 1.0 f.1.0 f.1.0 f.1.0 f);
/** * First argument: draw draw triangle * second argument: read from vertices array 0 * third argument: read 6 vertices ** Finally draw two triangles to form a rectangle */
GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0.6);
// Draw the dividing line
GLES20.glUniform4f(u_color, 1.0 f.0.0 f.0.0 f.1.0 f);
GLES20.glDrawArrays(GLES20.GL_LINES, 6.2);
/ / draw point
GLES20.glUniform4f(u_color, 0.0 f.0.0 f.1.0 f.1.0 f);
GLES20.glDrawArrays(GLES20.GL_POINTS, 8.1);
GLES20.glUniform4f(u_color, 1.0 f.0.0 f.0.0 f.1.0 f);
GLES20.glDrawArrays(GLES20.GL_POINTS, 9.1);
Copy the code
methods | describe |
---|---|
Gles20. glUniform4f(u_color, 1.0f, 0.0f, 0.0f, 1.0f) | Update the value of the shader u_color to red, green, blue, and transparency |
GLES20.glDrawArrays(int mode,int first,int count) | Using gles20.gl_triangles, gles20.gl_lines, and gles20.gl_points, there are three types of triangles: gles20.gl_points, triangle gles20.gl_lines, gles20.gl_points, triangle gles20.gl_points, triangle GLes20.gl_lines, and triangle gles20.gl_points |
Gles20.gluniform4f first updates the u_color values in the shader code with gles20.glUniform4f. Unlike properties, uniform components do not have default values, so if a UNIFORM defines vec4 type in a shader, we need to provide the values of all four components. Then call gles20.gldrawarrays (Gles20.gl_Triangles, 0, 6); The first parameter is to draw the triangle, the second parameter is to start with index 0 of the array, the third parameter is to draw the number of triangles to form a rectangle, and then draw the line and point
Map OpengL coordinates to the screen
So let’s look at the coordinates that we defined earlier
Opengl maps the screen to a range of [-1, 1], as shown:
We’ll talk about that later, but now let’s map the coordinates, okay
Here are the mapped coordinates
// Draw the triangle counterclockwise
float[] tableVertices = {
// First triangle
-0.5 f, -0.5 f.0.5 f.0.5 f,
-0.5 f.0.5 f.// Second triangle
-0.5 f, -0.5 f.0.5 f, -0.5 f.0.5 f.0.5 f./ / line
-0.5 f.0f.0.5 f.0f./ / points
0f, -0.25 f.0f.0.25 f
};
Copy the code
And then finally we can run the application
This article is done
The complete code
public class AirHockKeyRender implements GLSurfaceView.Renderer {
private final FloatBuffer verticeData;
private final int BYTES_PER_FLOAT = 4;
private final Context mContext;
// Draw the triangle counterclockwise
float[] tableVertices = {
// First triangle
-0.5 f, -0.5 f.0.5 f.0.5 f,
-0.5 f.0.5 f.// Second triangle
-0.5 f, -0.5 f.0.5 f, -0.5 f.0.5 f.0.5 f./ / line
-0.5 f.0f.0.5 f.0f./ / points
0f, -0.25 f.0f.0.25 f
};
private int u_color;
private int a_position;
private int POSITION_COMPONENT_COUNT = 2;
public AirHockKeyRender(Context context) {
this.mContext=context;
// Load float into local memory
verticeData = ByteBuffer.allocateDirect(tableVertices.length * BYTES_PER_FLOAT)
.order(ByteOrder.nativeOrder())
.asFloatBuffer()
.put(tableVertices);
verticeData.position(0);
}
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
GlsurfaceView calls this method when the surface is created. This happens in the application
It is also called when you first run it or when you return from another Activity
// Clear the screen
GLES20.glClearColor(0.0 f.0.0 f.0.0 f.0.0 f);
// Read the shader source code
String fragment_shader_source = ReadResouceText.readResoucetText(mContext, R.raw.fragment_shader);
String vertex_shader_source = ReadResouceText.readResoucetText(mContext, R.raw.vertex_shader);
// Compile the shader source code
int mVertexshader = ShaderHelper.compileShader(GLES20.GL_VERTEX_SHADER, vertex_shader_source);
int mFragmentshader = ShaderHelper.compileShader(GLES20.GL_FRAGMENT_SHADER, fragment_shader_source);
// Link program
int program = ShaderHelper.linkProgram(mVertexshader, mFragmentshader);
// Validate opengL objects
ShaderHelper.volidateProgram(program);
// Use the program
GLES20.glUseProgram(program);
// Get the shader attribute
u_color = GLES20.glGetUniformLocation(program, "u_Color");
a_position = GLES20.glGetAttribLocation(program, "a_Position");
// Bind a_position and verticeData vertex positions
/ * * * the first parameter, the second parameter is the shader properties * and how much weight each vertex, we have only to the component * the third parameter, the fourth parameter data types * and is only meaningful plastic, ignore * 5 parameters, an array has multiple attributes to be meaningful, we only have an attribute, Pass 0 * sixth argument, where does OpengL read from */
verticeData.position(0);
GLES20.glVertexAttribPointer(a_position, POSITION_COMPONENT_COUNT, GLES20.GL_FLOAT,
false.0, verticeData);
// Start vertex
GLES20.glEnableVertexAttribArray(a_position);
}
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
// After the Surface is created, this method is called every time the Surface size changes, such as switching between horizontal and vertical screens
GLES20.glViewport(0.0, width, height);
}
@Override
public void onDrawFrame(GL10 gl) {
// When drawing each frame of data, this method is called. This method must draw something, even if it is just to empty the screen
// Because when this method returns, the data in the render area will be exchanged and displayed on the screen. If nothing else, you will see a flicker effect
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
// Draw a rectangle
// Specifies that the color of the shader u_color is white
GLES20.glUniform4f(u_color, 1.0 f.1.0 f.1.0 f.1.0 f);
/** * First argument: draw draw triangle * second argument: read from vertices array 0 * third argument: read 6 vertices ** Finally draw two triangles to form a rectangle */
GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0.6);
// Draw the dividing line
GLES20.glUniform4f(u_color, 1.0 f.0.0 f.0.0 f.1.0 f);
GLES20.glDrawArrays(GLES20.GL_LINES, 6.2);
/ / draw point
GLES20.glUniform4f(u_color, 0.0 f.0.0 f.1.0 f.1.0 f);
GLES20.glDrawArrays(GLES20.GL_POINTS, 8.1);
GLES20.glUniform4f(u_color, 1.0 f.0.0 f.0.0 f.1.0 f);
GLES20.glDrawArrays(GLES20.GL_POINTS, 9.1); }}Copy the code
public class ReadResouceText {
public static String readResoucetText(Context context, int resouceId) {
StringBuffer body = new StringBuffer();
try {
InputStream inputStream = context.getResources().openRawResource(resouceId);
InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
String nextline;
while((nextline = bufferedReader.readLine()) ! =null) {
body.append(nextline);
body.append("\n"); }}catch (IOException e) {
e.printStackTrace();
}
returnbody.toString(); }}Copy the code
public class ShaderHelper {
public static int compileShader(int type, String source) {
/ / create a shader
int shaderId = GLES20.glCreateShader(type);
if (shaderId == 0) {
Log.d("mmm"."Failed to create shader");
return 0;
}
// Upload shader source code
GLES20.glShaderSource(shaderId, source);
// Compile the shader source code
GLES20.glCompileShader(shaderId);
// Retrieve the compiled result
int[] compileStatus = new int[1];
// fetch shaderId's compileStatus and write it to index 0 of compileStatus
GLES20.glGetShaderiv(shaderId, GLES20.GL_COMPILE_STATUS, compileStatus, 0);
Log.d("MMM compile status", GLES20.glGetShaderInfoLog(shaderId));
if (compileStatus[0] = =0) {
GLES20.glDeleteShader(shaderId);
Log.d("mmm"."Failed to create shader");
return 0;
}
return shaderId;
}
public static int linkProgram(int mVertexshader, int mFragmentshader) {
// Create a program object
int programId = GLES20.glCreateProgram();
if (programId == 0) {
Log.d("mmm".Failed to create program);
return 0;
}
// Attach the shader
GLES20.glAttachShader(programId, mVertexshader);
GLES20.glAttachShader(programId, mFragmentshader);
// Link program
GLES20.glLinkProgram(programId);
// Check the link status
int[] linkStatus = new int[1];
GLES20.glGetProgramiv(programId, GLES20.GL_LINK_STATUS, linkStatus, 0);
Log.d("mmm"."Link program" + GLES20.glGetProgramInfoLog(programId));
if (linkStatus[0] = =0) {
GLES20.glDeleteProgram(programId);
Log.d("mmm"."Link program failed");
return 0;
}
return programId;
}
public static boolean volidateProgram(int program) {
GLES20.glValidateProgram(program);
int[] validateStatus = new int[1];
GLES20.glGetProgramiv(program, GLES20.GL_VALIDATE_STATUS, validateStatus, 0);
Log.d("mmm"."Current OpenL status" + validateStatus[0] + "/" + GLES20.glGetProgramInfoLog(program));
return validateStatus[0] != 0;
}
public static int buildProgram(String vertex_shader_source,String fragment_shader_source){
// Compile the shader source code
int mVertexshader = compileShader(GLES20.GL_VERTEX_SHADER, vertex_shader_source);
int mFragmentshader = compileShader(GLES20.GL_FRAGMENT_SHADER, fragment_shader_source);
// Link program
int program = ShaderHelper.linkProgram(mVertexshader, mFragmentshader);
// Validate opengL objects
ShaderHelper.volidateProgram(program);
returnprogram; }}Copy the code