Developing with OpenGL ES in Android Lesson (2): Drawing Graphics

1. Preliminary basic knowledge reserve

The author plans to write three articles to analyze in detail the basics of OpenGL ES and the three key points of entry:

①What is OpenGL ES? What is the relationship with OpenGL? - Concept part

②The first step of using OpenGLES to draw 2D/3D graphics: define graphics; - application part

③Use OpenGLES to draw the graphics defined in the ②step: - the application part, where the difficulty lies

Through the analysis of these three articles, it is like laying the foundation stone for the high-rise building. The high-rise building starts from the ground, and then uses OpenGLES to do various effects. Various transformations are based on the understanding of these three steps of graphics programming.

Today, I will start the third lecture - the analysis of the drawing part, where the difficulty lies!

In the previous two articles "Development with OpenGL ES in Android Lesson (1): Concept First" and "Development with OpenGL ES in Android Lesson (2): Defining Graphics" , the author analyzed OpenGL ES2 in detail. .0 related important concepts and the realization of the definition of the vertex coordinates of a triangle, then, this article will talk about the specific drawing of the graphics defined in the second article, which is also the main difficulty of the author's three articles. where.

As mentioned in the first article, when using OpenGL ES2.0, although writing the simplest programs (such as basic drawing, triangle, rectangle or action translate, rotate, scale, etc.), be sure to write shaders (shaders). /renderer) to work, which was originally done by the system in OpenGL ES1.x, but now needs to be implemented manually, which increases the difficulty of development .

Second, the above code, the specific implementation

Step 1: Initialize graphics in the renderer class - simple;

public class MyGLRenderer2 implements GLSurfaceView.Renderer {

    ...
    private Triangle mTriangle;

    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        // initialize a triangle
        mTriangle = new Triangle();
    }
    ...
}

Step 2 : Prepare for the draw() method - create a shader object (shader), put it in the program (Program), link the program to GLES - complex ;

Shader-related concepts ( emphasis ): We spent a lot of time introducing shaders in the first article, so what exactly are shaders?

①Vertex Shader: OpenGL ES code used to render graphics vertices ;

② Fragment Shader: OpenGL ES code that uses color or texture to render graphics surfaces;

③ Program (Program): An OpenGL ES object that contains the shaders you want to use to draw graphics. Finally, vertex shaders and fragment shaders must be put into the program before they can be used.

For the above three, you need at least one vertex shader (Vertex Shader) to define a graph vertex, and a fragment shader (Fragment Shader) to color the graph. These shaders must be compiled and then added to an OpenGLES Program and use this progrem to draw shapes.

This code is implemented by the system in the original OpenGL ES1.X, so the code for 1.x is relatively simple to write, but in 2.x we use manual methods to implement it, although it is a bit complicated, but by writing vertices And fragment shader program to complete some vertex transformation and texture color calculation work, which can achieve more flexible and refined calculation and rendering.

After being familiar with the concept of shaders, our code is implemented in three steps:

①In the graphics class, create two GLSL code segments - vertex shader code segment + fragment shader code ;

public class Triangle {

   /**
     * vertex shader code
     * attribute variables (attribute variables) can only be used in vertex shaders
     * uniforms variables (uniform variables) are used to pass data values ​​from the application to the vertex shader or fragment shader. .
     * varying variables (variable variables) are data variables passed from the vertex shader to the fragment shader.
     * gl_Position (required) is a built-in variable representing the spatial position of the transformed point.
     */
    private final String vertexShaderCode =
            "attribute vec4 vPosition;" + // the vertex position passed to the vertex shader by the application
                    "void main() {" +
                    " gl_Position = vPosition;" + // Set the vertex position for this drawing
                    "}";

    /**
     * Fragment shader code
     */
    private final String fragmentShaderCode =
            "precision mediump float;" + // set working precision
                    "uniform vec4 vColor;" + // the color variable passed to the shader by the application
                    "void main() {" +
                    " gl_FragColor = vColor;" + // The color value is passed to the gl_FragColor built-in variable to complete the coloring of the fragment
                    "}";
   ...
}

For more shader code analysis content, interested readers can refer to 1 article , 2 article , 3 article

②In the renderer class, create a helper method for compiling the two code segments created in ①.

