The TriangleRenderer Class

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 }

Notes

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.