summaryrefslogtreecommitdiffstats
path: root/docs/html/resources/tutorials/opengl/opengl-es20.jd
blob: 5cdc3430fd4166211fa6527a4b121bfc38d23c9f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
page.title=Hello OpenGL ES 1.0
parent.title=Tutorials
parent.link=../../browser.html?tag=tutorial
@jd:body


<div id="qv-wrapper">
  <div id="qv">
    <h2>In this document</h2>
    
    <ol>
      <li><a href="#creating">Creating an OpenGL ES 2.0 Application</a></li>
      <li>
        <a href="#drawing">Drawing Graphic Elements</a>
        <ol>
          <li><a href="#define-triangle">Defining a Triangle</a></li>
          <li><a href="#draw-triangle">Draw the Triangle</a></li>
        </ol>
      </li>
      <li><a href="#projection-and-views">Using Projection and Views</a></li>
      <li><a href="#motion">Adding Motion</a></li>
      <li><a href="#resources">Additional Resources</a></li>
      
    </ol>
    <h2 id="code-samples-list">Related Samples</h2>
    <ol>
      <li><a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/graphics/
index.html">API Demos - graphics</a></li>
      <li><a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/graphics/
GLES20Activity.html">OpenGL ES 2.0 Example</a></li>
    </ol>
    <h2>See also</h2>
    <ol>
      <li><a href="{@docRoot}guide/topics/graphics/opengl.html">3D with OpenGL</a></li>
      <li><a href="{@docRoot}resources/tutorials/opengl/opengl-es10.html">Hello OpenGL
ES 1.0</a></li>
    </ol>
    </div>
  </div>

<p>This tutorial shows you how to create a simple Android application that uses OpenGL ES 2.0 and
perform some basic operations using the OpenGL ES 2.0 API, including:</p>

<ul>
  <li>Creating an application using {@link android.opengl.GLSurfaceView} and {@link
android.opengl.GLSurfaceView.Renderer}</li>
  <li>Defining a graphic object and drawing it</li>
  <li>Defining and applying an projection and view</li>
  <li>Applying rotation to the drawn object</li>
</ul>

<p>This tutorial demonstrates use of the OpenGL ES 2.0 API. Beginning with Android 2.2 (API Level
8), the framework supports OpenGL ES 2.0.</p>

<p>Both the OpenGL ES 1.0 and the ES 1.1 API are supported by the Android framework since release
1.0 (API Level 1).For more information about compatibility for OpenGL versions and Android devices,
see the <a href="{@docRoot}guide/topics/graphics/opengl.html#compatibility">3D with OpenGL</a>
document.</p>


<h2 id="creating">Creating an OpenGL ES 1.0 Application</h2>

<p>OpenGL applications for Android have the same basic structure as other applications, however
OpenGL applications use {@link android.opengl.GLSurfaceView} where other, non-OpenGL
applications use the {@link android.view.View} or {@link android.view.SurfaceView} class.<p>
 
<p>To get started using OpenGL in your Android application, you must implement both a {@link
android.opengl.GLSurfaceView} and a {@link android.opengl.GLSurfaceView.Renderer}. The {@link
android.opengl.GLSurfaceView} is the main view type for the OpenGL applications and the {@link
android.opengl.GLSurfaceView.Renderer} controls what is drawn within that view. For more
information about these classes, see the <a href="{@docRoot}guide/topics/graphics/opengl.html">3D
with OpenGL</a> document.</p>

<p>To create an application that uses OpenGL ES 2.0:</p>
  
<ol>
  <li>Start a new Android project with an Activity called <code>HelloOpenGLES20</code>. 
    
    <p class="note"><b>Note:</b> If you have not created a basic Android application yet, follow the
      <a href="{@docRoot}resources/tutorials/hello-world.html">Hello World Tutorial</a> instructions
      to familiarize yourself with the process.</p>
  </li>
  
  <li>Modify the <code>HelloOpenGLES20</code> class as follows:
<pre>
package com.example.android.apis.graphics;

import android.app.Activity;
import android.content.Context;
import android.opengl.GLSurfaceView;
import android.os.Bundle;

public class HelloOpenGLES20 extends Activity {
  
    private GLSurfaceView mGLView;    
  
    &#64;Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        
        mGLView = new HelloOpenGLES20SurfaceView(this);
        setContentView(mGLView);
    }
    
    &#64;Override
    protected void onPause() {
        super.onPause();
        mGLView.onPause();
    }
    
    &#64;Override
    protected void onResume() {
        super.onResume();
        mGLView.onResume();
    }
}
  
class HelloOpenGLES20SurfaceView extends GLSurfaceView {

