ESTriangles Renderer

          
package com.vizit;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import javax.microedition.khronos.egl.EGLConfig;
import java.nio.FloatBuffer;
import javax.microedition.khronos.opengles.GL10;
import android.opengl.GLES20;
import android.opengl.GLSurfaceView;
import java.nio.IntBuffer;
import android.util.Log;
import android.opengl.Matrix;
import android.graphics.PointF;

/**
 * Render two triangles. Drawing multiple objects is an essential step towards a complete application.
 * This example shows how to draw multiple objects, and we learn something interesting about depth
 * along the way.
 */
public class TriangleRenderer
             implements GLSurfaceView.Renderer
{
    private final String      appName               = "ESTriangles";

    // Vertex buffer for the second triangle.
    private       int         anotherBuffer;
    /** How many bytes per float. */
    private final int         bytesPerFloat         = 4;
    /** R, G, B, A for color at each vertex. */
    private final int         colorElementSize      = 4;
    private       float[]     moreVertices;
    /** Number of vertex elements. */
    private final int         nElements             = 3;
    /** Each position element has three values, x,y, and z coordinates. */
    private final int         positionElementSize   = 3;
    /** Offset of the position data. */
    private final int         positionOffset        = 0;
    private       int         program;
    // Vertex buffer for the first triangle.
    private       int         vertexBuffer;
    private       float[]     vertices;

    /**
     * This time, though, there are two sets of vertices hence two objects.
     */
    public TriangleRenderer()
    {
        // Now, we pay attention to the third, z, coordinate. The first triangle remains at z=0
        vertices     = new float[] {
                                    // Vertex coordinate,  3 floats (XYZ)
                                    -1.0f,   -1.0f,  0.0f,
                                    // Vertex color, four floats (RGBA)
                                     1.0f,    0.0f,  0.0f, 1.0f,
                                     1.0f,   -1.0f,  0.0f,
                                     0.0f,    1.0f,  0.0f, 1.0f,
                                     0.0f,    1.0f,  0.0f,
                                     0.0f,    0.0f,  1.0f, 1.0f
                                   };

        // The second set of vertices is at z=0.5.
        moreVertices = new float[] {
                                    -1.0f,   -1.0f,  0.5f,
                                     0.0f,    0.0f,  1.0f, 1.0f,
                                     1.0f,   -1.0f,  0.5f,
                                     0.0f,    1.0f,  0.0f, 1.0f,
                                     0.0f,    1.0f,  0.5f,
                                     1.0f,    0.0f,  0.0f, 1.0f
                                   };
        
    }

   /**
     * Compile a shader of the given type.
     * @param shaderSource A string containing GLSL code for the shader.
     * @param shaderType   The type of shader: GL_VERTEX_SHADER or GL_FRAGMENT_SHADER for OpenGL ES 2.
     *
     * @return The compiled shader.
     */
    protected int compileShader(String shaderSource, int shaderType)
    {
        IntBuffer logStatus;
        int       shader;
        String    compileLog;
        int[]     compileStatus;

        shader = GLES20.glCreateShader(shaderType);
        // Pass in the shader source.
        GLES20.glShaderSource(shader, shaderSource);
        // Compile the shader.
        GLES20.glCompileShader(shader);

        // Get the compilation status.
        compileStatus = new int[1];
        GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compileStatus, 0);

        // Error compile status, get the relevant log.
        if (compileStatus[0] != GLES20.GL_TRUE)
        {
            logStatus     = ByteBuffer.allocateDirect(4).order(ByteOrder.nativeOrder()).asIntBuffer();
            GLES20.glGetShaderiv(shader, GLES20.GL_INFO_LOG_LENGTH, logStatus);
            if (logStatus.get(0) > 0)
            {
                compileLog = GLES20.glGetShaderInfoLog(shader);
                Log.d(appName, "Shader compilation failed with, " + compileLog);
            }
            else
            {
                // Workaround for issue 9953, where GL_INFO_LOG_LENGTH is zero.
                Log.d(appName, "Shader compilation failed for an unknown reason.");
            }
            GLES20.glDeleteShader(shader);
            shader = 0;
        }
        
        return shader;
    }

    /**
     * Given the handles to two previously compiled shaders, assemble them into a program.
     *
     * @param vertexShader   The handle for the vertex shader for this program.
     * @param fragmentShader The handle for ths fragment shader for this program.
     *
     * @return int A handle on the linked program.
     */
    protected int createProgram(int vertexShader, int fragmentShader)
    {
        String    loaderLog;
        IntBuffer logStatus;
        int       program;

        program = GLES20.glCreateProgram();

        GLES20.glAttachShader(program, vertexShader);

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

        GLES20.glLinkProgram(program);

        // Get the link status.
        final int[] linkStatus = new int[1];
        GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0);

        // If the link failed, delete the program.
        if (linkStatus[0] != GLES20.GL_TRUE)
        {
            logStatus     = ByteBuffer.allocateDirect(4).order(ByteOrder.nativeOrder()).asIntBuffer();
            GLES20.glGetProgramiv(program, GLES20.GL_INFO_LOG_LENGTH, logStatus);
            if (logStatus.get(0) > 0)
            {
                loaderLog = GLES20.glGetProgramInfoLog(program);
                Log.d(appName, "Program linking failed with, " + loaderLog);
            }
            else
            {
                // Workaround for issue 9953, where GL_INFO_LOG_LENGTH is zero.
                Log.d(appName, "Program linking failed for an unknown reason.");
            }
            GLES20.glDeleteProgram(program);
            program = 0;
        }

        GLES20.glUseProgram(program);

        return program;
    }

    /**
     * Load vertex data into a buffer so that it can be read into vertex shader attributes.
     *
     * @param vertexData An array of floats containing per vertex data.
     */
    protected int createBuffer(float[] vertexData)
    {
        int[] vertexBuffer = new int[1];

        /** Store our model data in a float buffer. */
        FloatBuffer vertexBufferData;

        // The OpenGL Driver expects floating point data in native byte order.
        vertexBufferData = ByteBuffer.allocateDirect(vertexData.length * bytesPerFloat)
                                     .order(ByteOrder.nativeOrder()).asFloatBuffer();

        vertexBufferData.put(vertexData).position(0);

        // Generate a single buffer handle into element 0 of the array.
        GLES20.glGenBuffers(1, vertexBuffer, 0);

        // Binding an object in Open GL creates it, and makes it the target of subsequent manipulations.
        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vertexBuffer[0]);

        // loads the current buffer, the vertexBuffer found above, with the vertex data.
        GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, vertexBufferData.capacity() * bytesPerFloat,
                            vertexBufferData, GLES20.GL_STATIC_DRAW);

        return vertexBuffer[0];
    }

    /**
     * Invoked after the rendering surface is setup - the OpenGL context is created.
     * It is now safe to compile shaders, build programs, setup vertex buffers, etc.
     */
    @Override
    public void onSurfaceCreated(GL10 glUnused, EGLConfig config)
    {
        // Enable depth tests to draw only nearer objects, less depth.
        GLES20.glEnable(GLES20.GL_DEPTH_TEST);
        GLES20.glDepthFunc(GLES20.GL_LEQUAL);
        // Allow writing to the depth buffer
        GLES20.glDepthMask(true);

        final String vertexShaderSource   = "attribute vec3 position;"
                                            + "attribute vec4 color;"
                                            + ""
                                            + "varying vec4 vColor;"
                                            + ""
                                            + "void main()"
                                            + "{"
                                            + "    gl_Position = vec4(position, 1);"
                                            + "    vColor      = color;"
                                            + "}";

        // The fragment shader can be thought of for now as doing per pixel computations. It is the
        // fragment shader the colors each pixel in a 3d scene.
        final String fragmentShaderSource = "precision mediump float;"
                                            + ""
                                            + "varying vec4 vColor;"
                                            + ""
                                            + "void main()"
                                            + "{"
                                            + "    gl_FragColor = vColor;"
                                            + "}";

        int vertexShader   = compileShader(vertexShaderSource,   GLES20.GL_VERTEX_SHADER);
        int fragmentShader = compileShader(fragmentShaderSource, GLES20.GL_FRAGMENT_SHADER);

        program       = createProgram(vertexShader, fragmentShader);

        vertexBuffer  = createBuffer(vertices);
        anotherBuffer = createBuffer(moreVertices);
    }

    @Override
    public void onSurfaceChanged(GL10 glUnused, int width, int height)
    {
        // Resize our drawing surface to the new height and width.
        GLES20.glViewport(0, 0, width, height);
    }

    /**
     * Setup pointer for attribute to read data into shader attribute.
     * @param vertexBuffer The FloatBuffer within this program space containing the data to be read.
     * @param program      A handle on the GLSL program that will consume the data.
     * @param attribute    A String giving the name of the shader attribute associated with this data.
     * @param size         The number of entries from the buffer that make up each attribute value.
     * @param type         The type of each entry.
     * @param stride       An in giving the begin-begin spacing of values in bytes. 0 for adjacent values.
     * @param offset       An in giving the offset in bytes into the Buffer where we begin reading data.
     */
    protected void bindBuffer(int vertexBuffer, int program, String attribute, int size, int type, int stride, int offset)
    {
        int            attributeLocation;

        attributeLocation = GLES20.glGetAttribLocation(program, attribute);

        // Binding an object makes it the target of subsequent manipulations.
        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vertexBuffer);

        GLES20.glVertexAttribPointer(attributeLocation, size, type, false, stride, offset);

        GLES20.glEnableVertexAttribArray(attributeLocation);
    }

    /**
     * Draws each frame.
     * @param glUnused
     */
    @Override
    public void onDrawFrame(GL10 glUnused)
    {
        int stride;

        GLES20.glClearColor(.9255f, .8941f, .8275f, 1.0f);
        GLES20.glClear( GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);

        stride       = bytesPerFloat*(colorElementSize + positionElementSize);

        bindBuffer(vertexBuffer,    program, "position",                                           positionElementSize,
                   GLES20.GL_FLOAT, stride,   positionOffset*bytesPerFloat);
        bindBuffer(vertexBuffer,    program, "color",                                              colorElementSize,
                   GLES20.GL_FLOAT, stride,  (positionOffset + positionElementSize)*bytesPerFloat);

        GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, nElements);

        // Just repeat the part that defines the vertex data and draws the triangles.
        bindBuffer(anotherBuffer,   program, "position",                                          positionElementSize,
                   GLES20.GL_FLOAT, stride,   positionOffset*bytesPerFloat);
        bindBuffer(anotherBuffer,   program, "color",                                             colorElementSize,
                   GLES20.GL_FLOAT, stride,  (positionOffset + positionElementSize)*bytesPerFloat);


        GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, nElements);
    }
}
          
        

Notes

During the presentation we walked through and explained the ESTriangles sample code. The second triangle with z=0.5 is behind the traingle with z=0. Z increases into the screen.