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;
/**
* Use two different programs to render vertices in two completely different
* ways. The original program remains for the larger triangle, with the smaller
* triangle now rendered as a set of points.
*/
public class TriangleRenderer
implements GLSurfaceView.Renderer
{
private final String appName = "ESPrograms";
// 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;
// An OpenGL program
private int program;
// A second OpenGL program
private int programII;
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;
}
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)
{
int fragmentShader;
String fragmentShaderSource;
int vertexShader;
String vertexShaderSource;
// 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);
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.
fragmentShaderSource = "precision mediump float;"
+ ""
+ "varying vec4 vColor;"
+ ""
+ "void main()"
+ "{"
+ " gl_FragColor = vColor;"
+ "}";
vertexShader = compileShader(vertexShaderSource, GLES20.GL_VERTEX_SHADER);
fragmentShader = compileShader(fragmentShaderSource, GLES20.GL_FRAGMENT_SHADER);
program = createProgram(vertexShader, fragmentShader);
// We have now built our first program, and start with the second.
// To build the second program, we repeat the same process, just
// store the handle to the newly created program. onDrawFrame makes
// use of both programs.
vertexShaderSource = "precision highp float;"
+ "attribute vec3 position;"
+ "attribute vec4 color;"
+ "uniform mat4 modelViewMatrix;"
+ "uniform mat4 projectionMatrix;"
+ ""
+ "varying vec4 vColor;"
+ ""
+ "void main()"
+ "{"
+ " gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1);"
+ " gl_PointSize = 16.0;"
+ " vColor = color;"
+ "}";
vertexShader = compileShader(vertexShaderSource, GLES20.GL_VERTEX_SHADER);
// Reuse the same fragment shader source.
fragmentShader = compileShader(fragmentShaderSource, GLES20.GL_FRAGMENT_SHADER);
programII = 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;
// Take into account translations of the second triangle, if any.
float[] newModelView = new float[16];
GLES20.glClearColor(.9255f, .8941f, .8275f, 1.0f);
GLES20.glClear( GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);
stride = bytesPerFloat*(colorElementSize + positionElementSize);
Matrix.translateM(newModelView, 0, viewMatrix, 0, translation.x, translation.y, 0);
// Use the point drawing program.
GLES20.glUseProgram(programII);
// We have to bind uniforms and attributes separately for each program.
bindMatrixUniform(programII, newModelView, "modelViewMatrix");
bindMatrixUniform(programII, projectionMatrix, "projectionMatrix");
bindBuffer(anotherBuffer, programII, "position", positionElementSize,
GLES20.GL_FLOAT, stride, positionOffset * bytesPerFloat);
bindBuffer(anotherBuffer, programII, "color", colorElementSize,
GLES20.GL_FLOAT, stride, (positionOffset + positionElementSize)*bytesPerFloat);
GLES20.glDrawArrays(GLES20.GL_POINTS, 0, nElements);
// Use the original program to render the larger triangle.
GLES20.glUseProgram(program);
bindMatrixUniform(program, viewMatrix, "modelViewMatrix");
bindMatrixUniform(program, projectionMatrix, "projectionMatrix");
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);
}
/**
* 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;
}
}
Uses multiple programs to render the vertices in different styles. This is the preferred method to provide richer functionality. Do not use complex shaders with conditions, build multiple small programs and use the different programs for different tasks.