    public HelloOpenGLES20SurfaceView(Context context){
        super(context);
        
        // Create an OpenGL ES 2.0 context.
        setEGLContextClientVersion(2);

        setRenderer(new HelloOpenGLES20Renderer());
    }
}
</pre>
    <p>This Activity class creates a basic container for a {@link android.opengl.GLSurfaceView}.</p>
  </li>

  <li>Create the following class <code>HelloOpenGLES20Renderer</code>, which implements the 
    {@link android.opengl.GLSurfaceView.Renderer} interface:

<pre>
package com.example.android.apis.graphics;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

import android.opengl.GLES20;
import android.opengl.GLSurfaceView;

public class HelloOpenGLES20Renderer implements GLSurfaceView.Renderer {
  
    public void onSurfaceCreated(GL10 unused, EGLConfig config) {
    
        // Set the background frame color
        GLES20.glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
    }
    
    public void onDrawFrame(GL10 unused) {
    
        // Redraw background color
        GLES20.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
    }
    
    public void onSurfaceChanged(GL10 unused, int width, int height) {
        GLES20.glViewport(0, 0, width, height);
    }
  
}
</pre>
  </li>
</ol>

<p>These classes create a simple Android application which displays a grey screen using OpenGL
ES 2.0 calls. While this application does not do anything very interesting, by creating these two
classes, you have layed the foundation needed to start drawing graphic elements with OpenGL ES
2.0.</p>

<p>If your application only supports OpenGL ES 2.0, then you should declare that your application
  requires OpenGL ES 2.0 by adding the following settings to your <a                       
href="{@docRoot}guide/topics/manifest/manifest-intro.html">AndroidManifest.xml</a></code> file as
shown below.</p>

<pre>
...
    &lt;/application&gt;
    
    &lt;!-- Tell the system that you need ES 2.0. --&gt;
    &lt;uses-feature android:glEsVersion="0x00020000" android:required="true" /&gt;

&lt;/manifest&gt;
</pre>

<p>Adding this declaration hides your application from devices that do not support OpenGL ES 2.0
in the Android Market.</p>
  
<p>If you are familiar with the OpenGL ES APIs, these preceding classes and the AndroidManifest.xml
declaration should give you enough information to start using the OpenGL ES API and creating
graphics. However, if you need a bit more help getting started with OpenGL, head on to the next
sections for a few more tips.</p>


<h2 id="drawing">Drawing Graphic Elements</h2>

<p>Once you have implemented a {@link android.opengl.GLSurfaceView.Renderer}, the next step is to
draw something on it. This section shows you how to define and draw a basic triangle shape with the
OpenGL ES 2.0 API.</p>

<p class="note"><b>Note:</b> The following instructions build on the previous section, so if you
have not been following along, go back to the <a href="#get-started">previous section</a> and catch
up.</p>

<h3 id="define-triangle">Defining a Triangle</h3>

<p>OpenGL allows you to define objects using coordinates in three-dimensional space. So, before you
  can draw a triangle, you must define its coordinates. In OpenGL, the typical way to do this is to
  define a vertex array for the coordinates.</p>
  
<p>By default, OpenGL ES assumes a coordinate system where 0,0,0 (X,Y,Z) specifies the center of
  the {@link android.opengl.GLSurfaceView} frame, 1,1,0 is the top right corner of the frame  and 
-1,-1,0 is  bottom left corner of the frame.</p> 

<p>To define a vertex array for a triangle:</p>

<ol>
  <li>In your <code>HelloOpenGLES20Renderer</code> class, add new member variable to contain the
vertices of a triangle shape:
<pre>
    private FloatBuffer triangleVB;
</pre>
  </li>

  <li>Create a method, <code>initShapes()</code> which populates this member variable:
<pre>
    private void initShapes(){
    
        float triangleCoords[] = {
            // X, Y, Z
            -0.5f, -0.25f, 0,
             0.5f, -0.25f, 0,
             0.0f,  0.559016994f, 0
        }; 
        
        // initialize vertex Buffer for triangle  
        ByteBuffer vbb = ByteBuffer.allocateDirect(
                // (# of coordinate values * 4 bytes per float)
                triangleCoords.length * 4); 
        vbb.order(ByteOrder.nativeOrder());
        triangleVB = vbb.asFloatBuffer();
        triangleVB.put(triangleCoords);
        triangleVB.position(0);
    
    }
</pre>
    <p>This method defines a two-dimensional triangle shape with three equal sides.</p>
  </li>
  <li>Modify your <code>onSurfaceCreated()</code> method to initialize your triangle:
