CS 436 Lecture 21 – OpenGL ES
November 4, 2014 by Chris Johnson. Filed under cs436, fall 2014, lectures.
Code
activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<android.opengl.GLSurfaceView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:id="@+id/surfaceView"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"/>
</RelativeLayout>
MainActivity.java
package org.twodee.cuber;
import android.graphics.PointF;
import android.opengl.GLSurfaceView;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.view.MotionEvent;
public class MainActivity extends ActionBarActivity {
private GLSurfaceView surfaceView;
private BoxRenderer renderer;
private PointF lastTouchAt;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
surfaceView = (GLSurfaceView) findViewById(R.id.surfaceView);
surfaceView.setEGLContextClientVersion(2);
renderer = new BoxRenderer();
surfaceView.setRenderer(renderer);
surfaceView.setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
lastTouchAt = new PointF(event.getX(), event.getY());
} else if (event.getAction() == MotionEvent.ACTION_MOVE) {
final float delta = event.getX() - lastTouchAt.x;
lastTouchAt = new PointF(event.getX(), event.getY());
surfaceView.queueEvent(new Runnable() {
@Override
public void run() {
renderer.rotate(delta / 720.0f * 360.0f);
}
});
}
return true;
}
}
CuberView.java
package org.twodee.cuber;
import android.content.Context;
import android.opengl.GLSurfaceView;
import android.util.AttributeSet;
import android.view.SurfaceHolder;
public class CuberView extends GLSurfaceView {
public CuberView(Context context,
AttributeSet attrs) {
super(context, attrs);
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
super.surfaceCreated(holder);
}
@Override
public void surfaceChanged(SurfaceHolder holder,
int format,
int w,
int h) {
super.surfaceChanged(holder, format, w, h);
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
super.surfaceDestroyed(holder);
}
}
BoxRenderer.java
package org.twodee.cuber;
import android.opengl.GLES20;
import android.opengl.GLSurfaceView.Renderer;
import android.opengl.Matrix;
import android.util.Log;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.ShortBuffer;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
public class BoxRenderer implements Renderer {
private int[] vbos;
private int[] ibos;
private int shaderProgram;
private float[] rotation;
private float[] projectionLookAt;
private float rotationX;
private int positionHandle;
private int colorHandle;
private int rotationHandle;
private int projectionLookAtHandle;
@Override
public void onSurfaceCreated(GL10 gl,
EGLConfig config) {
rotation = new float[16];
projectionLookAt = new float[16];
rotationX = 0.0f;
Matrix.setIdentityM(rotation, 0);
makeVBOs();
makeShaderProgram();
updateHandles();
updateUniforms();
GLES20.glClearColor(0.6f, 0.6f, 0.6f, 1.0f);
GLES20.glEnable(GLES20.GL_DEPTH_TEST);
}
private void makeVBOs() {
// Vertex positions
float[] positions = {
-0.5f, -0.5f, 0.5f,
0.5f, -0.5f, 0.5f,
-0.5f, 0.5f, 0.5f,
0.5f, 0.5f, 0.5f,
0.5f, -0.5f, 0.5f,
0.5f, -0.5f, -0.5f,
0.5f, 0.5f, 0.5f,
0.5f, 0.5f, -0.5f,
-0.5f, -0.5f, -0.5f,
-0.5f, -0.5f, 0.5f,
-0.5f, 0.5f, -0.5f,
-0.5f, 0.5f, 0.5f,
0.5f, -0.5f, -0.5f,
-0.5f, -0.5f, -0.5f,
0.5f, 0.5f, -0.5f,
-0.5f, 0.5f, -0.5f
};
Log.d("FOO", "allocating floats native buffer");
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(positions.length * 4);
byteBuffer.order(ByteOrder.nativeOrder());
FloatBuffer positionsBuffer = byteBuffer.asFloatBuffer();
positionsBuffer.put(positions);
positionsBuffer.position(0);
// Vertex positions
float[] colors = {
1.0f, 0.0f, 0.0f,
1.0f, 0.0f, 0.0f,
1.0f, 0.0f, 0.0f,
1.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f,
0.0f, 1.0f, 0.0f,
0.0f, 1.0f, 0.0f,
0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 1.0f,
0.0f, 0.0f, 1.0f,
0.0f, 0.0f, 1.0f,
0.0f, 0.0f, 1.0f,
1.0f, 1.0f, 1.0f,
1.0f, 0.0f, 1.0f,
1.0f, 0.0f, 1.0f,
0.0f, 0.0f, 0.0f,
};
byteBuffer = ByteBuffer.allocateDirect(colors.length * 4);
byteBuffer.order(ByteOrder.nativeOrder());
FloatBuffer colorsBuffer = byteBuffer.asFloatBuffer();
colorsBuffer.put(colors);
colorsBuffer.position(0);
Log.d("FOO", "allocating floats GPU buffer");
vbos = new int[2];
GLES20.glGenBuffers(2, vbos, 0);
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vbos[0]);
GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, positionsBuffer.capacity() * 4, positionsBuffer, GLES20.GL_STATIC_DRAW);
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vbos[1]);
GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, colorsBuffer.capacity() * 4, colorsBuffer, GLES20.GL_STATIC_DRAW);
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
// Connectivity
short[] faces = {
0, 1, 2,
2, 1, 3,
4, 5, 6,
6, 5, 7,
8, 9, 10,
10, 9, 11,
12, 13, 14,
14, 13, 15,
};
Log.d("FOO", "allocating indices native buffer");
byteBuffer = ByteBuffer.allocateDirect(faces.length * 2);
byteBuffer.order(ByteOrder.nativeOrder());
ShortBuffer indicesBuffer = byteBuffer.asShortBuffer();
indicesBuffer.put(faces);
indicesBuffer.position(0);
ibos = new int[1];
GLES20.glGenBuffers(1, ibos, 0);
GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, ibos[0]);
GLES20.glBufferData(GLES20.GL_ELEMENT_ARRAY_BUFFER, indicesBuffer.capacity() * 2, indicesBuffer, GLES20.GL_STATIC_DRAW);
GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, 0);
}
private void makeShaderProgram() {
String vertexShaderCode =
"uniform mat4 rotation;" +
"uniform mat4 projectionLookAt;" +
"attribute vec3 position;" +
"attribute vec3 color;" +
"varying vec3 fcolor;" +
"void main() {" +
" gl_Position = projectionLookAt * rotation * vec4(position, 1.0);" +
" fcolor = color;" +
"}";
String fragmentShaderCode =
"precision mediump float;" +
"varying vec3 fcolor;" +
"void main() {" +
" gl_FragColor = vec4(fcolor, 1.0);" +
"}";
int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode);
int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode);
shaderProgram = GLES20.glCreateProgram();
GLES20.glAttachShader(shaderProgram, vertexShader);
GLES20.glAttachShader(shaderProgram, fragmentShader);
GLES20.glLinkProgram(shaderProgram);
}
private static int loadShader(int type, String shaderCode) {
int shader = GLES20.glCreateShader(type);
GLES20.glShaderSource(shader, shaderCode);
GLES20.glCompileShader(shader);
return shader;
}
@Override
public void onSurfaceChanged(GL10 gl,
int width,
int height) {
GLES20.glViewport(0, 0, width, height);
float ratio = (float) width / height;
float[] projection = new float[16];
Matrix.frustumM(projection, 0, -ratio, ratio, -1, 1, 2.1f, 10.0f);
float[] lookAt = new float[16];
Matrix.setLookAtM(lookAt, 0, 0.0f, 0.0f, -3.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f);
Matrix.multiplyMM(projectionLookAt, 0, projection, 0, lookAt, 0);
updateUniforms();
}
private void updateUniforms() {
GLES20.glUseProgram(shaderProgram);
GLES20.glUniformMatrix4fv(rotationHandle, 1, false, rotation, 0);
GLES20.glUniformMatrix4fv(projectionLookAtHandle, 1, false, projectionLookAt, 0);
GLES20.glUseProgram(0);
}
private void updateHandles() {
GLES20.glUseProgram(shaderProgram);
positionHandle = GLES20.glGetAttribLocation(shaderProgram, "position");
colorHandle = GLES20.glGetAttribLocation(shaderProgram, "color");
projectionLookAtHandle = GLES20.glGetUniformLocation(shaderProgram, "projectionLookAt");
rotationHandle = GLES20.glGetUniformLocation(shaderProgram, "rotation");
GLES20.glUseProgram(0);
}
@Override
public void onDrawFrame(GL10 gl) {
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
GLES20.glUseProgram(shaderProgram);
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vbos[0]);
GLES20.glVertexAttribPointer(positionHandle, 3, GLES20.GL_FLOAT, false, 0, 0);
GLES20.glEnableVertexAttribArray(positionHandle);
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vbos[1]);
GLES20.glVertexAttribPointer(colorHandle, 3, GLES20.GL_FLOAT, false, 0, 0);
GLES20.glEnableVertexAttribArray(colorHandle);
GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, ibos[0]);
GLES20.glDrawElements(GLES20.GL_TRIANGLES, 24, GLES20.GL_UNSIGNED_SHORT, 0);
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, 0);
GLES20.glUseProgram(0);
}
public void rotate(float degrees) {
rotationX += degrees;
Matrix.setRotateM(rotation, 0, rotationX, 0.0f, 1.0f, 0.0f);
updateUniforms();
}
}