From fe9197654cd203100c12aa87787371304e64ec06 Mon Sep 17 00:00:00 2001 From: Learn OpenGL ES Date: Sat, 1 Oct 2011 23:44:06 -0400 Subject: [PATCH] Added lesson 5: An Intro to Blending * Updated version to 1.0.4, version code 5. * Updated readme.txt * Added a cube builder function. --- .../AndroidManifest.xml | 5 +- android/AndroidOpenGLESLessons/README.TXT | 4 +- .../res/drawable-hdpi/ic_lesson_five.png | Bin 0 -> 10372 bytes .../res/raw/color_fragment_shader.glsl | 11 + .../res/raw/color_vertex_shader.glsl | 17 + .../res/values/strings.xml | 4 +- .../android/TableOfContents.java | 12 +- .../android/common/ShapeBuilder.java | 81 +++++ .../android/lesson4/LessonFourRenderer.java | 2 +- .../android/lesson5/LessonFiveActivity.java | 76 +++++ .../lesson5/LessonFiveGLSurfaceView.java | 50 +++ .../android/lesson5/LessonFiveRenderer.java | 310 ++++++++++++++++++ 12 files changed, 567 insertions(+), 5 deletions(-) create mode 100644 android/AndroidOpenGLESLessons/res/drawable-hdpi/ic_lesson_five.png create mode 100644 android/AndroidOpenGLESLessons/res/raw/color_fragment_shader.glsl create mode 100644 android/AndroidOpenGLESLessons/res/raw/color_vertex_shader.glsl create mode 100644 android/AndroidOpenGLESLessons/src/com/learnopengles/android/common/ShapeBuilder.java create mode 100644 android/AndroidOpenGLESLessons/src/com/learnopengles/android/lesson5/LessonFiveActivity.java create mode 100644 android/AndroidOpenGLESLessons/src/com/learnopengles/android/lesson5/LessonFiveGLSurfaceView.java create mode 100644 android/AndroidOpenGLESLessons/src/com/learnopengles/android/lesson5/LessonFiveRenderer.java diff --git a/android/AndroidOpenGLESLessons/AndroidManifest.xml b/android/AndroidOpenGLESLessons/AndroidManifest.xml index 924a8f2..b16f886 100644 --- a/android/AndroidOpenGLESLessons/AndroidManifest.xml +++ b/android/AndroidOpenGLESLessons/AndroidManifest.xml @@ -2,7 +2,7 @@ + android:versionCode="5" android:versionName="1.0.4"> @@ -35,5 +35,8 @@ + \ No newline at end of file diff --git a/android/AndroidOpenGLESLessons/README.TXT b/android/AndroidOpenGLESLessons/README.TXT index 308b3ab..783eafd 100644 --- a/android/AndroidOpenGLESLessons/README.TXT +++ b/android/AndroidOpenGLESLessons/README.TXT @@ -1,3 +1,5 @@ This project is a repository for the lessons and tutorials over at http://www.learnopengles.com/ -The compiled app can be downloaded from the Android market at https://market.android.com/details?id=com.learnopengles.android \ No newline at end of file +The compiled app can be downloaded from the Android market at https://market.android.com/details?id=com.learnopengles.android + +The WebGL lessons can be viewed at http://www.learnopengles.com/ \ No newline at end of file diff --git a/android/AndroidOpenGLESLessons/res/drawable-hdpi/ic_lesson_five.png b/android/AndroidOpenGLESLessons/res/drawable-hdpi/ic_lesson_five.png new file mode 100644 index 0000000000000000000000000000000000000000..79582893435c3ff97e6f24f92a3785161308abdf GIT binary patch literal 10372 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 iYa+?N->~2PThis builds on lesson two by adding per-pixel lighting. Lesson Four: Basic Texturing This builds on lesson three by adding basic texturing. - + Lesson Five: An Intro to Blending + This lesson takes a look at the basics of OpenGL blending. + Tap the screen to switch between blended and normal mode. diff --git a/android/AndroidOpenGLESLessons/src/com/learnopengles/android/TableOfContents.java b/android/AndroidOpenGLESLessons/src/com/learnopengles/android/TableOfContents.java index f89d54b..ec450e1 100644 --- a/android/AndroidOpenGLESLessons/src/com/learnopengles/android/TableOfContents.java +++ b/android/AndroidOpenGLESLessons/src/com/learnopengles/android/TableOfContents.java @@ -11,13 +11,14 @@ import android.os.Bundle; import android.view.View; import android.widget.AdapterView; -import android.widget.SimpleAdapter; import android.widget.AdapterView.OnItemClickListener; +import android.widget.SimpleAdapter; import com.learnopengles.android.lesson1.LessonOneActivity; import com.learnopengles.android.lesson2.LessonTwoActivity; import com.learnopengles.android.lesson3.LessonThreeActivity; import com.learnopengles.android.lesson4.LessonFourActivity; +import com.learnopengles.android.lesson5.LessonFiveActivity; public class TableOfContents extends ListActivity { @@ -74,6 +75,15 @@ public void onCreate(Bundle savedInstanceState) activityMapping.put(i++, LessonFourActivity.class); } + { + final Map item = new HashMap(); + item.put(ITEM_IMAGE, R.drawable.ic_lesson_five); + item.put(ITEM_TITLE, getText(R.string.lesson_five)); + item.put(ITEM_SUBTITLE, getText(R.string.lesson_five_subtitle)); + data.add(item); + activityMapping.put(i++, LessonFiveActivity.class); + } + final SimpleAdapter dataAdapter = new SimpleAdapter(this, data, R.layout.toc_item, new String[] {ITEM_IMAGE, ITEM_TITLE, ITEM_SUBTITLE}, new int[] {R.id.Image, R.id.Title, R.id.SubTitle}); setListAdapter(dataAdapter); diff --git a/android/AndroidOpenGLESLessons/src/com/learnopengles/android/common/ShapeBuilder.java b/android/AndroidOpenGLESLessons/src/com/learnopengles/android/common/ShapeBuilder.java new file mode 100644 index 0000000..fc04448 --- /dev/null +++ b/android/AndroidOpenGLESLessons/src/com/learnopengles/android/common/ShapeBuilder.java @@ -0,0 +1,81 @@ +package com.learnopengles.android.common; + +public class ShapeBuilder +{ + public static float[] generateCubeData(float[] point1, + float[] point2, + float[] point3, + float[] point4, + float[] point5, + float[] point6, + float[] point7, + float[] point8, + int elementsPerPoint) + { + // Given a cube with the points defined as follows: + // front left top, front right top, front left bottom, front right bottom, + // back left top, back right top, back left bottom, back right bottom, + // return an array of 6 sides, 2 triangles per side, 3 vertices per triangle, and 4 floats per vertex. + final int FRONT = 0; + final int RIGHT = 1; + final int BACK = 2; + final int LEFT = 3; + final int TOP = 4; + final int BOTTOM = 5; + + final int size = elementsPerPoint * 6 * 6; + final float[] cubeData = new float[size]; + + for (int face = 0; face < 6; face ++) + { + // Relative to the side, p1 = top left, p2 = top right, p3 = bottom left, p4 = bottom right + final float[] p1, p2, p3, p4; + + // Select the points for this face + if (face == FRONT) + { + p1 = point1; p2 = point2; p3 = point3; p4 = point4; + } + else if (face == RIGHT) + { + p1 = point2; p2 = point6; p3 = point4; p4 = point8; + } + else if (face == BACK) + { + p1 = point6; p2 = point5; p3 = point8; p4 = point7; + } + else if (face == LEFT) + { + p1 = point5; p2 = point1; p3 = point7; p4 = point3; + } + else if (face == TOP) + { + p1 = point5; p2 = point6; p3 = point1; p4 = point2; + } + else // if (side == BOTTOM) + { + p1 = point8; p2 = point7; p3 = point4; p4 = point3; + } + + // In OpenGL counter-clockwise winding is default. This means that when we look at a triangle, + // if the points are counter-clockwise we are looking at the "front". If not we are looking at + // the back. OpenGL has an optimization where all back-facing triangles are culled, since they + // usually represent the backside of an object and aren't visible anyways. + + // Build the triangles + // 1---3,6 + // | / | + // 2,4--5 + int offset = face * elementsPerPoint * 6; + + for (int i = 0; i < elementsPerPoint; i++) { cubeData[offset++] = p1[i]; } + for (int i = 0; i < elementsPerPoint; i++) { cubeData[offset++] = p3[i]; } + for (int i = 0; i < elementsPerPoint; i++) { cubeData[offset++] = p2[i]; } + for (int i = 0; i < elementsPerPoint; i++) { cubeData[offset++] = p3[i]; } + for (int i = 0; i < elementsPerPoint; i++) { cubeData[offset++] = p4[i]; } + for (int i = 0; i < elementsPerPoint; i++) { cubeData[offset++] = p2[i]; } + } + + return cubeData; + } +} diff --git a/android/AndroidOpenGLESLessons/src/com/learnopengles/android/lesson4/LessonFourRenderer.java b/android/AndroidOpenGLESLessons/src/com/learnopengles/android/lesson4/LessonFourRenderer.java index 63af7a0..4d6605e 100644 --- a/android/AndroidOpenGLESLessons/src/com/learnopengles/android/lesson4/LessonFourRenderer.java +++ b/android/AndroidOpenGLESLessons/src/com/learnopengles/android/lesson4/LessonFourRenderer.java @@ -25,7 +25,7 @@ public class LessonFourRenderer implements GLSurfaceView.Renderer { /** Used for debug logs. */ - private static final String TAG = "LessonTwoRenderer"; + private static final String TAG = "LessonFourRenderer"; private final Context mActivityContext; diff --git a/android/AndroidOpenGLESLessons/src/com/learnopengles/android/lesson5/LessonFiveActivity.java b/android/AndroidOpenGLESLessons/src/com/learnopengles/android/lesson5/LessonFiveActivity.java new file mode 100644 index 0000000..3ff2908 --- /dev/null +++ b/android/AndroidOpenGLESLessons/src/com/learnopengles/android/lesson5/LessonFiveActivity.java @@ -0,0 +1,76 @@ +package com.learnopengles.android.lesson5; + +import android.app.Activity; +import android.app.ActivityManager; +import android.content.Context; +import android.content.pm.ConfigurationInfo; +import android.os.Bundle; +import android.widget.Toast; + +import com.learnopengles.android.R; + +public class LessonFiveActivity extends Activity +{ + /** Hold a reference to our GLSurfaceView */ + private LessonFiveGLSurfaceView mGLSurfaceView; + + private static final String SHOWED_TOAST = "showed_toast"; + + @Override + public void onCreate(Bundle savedInstanceState) + { + super.onCreate(savedInstanceState); + + mGLSurfaceView = new LessonFiveGLSurfaceView(this); + + // Check if the system supports OpenGL ES 2.0. + final ActivityManager activityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE); + final ConfigurationInfo configurationInfo = activityManager.getDeviceConfigurationInfo(); + final boolean supportsEs2 = configurationInfo.reqGlEsVersion >= 0x20000; + + if (supportsEs2) + { + // Request an OpenGL ES 2.0 compatible context. + mGLSurfaceView.setEGLContextClientVersion(2); + + // Set the renderer to our demo renderer, defined below. + mGLSurfaceView.setRenderer(new LessonFiveRenderer(this)); + } + else + { + // This is where you could create an OpenGL ES 1.x compatible + // renderer if you wanted to support both ES 1 and ES 2. + return; + } + + setContentView(mGLSurfaceView); + + // Show a short help message to the user. + if (savedInstanceState == null || !savedInstanceState.getBoolean(SHOWED_TOAST, false)) + { + Toast.makeText(this, R.string.lesson_five_startup_toast, Toast.LENGTH_SHORT).show(); + } + } + + @Override + protected void onResume() + { + // The activity must call the GL surface view's onResume() on activity onResume(). + super.onResume(); + mGLSurfaceView.onResume(); + } + + @Override + protected void onPause() + { + // The activity must call the GL surface view's onPause() on activity onPause(). + super.onPause(); + mGLSurfaceView.onPause(); + } + + @Override + protected void onSaveInstanceState (Bundle outState) + { + outState.putBoolean(SHOWED_TOAST, true); + } +} \ No newline at end of file diff --git a/android/AndroidOpenGLESLessons/src/com/learnopengles/android/lesson5/LessonFiveGLSurfaceView.java b/android/AndroidOpenGLESLessons/src/com/learnopengles/android/lesson5/LessonFiveGLSurfaceView.java new file mode 100644 index 0000000..8b12516 --- /dev/null +++ b/android/AndroidOpenGLESLessons/src/com/learnopengles/android/lesson5/LessonFiveGLSurfaceView.java @@ -0,0 +1,50 @@ +package com.learnopengles.android.lesson5; + +import android.content.Context; +import android.opengl.GLSurfaceView; +import android.view.MotionEvent; + +public class LessonFiveGLSurfaceView extends GLSurfaceView +{ + private LessonFiveRenderer mRenderer; + + public LessonFiveGLSurfaceView(Context context) + { + super(context); + } + + @Override + public boolean onTouchEvent(MotionEvent event) + { + if (event != null) + { + if (event.getAction() == MotionEvent.ACTION_DOWN) + { + if (mRenderer != null) + { + // Ensure we call switchMode() on the OpenGL thread. + // queueEvent() is a method of GLSurfaceView that will do this for us. + queueEvent(new Runnable() + { + @Override + public void run() + { + mRenderer.switchMode(); + } + }); + + return true; + } + } + } + + return super.onTouchEvent(event); + } + + // Hides superclass method. + public void setRenderer(LessonFiveRenderer renderer) + { + mRenderer = renderer; + super.setRenderer(renderer); + } +} diff --git a/android/AndroidOpenGLESLessons/src/com/learnopengles/android/lesson5/LessonFiveRenderer.java b/android/AndroidOpenGLESLessons/src/com/learnopengles/android/lesson5/LessonFiveRenderer.java new file mode 100644 index 0000000..0c4a3dd --- /dev/null +++ b/android/AndroidOpenGLESLessons/src/com/learnopengles/android/lesson5/LessonFiveRenderer.java @@ -0,0 +1,310 @@ +package com.learnopengles.android.lesson5; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.FloatBuffer; + +import javax.microedition.khronos.egl.EGLConfig; +import javax.microedition.khronos.opengles.GL10; + +import android.content.Context; +import android.opengl.GLES20; +import android.opengl.GLSurfaceView; +import android.opengl.Matrix; +import android.os.Handler; +import android.os.SystemClock; + +import com.learnopengles.android.R; +import com.learnopengles.android.common.RawResourceReader; +import com.learnopengles.android.common.ShaderHelper; +import com.learnopengles.android.common.ShapeBuilder; + +/** + * This class implements our custom renderer. Note that the GL10 parameter passed in is unused for OpenGL ES 2.0 + * renderers -- the static class GLES20 is used instead. + */ +public class LessonFiveRenderer implements GLSurfaceView.Renderer +{ + /** Used for debug logs. */ + private static final String TAG = "LessonFiveRenderer"; + + private final Context mActivityContext; + + /** + * Store the model matrix. This matrix is used to move models from object space (where each model can be thought + * of being located at the center of the universe) to world space. + */ + private float[] mModelMatrix = new float[16]; + + /** + * Store the view matrix. This can be thought of as our camera. This matrix transforms world space to eye space; + * it positions things relative to our eye. + */ + private float[] mViewMatrix = new float[16]; + + /** Store the projection matrix. This is used to project the scene onto a 2D viewport. */ + private float[] mProjectionMatrix = new float[16]; + + /** Allocate storage for the final combined matrix. This will be passed into the shader program. */ + private float[] mMVPMatrix = new float[16]; + + /** Store our model data in a float buffer. */ + private final FloatBuffer mCubePositions; + private final FloatBuffer mCubeColors; + + /** This will be used to pass in the transformation matrix. */ + private int mMVPMatrixHandle; + + /** This will be used to pass in model position information. */ + private int mPositionHandle; + + /** This will be used to pass in model color information. */ + private int mColorHandle; + + /** How many bytes per float. */ + private final int mBytesPerFloat = 4; + + /** Size of the position data in elements. */ + private final int mPositionDataSize = 3; + + /** Size of the color data in elements. */ + private final int mColorDataSize = 4; + + /** This is a handle to our cube shading program. */ + private int mProgramHandle; + + /** This will be used to switch between blending mode and regular mode. */ + private boolean mBlending = true; + + /** + * Initialize the model data. + */ + public LessonFiveRenderer(final Context activityContext) + { + mActivityContext = activityContext; + + // Define points for a cube. + // X, Y, Z + final float[] p1p = {-1.0f, 1.0f, 1.0f}; + final float[] p2p = {1.0f, 1.0f, 1.0f}; + final float[] p3p = {-1.0f, -1.0f, 1.0f}; + final float[] p4p = {1.0f, -1.0f, 1.0f}; + final float[] p5p = {-1.0f, 1.0f, -1.0f}; + final float[] p6p = {1.0f, 1.0f, -1.0f}; + final float[] p7p = {-1.0f, -1.0f, -1.0f}; + final float[] p8p = {1.0f, -1.0f, -1.0f}; + + final float[] cubePositionData = ShapeBuilder.generateCubeData(p1p, p2p, p3p, p4p, p5p, p6p, p7p, p8p, p1p.length); + + // Points of the cube: color information + // R, G, B, A + final float[] p1c = {1.0f, 0.0f, 0.0f, 1.0f}; // red + final float[] p2c = {0.0f, 1.0f, 0.0f, 1.0f}; // green + final float[] p3c = {0.0f, 0.0f, 1.0f, 1.0f}; // blue + final float[] p4c = {0.33f, 0.33f, 0.33f, 1.0f}; // dark gray + final float[] p5c = {1.0f, 1.0f, 0.0f, 1.0f}; // yellow + final float[] p6c = {0.0f, 1.0f, 1.0f, 1.0f}; // cyan + final float[] p7c = {1.0f, 0.0f, 1.0f, 1.0f}; // magenta + final float[] p8c = {0.0f, 0.0f, 0.0f, 1.0f}; // black + + final float[] cubeColorData = ShapeBuilder.generateCubeData(p1c, p2c, p3c, p4c, p5c, p6c, p7c, p8c, p1c.length); + + // Initialize the buffers. + mCubePositions = ByteBuffer.allocateDirect(cubePositionData.length * mBytesPerFloat) + .order(ByteOrder.nativeOrder()).asFloatBuffer(); + mCubePositions.put(cubePositionData).position(0); + + mCubeColors = ByteBuffer.allocateDirect(cubeColorData.length * mBytesPerFloat) + .order(ByteOrder.nativeOrder()).asFloatBuffer(); + mCubeColors.put(cubeColorData).position(0); + } + + protected String getVertexShader() + { + return RawResourceReader.readTextFileFromRawResource(mActivityContext, R.raw.color_vertex_shader); + } + + protected String getFragmentShader() + { + return RawResourceReader.readTextFileFromRawResource(mActivityContext, R.raw.color_fragment_shader); + } + + public void switchMode() + { + mBlending = !mBlending; + + if (mBlending) + { + // No culling of back faces + GLES20.glDisable(GLES20.GL_CULL_FACE); + + // No depth testing + GLES20.glDisable(GLES20.GL_DEPTH_TEST); + + // Enable blending + GLES20.glEnable(GLES20.GL_BLEND); + GLES20.glBlendFunc(GLES20.GL_ONE, GLES20.GL_ONE); + } + else + { + // Cull back faces + GLES20.glEnable(GLES20.GL_CULL_FACE); + + // Enable depth testing + GLES20.glEnable(GLES20.GL_DEPTH_TEST); + + // Disable blending + GLES20.glDisable(GLES20.GL_BLEND); + } + } + + @Override + public void onSurfaceCreated(GL10 glUnused, EGLConfig config) + { + // Set the background clear color to black. + GLES20.glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + + // No culling of back faces + GLES20.glDisable(GLES20.GL_CULL_FACE); + + // No depth testing + GLES20.glDisable(GLES20.GL_DEPTH_TEST); + + // Enable blending + GLES20.glEnable(GLES20.GL_BLEND); + GLES20.glBlendFunc(GLES20.GL_ONE, GLES20.GL_ONE); +// GLES20.glBlendEquation(GLES20.GL_FUNC_ADD); + + // Position the eye in front of the origin. + final float eyeX = 0.0f; + final float eyeY = 0.0f; + final float eyeZ = -0.5f; + + // We are looking toward the distance + final float lookX = 0.0f; + final float lookY = 0.0f; + final float lookZ = -5.0f; + + // Set our up vector. This is where our head would be pointing were we holding the camera. + 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. + // NOTE: In OpenGL 1, a ModelView matrix is used, which is a combination of a model and + // view matrix. In OpenGL 2, we can keep track of these matrices separately if we choose. + Matrix.setLookAtM(mViewMatrix, 0, eyeX, eyeY, eyeZ, lookX, lookY, lookZ, upX, upY, upZ); + + final String vertexShader = getVertexShader(); + final String fragmentShader = getFragmentShader(); + + final int vertexShaderHandle = ShaderHelper.compileShader(GLES20.GL_VERTEX_SHADER, vertexShader); + final int fragmentShaderHandle = ShaderHelper.compileShader(GLES20.GL_FRAGMENT_SHADER, fragmentShader); + + mProgramHandle = ShaderHelper.createAndLinkProgram(vertexShaderHandle, fragmentShaderHandle, + new String[] {"a_Position", "a_Color"}); + } + + @Override + public void onSurfaceChanged(GL10 glUnused, int width, int height) + { + // Set the OpenGL viewport to the same size as the surface. + GLES20.glViewport(0, 0, width, height); + + // Create a new perspective projection matrix. The height will stay the same + // while the width will vary as per aspect ratio. + final float ratio = (float) width / height; + final float left = -ratio; + final float right = ratio; + final float bottom = -1.0f; + final float top = 1.0f; + final float near = 1.0f; + final float far = 10.0f; + + Matrix.frustumM(mProjectionMatrix, 0, left, right, bottom, top, near, far); + } + + @Override + public void onDrawFrame(GL10 glUnused) + { + if (mBlending) + { + GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); + } + else + { + GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT); + } + + // Do a complete rotation every 10 seconds. + long time = SystemClock.uptimeMillis() % 10000L; + float angleInDegrees = (360.0f / 10000.0f) * ((int) time); + + // Set our program + GLES20.glUseProgram(mProgramHandle); + + // Set program handles for cube drawing. + mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgramHandle, "u_MVPMatrix"); + mPositionHandle = GLES20.glGetAttribLocation(mProgramHandle, "a_Position"); + mColorHandle = GLES20.glGetAttribLocation(mProgramHandle, "a_Color"); + + // Draw some cubes. + Matrix.setIdentityM(mModelMatrix, 0); + Matrix.translateM(mModelMatrix, 0, 4.0f, 0.0f, -7.0f); + Matrix.rotateM(mModelMatrix, 0, angleInDegrees, 1.0f, 0.0f, 0.0f); + drawCube(); + + Matrix.setIdentityM(mModelMatrix, 0); + Matrix.translateM(mModelMatrix, 0, -4.0f, 0.0f, -7.0f); + Matrix.rotateM(mModelMatrix, 0, angleInDegrees, 0.0f, 1.0f, 0.0f); + drawCube(); + + Matrix.setIdentityM(mModelMatrix, 0); + Matrix.translateM(mModelMatrix, 0, 0.0f, 4.0f, -7.0f); + Matrix.rotateM(mModelMatrix, 0, angleInDegrees, 0.0f, 0.0f, 1.0f); + drawCube(); + + Matrix.setIdentityM(mModelMatrix, 0); + Matrix.translateM(mModelMatrix, 0, 0.0f, -4.0f, -7.0f); + drawCube(); + + Matrix.setIdentityM(mModelMatrix, 0); + Matrix.translateM(mModelMatrix, 0, 0.0f, 0.0f, -5.0f); + Matrix.rotateM(mModelMatrix, 0, angleInDegrees, 1.0f, 1.0f, 0.0f); + drawCube(); + } + + /** + * Draws a cube. + */ + private void drawCube() + { + // Pass in the position information + mCubePositions.position(0); + GLES20.glVertexAttribPointer(mPositionHandle, mPositionDataSize, GLES20.GL_FLOAT, false, + 0, mCubePositions); + + GLES20.glEnableVertexAttribArray(mPositionHandle); + + // Pass in the color information + mCubeColors.position(0); + GLES20.glVertexAttribPointer(mColorHandle, mColorDataSize, GLES20.GL_FLOAT, false, + 0, mCubeColors); + + GLES20.glEnableVertexAttribArray(mColorHandle); + + // This multiplies the view matrix by the model matrix, and stores the result in the MVP matrix + // (which currently contains model * view). + Matrix.multiplyMM(mMVPMatrix, 0, mViewMatrix, 0, mModelMatrix, 0); + + // This multiplies the modelview matrix by the projection matrix, and stores the result in the MVP matrix + // (which now contains model * view * projection). + Matrix.multiplyMM(mMVPMatrix, 0, mProjectionMatrix, 0, mMVPMatrix, 0); + + // Pass in the combined matrix. + GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mMVPMatrix, 0); + + // Draw the cube. + GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, 36); + } +}