ESProjection 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 triangles with perspective. We are no longer limited to normalized device coordinates.
 */
public class TriangleRenderer
             implements GLSurfaceView.Renderer
{
    private final String      appName               = "ESProjection";

    // 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;
    private       float[]     projectionMatrix      = new float[16];
    /** Translation for the second triangle has floating point x and y components. */
    private       PointF      translation           = new PointF();
    // Vertex buffer for the first triangle.
    private       int         vertexBuffer;
    private       float[]     vertices;
    /** Translate from world space to the view. */
    private       float[]     viewMatrix;


    public TriangleRenderer()
    {
        vertices     = new float[] {
                                    // Vetex coordinate
                                    -150.0f, -150.0f, -1.0f,
                                    // Vertex color
                                       1.0f,    0.0f,  0.0f, 1.0f,
                                     150.0f, -150.0f, -1.0f,
                                       0.0f,    1.0f,  0.0f, 1.0f,
                                       0.0f,  150.0f, -1.0f,
                                       0.0f,    0.0f,  1.0f, 1.0f
                                   };
        
        moreVertices = new float[] {
                                    -150.0f, -150.0f,  -2.0f,
                                       0.0f,    0.0f,   1.0f, 1.0f,
                                     150.0f, -150.0f,  -2.0f,
                                       0.0f,    1.0f,   0.0f, 1.0f,
                                       0.0f,  150.0f,  -2.0f,
                                       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] == 0)
        {
            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] == 0)
        {
            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;
    }

    /**
     * Generate a matrix corresponding to an eye at eye(x,y,z) looking toward
     * look (x,y,z), with the up direction up (x,y,z).
     *
     * @return A matrix that will transform the scene according to the given view.
     */
    public float[] generateViewMatrix()
    {
        final float[] viewMatrix = new float[16];
        
        // Position the eye behind the origin.
        final float   eyeX       = 0.0f;
        final float   eyeY       = 0.0f;
        final float   eyeZ       = 0.0f;

        // We are looking in the -z direction
        final float   lookX      =  0.0f;
        final float   lookY      =  0.0f;
        final float   lookZ      = -1.0f;

        // Up is the y axis.
        final float   upX        = 0.0f;
        final float   upY        = 1.0f;
        final float   upZ        = 0.0f;

        // Set the view matrix. This matrix can be said to represent the camera position.
        Matrix.setLookAtM(viewMatrix, 0, eyeX, eyeY, eyeZ, lookX, lookY, lookZ, upX, upY, upZ);

        return viewMatrix;
    }

    /**
     * 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);
        GLES20.glDepthMask(true);

        final String vertexShaderSource   = "attribute vec3 position;"
                                            + "attribute vec4 color;"
                                            + "uniform   mat4 modelViewMatrix;"
                                            + "uniform   mat4 projectionMatrix;"
                                            + ""
                                            + "varying vec4 vColor;"
                                            + ""
                                            + "void main()"
                                            + "{"
                                            + "    gl_Position = projectionMatrix * modelViewMatrix * 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);

        viewMatrix    = generateViewMatrix();

        // Load a projection matrix, it projects the scene down into normalized device coordinates.
        // frustumM (float[] m, int offset, float left, float right, float bottom, float top, float near, float far)
        Matrix.frustumM(projectionMatrix,  0, -150.0f, 150.0f, -150.0f, 150.0f, 1.0f, 100.0f);
    }

    /**
     * Height or width changed, usually portrait <=> landscape.
     */
    @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 into the FloatBuffer 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);
    }

    /**
     * Supply a 4x4 floating point matrix to the given program.
     * @param program A handle on the target program.
     * @param matrix  A Float array containing the matrix elements in column major order.
     * @param name    A String giving the name of the uniform/
     */
    protected void bindMatrixUniform(int program, float[] matrix, String name)
    {
        int uniformLocation = GLES20.glGetUniformLocation(program, name);
        // Location, count (1 matrix), transposed, values, offset
        GLES20.glUniformMatrix4fv(uniformLocation, 1, false, matrix, 0);
    }

    /**
     * Draws each frame. Invoked when the surface first becomes drawable,
     * and again whenever motion is detected.
     * @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);

        bindMatrixUniform(program, viewMatrix,       "modelViewMatrix");
        bindMatrixUniform(program, projectionMatrix, "projectionMatrix");

        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);

        // Take into account translations of the second triangle, if any.
        float[] newModelView = new float[16];

        Matrix.translateM(newModelView, 0, viewMatrix, 0, translation.x, translation.y, 0);
        bindMatrixUniform(program, newModelView,       "modelViewMatrix");

        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);
    }

    /**
     * Invoked by the surfaced view with normalized change in x and y
     * @param deltaX A float giving how far the touch moved in the x direction.
     * @param deltaY A float giving how far the touch moved in the y direction.
     */
    public void motion(float deltaX, float deltaY)
    {
        translation.x += deltaX * 300;
        translation.y += deltaY * 300;
    }
}
          
        

Notes

Now we have a slightly more complex structure for our OpenGL program.