Android OpenGL ES2.0 (1) - Drawing Static Shapes

Posted by vivianp79 on Sat, 06 Jul 2019 18:48:46 +0200

quick get start

Key Classes

  1. GLSurfaceView
    • Carriers drawn
    • TextureView is available for small areas
    • SurfaceView can theoretically be used
  2. GLSurfaceView.Renderer
    • Essential Drawing Action

Keying a GLSurfaceView object

class MyGLSurfaceView extends GLSurfaceView {

    public MyGLSurfaceView(Context context){
        super(context);

        // Create an OpenGL ES 2.0 context
        setEGLContextClientVersion(2);

        // Set Renderer to GLSurfaceView
        setRenderer(new MyRenderer());

        // Draw view only when drawing data changes
        setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
    }
}

GLSurfaceView.RENDERMODE_WHEN_DIRTY prevents the frames of GLSurfaceView from being drawn until you call requestRender(), which is very efficient.

Renderer drawing gray background

public class MyGL20Renderer implements GLSurfaceView.Renderer {

    // Called only once to set the OpenGLES environment for view
    public void onSurfaceCreated(GL10 unused, EGLConfig config) {
        // Set the color of the background
        GLES20.glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
    }

    public void onDrawFrame(GL10 unused) {
        // Redraw Background Color
        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
    }

    public void onSurfaceChanged(GL10 unused, int width, int height) {
        GLES20.glViewport(0, 0, width, height);
    }
}

These methods all have a GL10 parameter, but you are using the OpengGLES 2.0 API s, which are designed to make the Android framework easily compatible with OpenGLES versions.

Define Shape

With OpenGLES's coordinate system relative to Android, shapes can be defined.Triangles are the basis of all shapes, because many complex three-dimensional graphics can be thought of as a large number of triangles.Because we know how to define triangles, and then we make squares out of them.

Triangle

To define a triangle, you need to define the coordinates of its three vertices in three-dimensional space.In OpenGL, a typical way is to define an array of vertices of floating point type for coordinates.

For maximum efficiency, you should write these coordinates into a ByteBuffer, which will be passed to the OpenGLES graphics pipeline for processing.

By default, OpenGLES assumes that [0,0,0] is the center of the GLSurfaceView frame, [1,1,0] is the upper right corner, [-1, -1,0] is the lower left corner.The order of coordinates is (X, Y, Z).

class Triangle {

    private FloatBuffer vertexBuffer;

    // Number of coordinates per vertex in the array
    static final int COORDS_PER_VERTEX = 3;
    static float triangleCoords[] = { // In counterclockwise order:
         0.0f,  0.622008459f, 0.0f,   // top
        -0.5f, -0.311004243f, 0.0f,   // bottom left
         0.5f, -0.311004243f, 0.0f    // bottom right
    };

    // Set colors, red, green, blue and alpha (opacity)
    float color[] = { 0.63671875f, 0.76953125f, 0.22265625f, 1.0f };

    public Triangle() {
        // Initialize vertex byte buffer to store shape coordinates
        ByteBuffer bb = ByteBuffer.allocateDirect(
                // (Number of coordinates * 4)float takes up four bytes
                triangleCoords.length * 4);
        // Set the native byte order of the device
        bb.order(ByteOrder.nativeOrder());

        // Create a floating-point buffer from ByteBuffer
        vertexBuffer = bb.asFloatBuffer();
        // Add coordinates to FloatBuffer
        vertexBuffer.put(triangleCoords);
        // Set buffer to read from first coordinate
        vertexBuffer.position(0);
    }
}

The coordinates of this shape are defined counterclockwise.Drawing order is important because it defines which side is the front of the shape and which side is the back. With the cullface feature of OpenGLES, you can draw only the front side and not the back side.

Square

There are many ways to define a square, typically by using two triangles.


opengl_square.jpg

This square can be defined by defining four key coordinate points and the drawing order of two triangles as shown in the figure above.

class Square {