<pre>
    public void onSurfaceCreated(GL10 unused, EGLConfig config) {
    
        // Set the background frame color
        GLES20.glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
        
        // initialize the triangle vertex array
        initShapes();
    }
</pre>
  <p class="warning"><strong>Warning:</strong> Shapes and other static objects should be initialized
    once in your <code>onSurfaceCreated()</code> method for best performance. Avoid initializing the
    new objects in <code>onDrawFrame()</code>, as this causes the system to re-create the objects
    for every frame redraw and slows down your application.
  </p>
  </li>

</ol>

<h3 id="draw-triangle">Draw the Triangle</h3>

<p>Now that you have defined a shape to draw, you can now use the OpenGL APIs to draw the
  object. In order to do this with OpenGL ES 2.0, you must define a vertex shader and a
fragment shader to describe how to draw your shape.</p>

<p>To draw the triangle with OpenGL:</p>
  
<ol>
  <li>In your <code>HelloOpenGLES20Renderer</code> class, define a vertex shader and a fragment
shader.
<pre>
    private final String vertexShaderCode = 
        "attribute vec4 vPosition;    \n" +
        "void main(){         \n" +
        " gl_Position = vPosition;  \n" +
        "}                \n";
    
    private final String fragmentShaderCode = 
        "precision mediump float; \n" +
        "void main(){       \n" +
        " gl_FragColor = vec4 (0.63671875, 0.76953125, 0.22265625, 1.0); \n" +
        "} \n";
</pre>
  </li>
  <li>In your <code>HelloOpenGLES20Renderer</code> class, create a method for loading the shaders.
<pre>
    private int loadShader(int type, String shaderCode){
    
        int shader = GLES20.glCreateShader(type);
        
        GLES20.glShaderSource(shader, shaderCode);
        GLES20.glCompileShader(shader);
        
        return shader;
    }
</pre>
  </li>
  
  <li>Add the following members to your <code>HelloOpenGLES20Renderer</code> class for an OpenGL
Program.
<pre>
    private int mProgram;
    private int maPositionHandle;
</pre>
  </li>
  
  <li>Modify your <code>onSurfaceCreated()</code> method to load the shaders and attach them to a
OpenGL Program.
<pre>
    public void onSurfaceCreated(GL10 unused, EGLConfig config) {
    
        // Set the background frame color
        GLES20.glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
        
        // initialize the triangle vertex array
        initShapes();
        
        // Create shaders for shape and attach to program
        int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode);
        int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode);
        
        mProgram = GLES20.glCreateProgram();
        GLES20.glAttachShader(mProgram, vertexShader);
        GLES20.glAttachShader(mProgram, fragmentShader);
        GLES20.glLinkProgram(mProgram);
        
        maPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");
    }
</pre>
    <p>At this point, you are ready to draw the triangle object in the OpenGL frame.</p>
  </li>
  
  <li>Modify your <code>onDrawFrame()</code> method to draw the triangle.
<pre>
    public void onDrawFrame(GL10 unused) {
        
        // Redraw background color
        GLES20.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
    
        // Specify a program with shaders
        GLES20.glUseProgram(mProgram);
        
        // Prepare the triangle data
        GLES20.glVertexAttribPointer(maPositionHandle, 3, GLES20.GL_FLOAT, false, 12, triangleVB);
        GLES20.glEnableVertexAttribArray(maPositionHandle);
        
        // Draw the triangle
        GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, 3);
    }
</pre>
    <p><b>Note:</b> Since the triangle is stationary at this point, the system is redrawing the
      object repeatedly in exactly the same place and so is not the most efficient use of the OpenGL
      graphics pipeline. In a <a href="#motion">later section</a>, we add motion to the object to
      justify this use of processing power.</p>
  </li>
</ol>

<p id="squashed-triangle">Try running this example application on your emulator or test device and
you should see something like this:</p>

<img src="{@docRoot}images/opengl/helloopengl-es20-1.png">

<p>There are a few problems with this example. First of all, it's not going to impress your
friends. Secondly, the triangle is a bit squashed and changes shape when you change the screen
orientation of the test device. The reason the shape is skewed is due to the fact that the object is
being rendered in a frame which is not perfectly square. We fix that problem in the
<a href="#projection-and-views">next section</a>.</p>

<h2 id="projection-and-views">Using Projection and Views</h2>

<p>OpenGL Projection and Views provide a way to calculate the coordinates of graphic objects in
realistic proportions on graphics displays of any size. One of the basic problems in displaying
graphics is that Android device displays are typically not square and&#8212;by default&#8212;OpenGL
happily maps a perfectly square, uniform coordinate system onto your typically non-square
screen.</p>

<img src="{@docRoot}images/opengl/coordinates.png">
  
