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