ESTransparency 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               = "ESTransparency";

    // 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, 0.5f,
                                     150.0f, -150.0f, -1.0f,
                                       0.0f,    1.0f,  0.0f, 0.5f,
                                       0.0f,  150.0f, -1.0f,
                                       0.0f,    0.0f,  1.0f, 0.5f
                                   };
        
        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);
        // Enable transparency
        GLES20.glEnable(GLES20.GL_BLEND);
        // How to blend into existing scent when a transparent object is drawn over preexisting content.
        GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA);

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

        stride       = bytesPerFloat*(colorElementSize + positionElementSize);

        bindMatrixUniform(program, projectionMatrix, "projectionMatrix");
        bindMatrixUniform(program, viewMatrix,       "modelViewMatrix");
        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

Initially the small triangle that is behind the first is not visible. This is because the large triangle is in front of the small triangle and it is drawn first. OpenGL does not normally draw an object that is behind an object that has already been drawn.

Reversing the order in which the triangles are drawn makes the smaller triangle visible.

In general, draw opaque objects first, then draw transparent objects back to front.