In this auxiliary method, we pass in two parameters. The first parameter is the type of shader, including two vertex shaders (GLES20.GL_VERTEX_SHADER) and fragment shaders (GLES20.GL_FRAGMENT_SHADER). The second parameter is ① The two shader code snippets defined in .

public class MyGLRenderer2 implements GLSurfaceView.Renderer
    ...

    /**
     * Load and compile shader code
     * Renderer type type={GLES20.GL_VERTEX_SHADER, GLES20.GL_FRAGMENT_SHADER}
     * Renderer code GLSL
     */
    public static int loadShader(int type, String shaderCode){

        int shader = GLES20.glCreateShader(type);

        GLES20.glShaderSource(shader, shaderCode);
        GLES20.glCompileShader(shader);

        return shader;
    }
}

③In the graphics class, we pass in the specific parameters to the method defined in ②, get two shader objects, and then put the two shader objects into the program (Program). Finally, link the program with GLES.

public class Triangle() {
    ...

    private final int mProgram;

    public Triangle() {
        ...
        
        // Load the compiled vertex renderer
        int vertexShader = MyGLRenderer2.loadShader(GLES20.GL_VERTEX_SHADER,
                vertexShaderCode);

        // Load the compiled fragment renderer
        int fragmentShader = MyGLRenderer2.loadShader(GLES20.GL_FRAGMENT_SHADER,
                fragmentShaderCode);

        // create empty OpenGL ES Program
        mProgram = GLES20.glCreateProgram();

        // add the vertex shader to program
        GLES20.glAttachShader(mProgram, vertexShader);

        // add the fragment shader to program
        GLES20.glAttachShader(mProgram, fragmentShader);

        // creates OpenGL ES program executables
        GLES20.glLinkProgram(mProgram);
    }
}

After completing the above two steps, you are ready to define the draw() method in the graphics class - the draw() method is really long-awaited!

Step 3 : Create the draw() method in the graphics class , get the program linked to GLES, set the vertex position of the shape and the color value of the surface - complex ;

The following code sets the position and color values ​​for the shape's vertex shader and shape shader , and then executes the draw function:

public class Triangle {

    // number of vertices to draw the shape
    private static final int COORDS_PER_VERTEX = 3;

    ...

    private int mPositionHandle;
    private int mColorHandle;

    private final int vertexCount = triangleCoords.length / COORDS_PER_VERTEX;
    private final int vertexStride = COORDS_PER_VERTEX * 4; // 4 bytes per vertex

        public void draw() {
        // Add program to OpenGL ES environment
        GLES20.glUseProgram(mProgram);

        // get handle to vertex shader's vPosition member
        mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");

        // Enable a handle to the triangle vertices
        GLES20.glEnableVertexAttribArray(mPositionHandle);

        // Prepare the triangle coordinate data
        GLES20.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX,
                GLES20.GL_FLOAT, false,
                vertexStride, vertexBuffer);

        // get handle to fragment shader's vColor member
        mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor");

        // Set color for drawing the triangle
        GLES20.glUniform4fv(mColorHandle, 1, color, 0);

        // Draw the triangle
        GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount);

        // Disable vertex array
        GLES20.glDisableVertexAttribArray(mPositionHandle);
    }
}

The code in the draw() method is also very complicated, it doesn't matter, we can see it clearly by swiping it. The whole code can actually be divided into three parts:

①1 line of code - get the program (Program) linked to GLES in the draw() method;

②3 lines of code - take out the vertex shader from the program and start setting the position of the vertex ;

③3 lines of code - Take out the fragment shader from the program and start setting the color of the graphics surface .

Step 4 : Call the draw() method defined in the graphics class in the renderer class for specific drawing

public class MyGLRenderer2 implements GLSurfaceView.Renderer {

    @Override
    public void onDrawFrame(GL10 gl) {
        ...
        mTriangle.draw();
    }
}

Summary: So far, the author's three introductory lessons on OpenGL ES2.0 have ended. The coverage is there, but it is not in-depth. In fact, the original plan is to popularize the basic OpenGL ES2.0 through these three articles. usage, and I will continue to write articles to improve it in the future. (Writing and writing, I found a lot of pits dug! 囧囧)

Finally, attach the links to the first two articles: "Development with OpenGL ES in Android Lesson (1): Concept First"

"Development with OpenGL ES in Android Lesson (2): Defining Graphics"

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324646475&siteId=291194637