This is where all the really interesting stuff happens. Once again these are the methods we saw earlier, presented as the full class in one place.
1 package com.vizit; 2 3 import java.nio.ByteBuffer; 4 import java.nio.ByteOrder; 5 import javax.microedition.khronos.egl.EGLConfig; 6 import java.nio.FloatBuffer; 7 import javax.microedition.khronos.opengles.GL10; 8 import android.opengl.GLES20; 9 import android.opengl.GLSurfaceView; 10 import java.nio.IntBuffer; 11 import android.util.Log; 12 import android.opengl.Matrix; 13 import android.graphics.PointF; 14 15 /** 16 * Render a simple triangle using the simplest possible OpenGL ES 2. 17 */ 18 public class TriangleRenderer 19 implements GLSurfaceView.Renderer 20 { 21 private final String appName = "ESProjection"; 22 23 // Vertex buffer for the second triangle. 24 private int anotherBuffer; 25 /** How many bytes per float. */ 26 private final int bytesPerFloat = 4; 27 /** R, G, B, A for color at each vertex. */ 28 private final int colorElementSize = 4; 29 private float[] moreVertices; 30 /** Number of vertex elements. */ 31 private final int nElements = 3; 32 /** Each position element has three values, x,y, and z coordinates. */ 33 private final int positionElementSize = 3; 34 /** Offset of the position data. */ 35 private final int positionOffset = 0; 36 private int program; 37 private float[] projectionMatrix = new float[16]; 38 /** Translation for the second triangle has floating point x and y components. */ 39 private PointF translation = new PointF(); 40 // Vertex buffer for the first triangle. 41 private int vertexBuffer; 42 private float[] vertices; 43 /** Translate from world space to the view. */ 44 private float[] viewMatrix; 45 46 47 public TriangleRenderer() 48 { 49 vertices = new float[] { 50 // Vetex coordinate 51 -150.0f, -150.0f, -1.0f, 52 // Vertex color 53 1.0f, 0.0f, 0.0f, 1.0f, 54 150.0f, -150.0f, -1.0f, 55 0.0f, 1.0f, 0.0f, 1.0f, 56 0.0f, 150.0f, -1.0f, 57 0.0f, 0.0f, 1.0f, 1.0f 58 }; 59 60 moreVertices = new float[] { 61 -150.0f, -150.0f, -2.0f, 62 0.0f, 0.0f, 1.0f, 1.0f, 63 150.0f, -150.0f, -2.0f, 64 0.0f, 1.0f, 0.0f, 1.0f, 65 0.0f, 150.0f, -2.0f, 66 1.0f, 0.0f, 0.0f, 1.0f 67 }; 68 69 } 70 71 protected int compileShader(String shaderSource, int shaderType) 72 { 73 IntBuffer logStatus; 74 int shader; 75 String compileLog; 76 int[] compileStatus; 77 78 shader = GLES20.glCreateShader(shaderType); 79 // Pass in the shader source. 80 GLES20.glShaderSource(shader, shaderSource); 81 // Compile the shader. 82 GLES20.glCompileShader(shader); 83 84 // Get the compilation status. 85 compileStatus = new int[1]; 86 GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compileStatus, 0); 87 88 // Error compile status, get the relevant log. 89 if (compileStatus[0] == 0) 90 { 91 logStatus = ByteBuffer.allocateDirect(4).order(ByteOrder.nativeOrder()).asIntBuffer(); 92 GLES20.glGetShaderiv(shader, GLES20.GL_INFO_LOG_LENGTH, logStatus); 93 if (logStatus.get(0) > 0) 94 { 95 compileLog = GLES20.glGetShaderInfoLog(shader); 96 Log.d(appName, "Shader compilation failed with, " + compileLog); 97 } 98 else 99 { 100 // Workaround for issue 9953, where GL_INFO_LOG_LENGTH is zero. 101 Log.d(appName, "Shader compilation failed for an unknown reason."); 102 } 103 GLES20.glDeleteShader(shader); 104 shader = 0; 105 } 106 107 return shader; 108 } 109 110 protected int createProgram(int vertexShader, int fragmentShader) 111 { 112 String loaderLog; 113 IntBuffer logStatus; 114 int program; 115 116 program = GLES20.glCreateProgram(); 117 118 GLES20.glAttachShader(program, vertexShader); 119 120 // Bind the fragment shader to the program. 121 GLES20.glAttachShader(program, fragmentShader); 122 123 GLES20.glLinkProgram(program); 124 125 // Get the link status. 126 final int[] linkStatus = new int[1]; 127 GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0); 128 129 // If the link failed, delete the program. 130 if (linkStatus[0] == 0) 131 { 132 logStatus = ByteBuffer.allocateDirect(4).order(ByteOrder.nativeOrder()).asIntBuffer(); 133 GLES20.glGetProgramiv(program, GLES20.GL_INFO_LOG_LENGTH, logStatus); 134 if (logStatus.get(0) > 0) 135 { 136 loaderLog = GLES20.glGetProgramInfoLog(program); 137 Log.d(appName, "Program linking failed with, " + loaderLog); 138 } 139 else 140 { 141 // Workaround for issue 9953, where GL_INFO_LOG_LENGTH is zero. 142 Log.d(appName, "Program linking failed for an unknown reason."); 143 } 144 GLES20.glDeleteProgram(program); 145 program = 0; 146 } 147 148 GLES20.glUseProgram(program); 149 150 return program; 151 } 152 153 public float[] generateViewMatrix() 154 { 155 final float[] viewMatrix = new float[16]; 156 157 // Position the eye behind the origin. 158 final float eyeX = 0.0f; 159 final float eyeY = 0.0f; 160 final float eyeZ = 0.0f; 161 162 // We are looking in the -z direction 163 final float lookX = 0.0f; 164 final float lookY = 0.0f; 165 final float lookZ = -1.0f; 166 167 // Up is the y axis. 168 final float upX = 0.0f; 169 final float upY = 1.0f; 170 final float upZ = 0.0f; 171 172 // Set the view matrix. This matrix can be said to represent the camera position. 173 Matrix.setLookAtM(viewMatrix, 0, eyeX, eyeY, eyeZ, lookX, lookY, lookZ, upX, upY, upZ); 174 175 return viewMatrix; 176 } 177 178 /** 179 * Load vertex data into a buffer so that it can be read into vertex shader attributes. 180 * 181 * @param vertexData An array of floats containing per vertex data. 182 */ 183 protected int createBuffer(float[] vertexData) 184 { 185 int[] vertexBuffer = new int[1]; 186 187 /** Store our model data in a float buffer. */ 188 FloatBuffer vertexBufferData; 189 190 // The OpenGL Driver expects floating point data in native byte order. 191 vertexBufferData = ByteBuffer.allocateDirect(vertexData.length * bytesPerFloat) 192 .order(ByteOrder.nativeOrder()).asFloatBuffer(); 193 194 vertexBufferData.put(vertexData).position(0); 195 196 // Generate a single buffer handle into element 0 of the array. 197 GLES20.glGenBuffers(1, vertexBuffer, 0); 198 199 // Binding an object in Open GL creates it, and makes it the target of subsequent manipulations. 200 GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vertexBuffer[0]); 201 202 // loads the current buffer, the vertexBuffer found above, with the vertex data. 203 GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, vertexBufferData.capacity() * bytesPerFloat, 204 vertexBufferData, GLES20.GL_STATIC_DRAW); 205 206 return vertexBuffer[0]; 207 } 208 209 @Override 210 public void onSurfaceCreated(GL10 glUnused, EGLConfig config) 211 { 212 // Enable depth tests to draw only nearer objects, less depth. 213 GLES20.glEnable(GLES20.GL_DEPTH_TEST); 214 GLES20.glDepthFunc(GLES20.GL_LEQUAL); 215 GLES20.glDepthMask(true); 216 217 final String vertexShaderSource = "attribute vec3 position;" 218 + "attribute vec4 color;" 219 + "uniform mat4 modelViewMatrix;" 220 + "uniform mat4 projectionMatrix;" 221 + "" 222 + "varying vec4 vColor;" 223 + "" 224 + "void main()" 225 + "{" 226 + " gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1);" 227 + " vColor = color;" 228 + "}"; 229 230 // The fragment shader can be thought of for now as doing per pixel computations. It is the 231 // fragment shader the colors each pixel in a 3d scene. 232 final String fragmentShaderSource = "precision mediump float;" 233 + "" 234 + "varying vec4 vColor;" 235 + "" 236 + "void main()" 237 + "{" 238 + " gl_FragColor = vColor;" 239 + "}"; 240 241 int vertexShader = compileShader(vertexShaderSource, GLES20.GL_VERTEX_SHADER); 242 int fragmentShader = compileShader(fragmentShaderSource, GLES20.GL_FRAGMENT_SHADER); 243 244 program = createProgram(vertexShader, fragmentShader); 245 246 vertexBuffer = createBuffer(vertices); 247 anotherBuffer = createBuffer(moreVertices); 248 249 viewMatrix = generateViewMatrix(); 250 251 // Load a projection matrix 252 // frustumM (float[] m, int offset, float left, float right, float bottom, float top, float near, float far) 253 Matrix.frustumM(projectionMatrix, 0, -150.0f, 150.0f, -150.0f, 150.0f, 1.0f, 100.0f); 254 } 255 256 @Override 257 public void onSurfaceChanged(GL10 glUnused, int width, int height) 258 { 259 // Resize our drawing surface to the new height and width. 260 GLES20.glViewport(0, 0, width, height); 261 } 262 263 protected void bindBuffer(int vertexBuffer, int program, String attribute, int size, int type, int stride, int offset) 264 { 265 int attributeLocation; 266 267 attributeLocation = GLES20.glGetAttribLocation(program, attribute); 268 269 // Binding an object makes it the target of subsequent manipulations. 270 GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vertexBuffer); 271 272 GLES20.glVertexAttribPointer(attributeLocation, size, type, false, stride, offset); 273 274 GLES20.glEnableVertexAttribArray(attributeLocation); 275 } 276 277 protected void bindMatrixUniform(int program, float[] matrix, String name) 278 { 279 int uniformLocation = GLES20.glGetUniformLocation(program, name); 280 GLES20.glUniformMatrix4fv(uniformLocation, 1, false, matrix, 0); 281 } 282 283 284 @Override 285 public void onDrawFrame(GL10 glUnused) 286 { 287 int stride; 288 289 GLES20.glClearColor(.9255f, .8941f, .8275f, 1.0f); 290 GLES20.glClear( GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT); 291 292 bindMatrixUniform(program, viewMatrix, "modelViewMatrix"); 293 bindMatrixUniform(program, projectionMatrix, "projectionMatrix"); 294 295 stride = bytesPerFloat*(colorElementSize + positionElementSize); 296 297 bindBuffer(vertexBuffer, program, "position", positionElementSize, 298 GLES20.GL_FLOAT, stride, positionOffset*bytesPerFloat); 299 bindBuffer(vertexBuffer, program, "color", colorElementSize, 300 GLES20.GL_FLOAT, stride, (positionOffset + positionElementSize)*bytesPerFloat); 301 302 GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, nElements); 303 304 // Take into account translations of the second triangle, if any. 305 float[] newModelView = new float[16]; 306 307 Matrix.translateM(newModelView, 0, viewMatrix, 0, translation.x, translation.y, 0); 308 bindMatrixUniform(program, newModelView, "modelViewMatrix"); 309 310 bindBuffer(anotherBuffer, program, "position", positionElementSize, 311 GLES20.GL_FLOAT, stride, positionOffset*bytesPerFloat); 312 bindBuffer(anotherBuffer, program, "color", colorElementSize, 313 GLES20.GL_FLOAT, stride, (positionOffset + positionElementSize)*bytesPerFloat); 314 315 316 GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, nElements); 317 } 318 319 public void motion(float deltaX, float deltaY) 320 { 321 translation.x += deltaX; 322 translation.y += deltaY; 323 } 324 }
This is significantly complex to render a couple of triangles onto the screen. The good news is that even for much more complex scenes it does not get much more complicated.
There are though two aspects we have left off: lighting and textures.