    private FloatBuffer vertexBuffer;
    private ShortBuffer drawListBuffer;

    // Number of coordinates per vertex
    static final int COORDS_PER_VERTEX = 3;
    static float squareCoords[] = { -0.5f,  0.5f, 0.0f,   // top left
                                    -0.5f, -0.5f, 0.0f,   // bottom left
                                     0.5f, -0.5f, 0.0f,   // bottom right
                                     0.5f,  0.5f, 0.0f }; // top right

    private short drawOrder[] = { 0, 1, 2, 0, 2, 3 }; // Drawing order of vertices

    public Square() {
        // initialize vertex byte buffer for shape coordinates
        ByteBuffer bb = ByteBuffer.allocateDirect(
        // (Number of coordinates * 4)
                squareCoords.length * 4);
        bb.order(ByteOrder.nativeOrder());
        vertexBuffer = bb.asFloatBuffer();
        vertexBuffer.put(squareCoords);
        vertexBuffer.position(0);

        // Initialize byte buffer for drawing list
        ByteBuffer dlb = ByteBuffer.allocateDirect(
        // (Number of coordinates in corresponding order * 2)short is 2 bytes
                drawOrder.length * 2);
        dlb.order(ByteOrder.nativeOrder());
        drawListBuffer = dlb.asShortBuffer();
        drawListBuffer.put(drawOrder);
        drawListBuffer.position(0);
    }
}

Draw Shape

OpenGL ES 2.0 requires a lot of code to draw a well-defined graph. Typically, you need to define the following:

  1. VertexShader - Vertices used to render shapes
  2. FragmentShader - Appearance (color or texture) used to render shapes
  3. Program-Contains the Shader you want to use to draw one or more shapes.

At least one VertexShader and one FragmentShader need to be protected in the drawing Program.

OpenGLShading Language

Following is the OpenGLShading Language (GLSL) code, Shader must be compiled to be added to the Program and then drawn through the Program.

private final String vertexShaderCode =
    "attribute vec4 vPosition;" +
    "void main() {" +
    "  gl_Position = vPosition;" +
    "}";

private final String fragmentShaderCode =
    "precision mediump float;" +
    "uniform vec4 vColor;" +
    "void main() {" +
    "  gl_FragColor = vColor;" +
    "}";

Shader can be compiled using the following static methods

public static int loadShader(int type, String shaderCode){

    // Create a vertex shader type (GLES20.GL_VERTEX_SHADER)
    // Or fragment shader type (GLES20.GL_FRAGMENT_SHADER)
    int shader = GLES20.glCreateShader(type);

    // Add source code to shader and compile it
    GLES20.glShaderSource(shader, shaderCode);
    GLES20.glCompileShader(shader);

    return shader;
}

Compiling OpenGLES shader s and linked programs is CPU intensive, so you should avoid executing them multiple times.

Draw

draw() is generally created in the shape class to draw.The following code sets the location and color values to the VertexShader and FragmentShader of the shape and then performs the drawing function.

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

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

    // Get handle pointing to member vPosition of vertex shader
    mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");

    // Enable handle on an array of vertices pointing to a triangle
    GLES20.glEnableVertexAttribArray(mPositionHandle);

    // Preparing coordinate data for triangles
    GLES20.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX,
                                 GLES20.GL_FLOAT, false,
                                 vertexStride, vertexBuffer);

    // Get handle pointing to member vColor of fragment shader 
    mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor");

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

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

    // Disable vertex arrays pointing to triangles
    GLES20.glDisableVertexAttribArray(mPositionHandle);
}

Ultimately, you only need to call the draw() method on Renderer's onDrawFrame() to complete the shape drawing.

Square's drawing logic is similar to Triangle's, requiring only modifications to key drawing methods.

//GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount);
GLES20.glDrawElements(GLES20.GL_TRIANGLES, drawOrder.length, GLES20.GL_UNSIGNED_SHORT, drawListBuffer);

Reference Article

Topics: Android Fragment SurfaceView Attribute