<p>The illustration above shows the uniform coordinate system assumed for an OpenGL frame on the
  left, and how these coordinates actually map to a typical device screen in landscape orientation
  on the right. To solve this problem, you can apply OpenGL projection modes and views to transform
  object coordinates so your graphic objects have the correct proportions on any display.</p>

<p>To use a projection transformation on your triangle:</p>
<ol>
  <li>Add the following members to your <code>HelloOpenGLES20Renderer</code> class.
<pre>
    private int muMVPMatrixHandle;
    private float[] mMVPMatrix = new float[16];
    private float[] mMMatrix = new float[16];
    private float[] mVMatrix = new float[16];
    private float[] mProjMatrix = new float[16];
</pre>
    </li>
    <li>Modify your <code>vertexShaderCode</code> string to add a variable for a model view
projection matrix.
<pre>
    private final String vertexShaderCode = 
        "uniform mat4 uMVPMatrix;   \n" +
        "attribute vec4 vPosition;  \n" +
        "void main(){               \n" +
        " gl_Position = uMVPMatrix * vPosition; \n" +
        "}  \n";
</pre>
    </li>
    <li>Modify your <code>onSurfaceChanged()</code> method to reference the <code>uMVPMatrix</code>
shader variable you added and define a view transformation matrix.
<pre>
    public void onSurfaceCreated(GL10 unused, EGLConfig config) {
        ...
        maPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");
        
        muMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
        Matrix.setLookAtM(mVMatrix, 0, 0, 0, -3, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
    }
</pre>
    </li>
    <li>Next, modify your <code>onSurfaceChanged()</code> method to calculate the device screen
ration and create a projection matrix.
<pre>
    public void onSurfaceChanged(GL10 unused, int width, int height) {
        GLES20.glViewport(0, 0, width, height);
        
        float ratio = (float) width / height;
        Matrix.frustumM(mProjMatrix, 0, -ratio, ratio, -1, 1, 3, 7);
    }  
</pre>
    </li>
    <li>Finally, modify your <code>onDrawFrame()</code> method to apply the transformation.
<pre>
    public void onDrawFrame(GL10 unused) {
        ...
        // Apply a ModelView Projection transformation
        Matrix.multiplyMM(mMVPMatrix, 0, mProjMatrix, 0, mVMatrix, 0);
        GLES20.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, mMVPMatrix, 0);
        
        // Draw the triangle
        GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, 3);        
    }
</pre>
    </li>
  </ol>
  
<p>Try running this updated application on your emulator or test device and you should see
something like this:</p>
  
  <img src="{@docRoot}images/opengl/helloopengl-es20-2.png">
    
<p>Now that you have applied this transformation, the triangle has three equal sides, instead of the
squashed display in the <a href="#squashed-triangle">earlier version</a>.</p>


<h2 id="motion">Adding Motion</h2>

<p>While it may be interesting exercise to create static graphic objects with OpenGL ES, chances
are you want at least some of your objects to move. In this section, we add motion to to our
triangle by rotating it.</p>

<p>To add rotation to your triangle:</p>
<ol>
  <li>Add an additional tranformation matrix member to your <code>HelloOpenGLES20Renderer</code>
class.
    <pre>
      private float[] mMMatrix = new float[16];
    </pre>
    </li>  <li>Modify your <code>onDrawFrame()</code> method to rotate the triangle object:
<pre>
    public void onDrawFrame(GL10 gl) {
        ...
    
        // Create a rotation for the triangle
        long time = SystemClock.uptimeMillis() % 4000L;
        float angle = 0.090f * ((int) time);
        Matrix.setRotateM(mMMatrix, 0, angle, 0, 0, 1.0f);
        Matrix.multiplyMM(mMVPMatrix, 0, mVMatrix, 0, mMMatrix, 0);
        Matrix.multiplyMM(mMVPMatrix, 0, mProjMatrix, 0, mMVPMatrix, 0);
        
        // Apply a ModelView Projection transformation
        //Matrix.multiplyMM(mMVPMatrix, 0, mProjMatrix, 0, mVMatrix, 0);
        GLES20.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, mMVPMatrix, 0);
        
        // Draw the triangle
        ...
    }
</pre>
  </li>
</ol>

<p>Run the application with this code and your triangle should rotate around its center.</p>

<!--
  <h2>TouchScreen Interaction</h2>
  Optional extra (using code from API examples)
  -->

<h2 id="resources">Additional Resources</h2>

<p>Be sure to check out the OpenGL ES code samples are available in the
<a
href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/graphics/index.html">API
Demos</a> sample application and listed in <a href="#code-samples-list">Related Samples</a>
sidebar above.
</p>