diff options
| author | Joe Fernandez <joefernandez@google.com> | 2011-07-15 16:02:30 -0700 |
|---|---|---|
| committer | Joe Fernandez <joefernandez@google.com> | 2011-08-16 16:19:52 -0700 |
| commit | 0664a8f661e15991e20cab1c0daa41f5e21977ab (patch) | |
| tree | 676eea3cd13f332a85136c45b292a948c04e71b4 /docs/html/resources/tutorials/opengl | |
| parent | d5a7fc0a3bb7c05aef3484c7bc57d3f2f3046e45 (diff) | |
| download | frameworks_base-0664a8f661e15991e20cab1c0daa41f5e21977ab.zip frameworks_base-0664a8f661e15991e20cab1c0daa41f5e21977ab.tar.gz frameworks_base-0664a8f661e15991e20cab1c0daa41f5e21977ab.tar.bz2 | |
docs: OpenGL ES 1.0 and 2.0 Tutorials
Change-Id: Id6dc8f8737c60406e96b9b8afac646b8a24fc304
Diffstat (limited to 'docs/html/resources/tutorials/opengl')
| -rw-r--r-- | docs/html/resources/tutorials/opengl/opengl-es10.jd | 532 | ||||
| -rw-r--r-- | docs/html/resources/tutorials/opengl/opengl-es20.jd | 652 |
2 files changed, 1184 insertions, 0 deletions
diff --git a/docs/html/resources/tutorials/opengl/opengl-es10.jd b/docs/html/resources/tutorials/opengl/opengl-es10.jd new file mode 100644 index 0000000..40304fd --- /dev/null +++ b/docs/html/resources/tutorials/opengl/opengl-es10.jd @@ -0,0 +1,532 @@ +page.title=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">Create an Activity with GLSurfaceView</a></li> + <li> + <a href="#drawing">Draw a Shape on GLSurfaceView</a> + <ol> + <li><a href="#define-triangle">Define a Triangle</a></li> + <li><a href="#draw-triangle">Draw the Triangle</a></li> + </ol> + </li> + <li><a href="#projection-and-views">Apply Projection and Camera Views</a></li> + <li><a href="#motion">Add Motion</a></li> + <li><a href="#touch">Respond to Touch Events</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/ +GLSurfaceViewActivity.html">OpenGL ES 1.0 Sample</a></li> + <li><a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/graphics/ +TouchRotateActivity.html">TouchRotateActivity</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-es20.html">OpenGL +ES 2.0</a></li> + </ol> + </div> + </div> + +<p>This tutorial shows you how to create a simple Android application that uses the OpenGL ES 1.0 +API to perform some basic graphics operations. You'll learn how to:</p> + +<ul> + <li>Create an activity using {@link android.opengl.GLSurfaceView} and {@link +android.opengl.GLSurfaceView.Renderer}</li> + <li>Create and draw a graphic object</li> + <li>Define a projection to correct for screen geometry</li> + <li>Define a camera view</li> + <li>Rotate a graphic object</li> + <li>Make graphics touch-interactive</li> +</ul> + +<p>The Android framework supports both the OpenGL ES 1.0/1.1 and OpenGL ES 2.0 APIs. You should +carefully consider which version of the OpenGL ES API (1.0/1.1 or 2.0) is most appropriate for your +needs. For more information, see +<a href="{@docRoot}guide/topics/graphics/opengl.html#choosing-version">Choosing an OpenGL API +Version</a>. If you would prefer to use OpenGL ES 2.0, see the <a +href="{@docRoot}resources/tutorials/opengl/opengl-es20.jd">OpenGL ES 2.0 tutorial</a>.</p> + +<p>Before you start, you should understand how to create a basic Android application. If you do not +know how to create an app, follow the <a href="{@docRoot}resources/tutorials/hello-world.html">Hello +World Tutorial</a> to familiarize yourself with the process.</p> + +<h2 id="creating">Create an Activity with GLSurfaceView</h2> + +<p>To get started using OpenGL, 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 applications that use OpenGL 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 activity using {@code GLSurfaceView}:</p> + +<ol> + <li>Start a new Android project that targets Android 1.6 (API Level 4) or higher. + </li> + <li>Name the project <strong>HelloOpenGLES10</strong> and make sure it includes an activity called +{@code HelloOpenGLES10}. + </li> + <li>Modify the {@code HelloOpenGLES10} 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 HelloOpenGLES10 extends Activity { + + private GLSurfaceView mGLView; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // Create a GLSurfaceView instance and set it + // as the ContentView for this Activity. + mGLView = new HelloOpenGLES10SurfaceView(this); + setContentView(mGLView); + } + + @Override + protected void onPause() { + super.onPause(); + // The following call pauses the rendering thread. + // If your OpenGL application is memory intensive, + // you should consider de-allocating objects that + // consume significant memory here. + mGLView.onPause(); + } + + @Override + protected void onResume() { + super.onResume(); + // The following call resumes a paused rendering thread. + // If you de-allocated graphic objects for onPause() + // this is a good place to re-allocate them. + mGLView.onResume(); + } +} + +class HelloOpenGLES10SurfaceView extends GLSurfaceView { + + public HelloOpenGLES10SurfaceView(Context context){ + super(context); + + // Set the Renderer for drawing on the GLSurfaceView + setRenderer(new HelloOpenGLES10Renderer()); + } +} +</pre> + <p class="note"><strong>Note:</strong> You will get a compile error for the {@code +HelloOpenGLES10Renderer} class reference. That's expected; you will fix this error in the next step. + </p> + + <p>As shown above, this activity uses a single {@link android.opengl.GLSurfaceView} for its +view. Notice that this activity implements crucial lifecycle callbacks for pausing and resuming its +work.</p> + + <p>The {@code HelloOpenGLES10SurfaceView} class in this example code above is just a thin wrapper +for an instance of {@link android.opengl.GLSurfaceView} and is not strictly necessary for this +example. However, if you want your application to monitor and respond to touch screen +events—and we are guessing you do—you must extend {@link android.opengl.GLSurfaceView} +to add touch event listeners, which you will learn how to do in the <a href="#touch">Reponding to +Touch Events</a> section.</p> + + <p>In order to draw graphics in the {@link android.opengl.GLSurfaceView}, you must define an +implementation of {@link android.opengl.GLSurfaceView.Renderer}. In the next step, you create +a renderer class to complete this OpenGL application.</p> + </li> + + <li>Create a new file for the following class {@code HelloOpenGLES10Renderer}, 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.GLSurfaceView; + +public class HelloOpenGLES10Renderer implements GLSurfaceView.Renderer { + + public void onSurfaceCreated(GL10 gl, EGLConfig config) { + // Set the background frame color + gl.glClearColor(0.5f, 0.5f, 0.5f, 1.0f); + } + + public void onDrawFrame(GL10 gl) { + // Redraw background color + gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); + } + + public void onSurfaceChanged(GL10 gl, int width, int height) { + gl.glViewport(0, 0, width, height); + } + +} +</pre> + <p>This minimal implementation of {@link android.opengl.GLSurfaceView.Renderer} provides the +code structure needed to use OpenGL drawing methods: +<ul> + <li>{@link + android.opengl.GLSurfaceView.Renderer#onSurfaceCreated(javax.microedition.khronos.opengles.GL10, + javax.microedition.khronos.egl.EGLConfig) onSurfaceCreated()} is called once to set up the +{@link android.opengl.GLSurfaceView} +environment.</li> + <li>{@link + android.opengl.GLSurfaceView.Renderer#onDrawFrame(javax.microedition.khronos.opengles.GL10) + onDrawFrame()} is called for each redraw of the {@link +android.opengl.GLSurfaceView}.</li> + <li>{@link + android.opengl.GLSurfaceView.Renderer#onSurfaceChanged(javax.microedition.khronos.opengles.GL10, + int, int) onSurfaceChanged()} is called if the geometry of the {@link +android.opengl.GLSurfaceView} changes, for example when the device's screen orientation +changes.</li> +</ul> + </p> + <p>For more information about these methods, see the <a +href="{@docRoot}guide/topics/graphics/opengl.html">3D with OpenGL</a> document. +</p> + </li> +</ol> + +<p>The code example above creates a simple Android application that displays a grey screen using +OpenGL ES 1.0 calls. While this application does not do anything very interesting, by creating these +classes, you have layed the foundation needed to start drawing graphic elements with OpenGL ES +1.0.</p> + +<p>If you are familiar with the OpenGL ES APIs, these classes should give you enough information +to use the OpenGL ES 1.0 API and create graphics. However, if you need a bit more help getting +started with OpenGL, head on to the next sections for a few more hints.</p> + +<h2 id="drawing">Draw a Shape on GLSurfaceView</h2> + +<p>Once you have implemented a {@link android.opengl.GLSurfaceView.Renderer}, the next step is to +draw something with it. This section shows you how to define and draw a triangle.</p> + +<h3 id="define-triangle">Define 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 HelloOpenGLES10Renderer} 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()} 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());// use the device hardware's native byte order + triangleVB = vbb.asFloatBuffer(); // create a floating point buffer from the ByteBuffer + triangleVB.put(triangleCoords); // add the coordinates to the FloatBuffer + triangleVB.position(0); // set the buffer to read the first coordinate + + } +</pre> + <p>This method defines a two-dimensional triangle with three equal sides.</p> + </li> + <li>Modify your {@code onSurfaceCreated()} method to initialize your triangle: + <pre> + public void onSurfaceCreated(GL10 gl, EGLConfig config) { + + // Set the background frame color + gl.glClearColor(0.5f, 0.5f, 0.5f, 1.0f); + + // initialize the triangle vertex array + initShapes(); + } +</pre> + <p class="caution"><strong>Caution:</strong> Shapes and other static objects should be initialized + once in your {@code onSurfaceCreated()} method for best performance. Avoid initializing the + new objects in {@code onDrawFrame()}, as this causes the system to re-create the objects + for every frame redraw and slows down your application. + </p> + </li> + +</ol> + +<p>You have now defined a triangle shape, but if you run the application, nothing appears. What?! +You also have to tell OpenGL to draw the triangle, which you'll do in the next section. +</p> + + +<h3 id="draw-triangle">Draw the Triangle</h3> + +<p>Before you can draw your triangle, you must tell OpenGL that you are using vertex arrays. After +that setup step, you can call the drawing APIs to display the triangle.</p> + +<p>To draw the triangle:</p> + +<ol> + <li>Add the {@code glEnableClientState()} method to the end of {@code onSurfaceCreated()} to +enable vertex arrays. +<pre> + // Enable use of vertex arrays + gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); +</pre> + <p>At this point, you are ready to draw the triangle object in the OpenGL view.</p> + </li> + + <li>Add the following code to the end of your {@code onDrawFrame()} method to draw the triangle. +<pre> + // Draw the triangle + gl.glColor4f(0.63671875f, 0.76953125f, 0.22265625f, 0.0f); + gl.glVertexPointer(3, GL10.GL_FLOAT, 0, triangleVB); + gl.glDrawArrays(GL10.GL_TRIANGLES, 0, 3); +</pre> + </li> + <li id="squashed-triangle">Run the app! Your application should look something like this: + </li> +</ol> + +<img src="{@docRoot}images/opengl/helloopengl-es10-1.png"> +<p class="img-caption"> + <strong>Figure 1.</strong> Triangle drawn without a projection or camera view. +</p> + +<p>There are a few problems with this example. First of all, it is not going to impress your +friends. Secondly, the triangle is a bit squashed and changes shape when you change the screen +orientation of the 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. You'll fix that problem using a projection +and camera view in the next section.</p> + +<p>Lastly, because the triangle is stationary, the system is redrawing the object repeatedly in +exactly the same place, which is not the most efficient use of the OpenGL graphics pipeline. In the +<a href="#motion">Add Motion</a> section, you'll make this shape rotate and justify +this use of processing power.</p> + +<h2 id="projection-and-views">Apply Projection and Camera View</h2> + +<p>One of the basic problems in displaying graphics is that Android device displays are typically +not square and, by default, OpenGL happily maps a perfectly square, uniform coordinate +system onto your typically non-square screen. To solve this problem, you can apply an OpenGL +projection mode and camera view (eye point) to transform the coordinates of your graphic objects +so they have the correct proportions on any display. For more information about OpenGL coordinate +mapping, see <a href="{@docRoot}guide/topics/graphics/opengl.html#coordinate-mapping">Coordinate +Mapping for Drawn Objects</a>.</p> + +<p>To apply projection and camera view transformations to your triangle: +</p> +<ol> + <li>Modify your {@code onSurfaceChanged()} method to enable {@link + javax.microedition.khronos.opengles.GL10#GL_PROJECTION GL10.GL_PROJECTION} mode, calculate the + screen ratio and apply the ratio as a transformation of the object coordinates. +<pre> + public void onSurfaceChanged(GL10 gl, int width, int height) { + gl.glViewport(0, 0, width, height); + + // make adjustments for screen ratio + float ratio = (float) width / height; + gl.glMatrixMode(GL10.GL_PROJECTION); // set matrix to projection mode + gl.glLoadIdentity(); // reset the matrix to its default state + gl.glFrustumf(-ratio, ratio, -1, 1, 3, 7); // apply the projection matrix + } +</pre> + </li> + + <li>Next, modify your {@code onDrawFrame()} method to apply the {@link +javax.microedition.khronos.opengles.GL10#GL_MODELVIEW GL_MODELVIEW} mode and set +a view point using {@link android.opengl.GLU#gluLookAt(javax.microedition.khronos.opengles.GL10, +float, float, float, float, float, float, float, float, float) GLU.gluLookAt()}. +<pre> + public void onDrawFrame(GL10 gl) { + // Redraw background color + gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); + + // Set GL_MODELVIEW transformation mode + gl.glMatrixMode(GL10.GL_MODELVIEW); + gl.glLoadIdentity(); // reset the matrix to its default state + + // When using GL_MODELVIEW, you must set the view point + GLU.gluLookAt(gl, 0, 0, -5, 0f, 0f, 0f, 0f, 1.0f, 0.0f); + + // Draw the triangle + ... + } +</pre> + </li> + <li>Run the updated application and you should see something like this:</li> +</ol> + +<img src="{@docRoot}images/opengl/helloopengl-es10-2.png"> +<p class="img-caption"> + <strong>Figure 2.</strong> Triangle drawn with a projection and camera view applied. +</p> + +<p>Now that you have applied this transformation, the triangle has three equal sides, instead of the +<a href="#squashed-triangle">squashed triangle</a> in the earlier version.</p> + +<h2 id="motion">Add Motion</h2> + +<p>While it may be an interesting exercise to create static graphic objects with OpenGL ES, chances +are you want at least <em>some</em> of your objects to move. In this section, you'll add motion to +your triangle by rotating it.</p> + +<p>To add rotation to your triangle:</p> +<ol> + <li>Modify your {@code onDrawFrame()} method to rotate the triangle object: +<pre> + public void onDrawFrame(GL10 gl) { + ... + // When using GL_MODELVIEW, you must set the view point + GLU.gluLookAt(gl, 0, 0, -5, 0f, 0f, 0f, 0f, 1.0f, 0.0f); + + // Create a rotation for the triangle + long time = SystemClock.uptimeMillis() % 4000L; + float angle = 0.090f * ((int) time); + gl.glRotatef(angle, 0.0f, 0.0f, 1.0f); + + // Draw the triangle + ... + } +</pre> + </li> + <li>Run the application and your triangle should rotate around its center.</li> +</ol> + + +<h2 id="touch">Respond to Touch Events</h2> +<p>Making objects move according to a preset program like the rotating triangle is useful for +getting some attention, but what if you want to have users interact with your OpenGL graphics? In +this section, you'll learn how listen for touch events to let users interact with objects in your +{@code HelloOpenGLES10SurfaceView}.</p> + +<p>The key to making your OpenGL application touch interactive is expanding your implementation of +{@link android.opengl.GLSurfaceView} to override the {@link +android.view.View#onTouchEvent(android.view.MotionEvent) onTouchEvent()} to listen for touch events. +Before you do that, however, you'll modify the renderer class to expose the rotation angle of the +triangle. Afterwards, you'll modify the {@code HelloOpenGLES10SurfaceView} to process touch events +and pass that data to your renderer.</p> + +<p>To make your triangle rotate in response to touch events:</p> + +<ol> + <li>Modify your {@code HelloOpenGLES10Renderer} class to include a new, public member so that +your {@code HelloOpenGLES10SurfaceView} class is able to pass new rotation values your renderer: +<pre> + public float mAngle; +</pre> + </li> + <li>In your {@code onDrawFrame()} method, comment out the code that generates an angle and +replace the {@code angle} variable with {@code mAngle}. +<pre> + // Create a rotation for the triangle (Boring! Comment this out:) + // long time = SystemClock.uptimeMillis() % 4000L; + // float angle = 0.090f * ((int) time); + + // Use the mAngle member as the rotation value + gl.glRotatef(mAngle, 0.0f, 0.0f, 1.0f); +</pre> + </li> + <li>In your {@code HelloOpenGLES10SurfaceView} class, add the following member variables. +<pre> + private final float TOUCH_SCALE_FACTOR = 180.0f / 320; + private HelloOpenGLES10Renderer mRenderer; + private float mPreviousX; + private float mPreviousY; +</pre> + </li> + <li>In the constructor method for {@code HelloOpenGLES10SurfaceView}, set the {@code mRenderer} +member so you have a handle to pass in rotation input and set the render mode to {@link +android.opengl.GLSurfaceView#RENDERMODE_WHEN_DIRTY}. +<pre> + public HelloOpenGLES10SurfaceView(Context context){ + super(context); + // set the mRenderer member + mRenderer = new HelloOpenGLES10Renderer(); + setRenderer(mRenderer); + + // Render the view only when there is a change + setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY); + } +</pre> + </li> + <li>In your {@code HelloOpenGLES10SurfaceView} class, override the {@link +android.view.View#onTouchEvent(android.view.MotionEvent) onTouchEvent()} method to listen for touch +events and pass them to your renderer. +<pre> + @Override + public boolean onTouchEvent(MotionEvent e) { + // MotionEvent reports input details from the touch screen + // and other input controls. In this case, you are only + // interested in events where the touch position changed. + + float x = e.getX(); + float y = e.getY(); + + switch (e.getAction()) { + case MotionEvent.ACTION_MOVE: + + float dx = x - mPreviousX; + float dy = y - mPreviousY; + + // reverse direction of rotation above the mid-line + if (y > getHeight() / 2) { + dx = dx * -1 ; + } + + // reverse direction of rotation to left of the mid-line + if (x < getWidth() / 2) { + dy = dy * -1 ; + } + + mRenderer.mAngle += (dx + dy) * TOUCH_SCALE_FACTOR; + requestRender(); + } + + mPreviousX = x; + mPreviousY = y; + return true; + } +</pre> + <p class="note"><strong>Note:</strong> Touch events return pixel coordinates which <em>are not the +same</em> as OpenGL coordinates. Touch coordinate [0,0] is the bottom-left of the screen and the +highest value [max_X, max_Y] is the top-right corner of the screen. To match touch events to OpenGL +graphic objects, you must translate touch coordinates into OpenGL coordinates.</p> + </li> + <li>Run the application and drag your finger or cursor around the screen to rotate the +triangle.</li> +</ol> +<p>For another example of OpenGL touch event functionality, see <a +href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/graphics/ +TouchRotateActivity.html">TouchRotateActivity</a>.</p>
\ No newline at end of file diff --git a/docs/html/resources/tutorials/opengl/opengl-es20.jd b/docs/html/resources/tutorials/opengl/opengl-es20.jd new file mode 100644 index 0000000..439f7d5 --- /dev/null +++ b/docs/html/resources/tutorials/opengl/opengl-es20.jd @@ -0,0 +1,652 @@ +page.title=OpenGL ES 2.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">Create an Activity with GLSurfaceView</a></li> + <li> + <a href="#drawing">Draw a Shape on GLSurfaceView</a> + <ol> + <li><a href="#define-triangle">Define a Triangle</a></li> + <li><a href="#draw-triangle">Draw the Triangle</a></li> + </ol> + </li> + <li><a href="#projection-and-views">Apply Projection and Camera Views</a></li> + <li><a href="#motion">Add Motion</a></li> + <li><a href="#touch">Respond to Touch Events</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 Sample</a></li> + <li><a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/graphics/ +TouchRotateActivity.html">TouchRotateActivity</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">OpenGL +ES 1.0</a></li> + </ol> + </div> + </div> + +<p>This tutorial shows you how to create a simple Android application that uses the OpenGL ES 2.0 +API to perform some basic graphics operations. You'll learn how to:</p> + +<ul> + <li>Create an activity using {@link android.opengl.GLSurfaceView} and {@link +android.opengl.GLSurfaceView.Renderer}</li> + <li>Create and draw a graphic object</li> + <li>Define a projection to correct for screen geometry</li> + <li>Define a camera view</li> + <li>Rotate a graphic object</li> + <li>Make graphics touch interactive</li> +</ul> + +<p>The Android framework supports both the OpenGL ES 1.0/1.1 and OpenGL ES 2.0 APIs. You should +carefully consider which version of the OpenGL ES API (1.0/1.1 or 2.0) is most appropriate for your +needs. For more information, see +<a href="{@docRoot}guide/topics/graphics/opengl.html#choosing-version">Choosing an OpenGL API +Version</a>. If you would prefer to use OpenGL ES 1.0, see the <a +href="{@docRoot}resources/tutorials/opengl/opengl-es10.jd">OpenGL ES 1.0 tutorial</a>.</p> + +<p>Before you start, you should understand how to create a basic Android application. If you do not +know how to create an app, follow the <a href="{@docRoot}resources/tutorials/hello-world.html">Hello +World Tutorial</a> to familiarize yourself with the process.</p> + +<p class="caution"><strong>Caution:</strong> OpenGL ES 2.0 <em>is currently not supported</em> by +the Android Emulator. You must have a physical test device running Android 2.2 (API Level 8) or +higher in order to run and test the example code in this tutorial.</p> + +<h2 id="creating">Create an Activity with GLSurfaceView</h2> + +<p>To get started using OpenGL, 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 applications that use OpenGL 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 activity using {@code GLSurfaceView}:</p> + +<ol> + <li>Start a new Android project that targets Android 2.2 (API Level 8) or higher. + </li> + <li>Name the project <strong>HelloOpenGLES20</strong> and make sure it includes an activity called +{@code HelloOpenGLES20}. + </li> + <li>Modify the {@code HelloOpenGLES20} 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; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // Create a GLSurfaceView instance and set it + // as the ContentView for this Activity + mGLView = new HelloOpenGLES20SurfaceView(this); + setContentView(mGLView); + } + + @Override + protected void onPause() { + super.onPause(); + // The following call pauses the rendering thread. + // If your OpenGL application is memory intensive, + // you should consider de-allocating objects that + // consume significant memory here. + mGLView.onPause(); + } + + @Override + protected void onResume() { + super.onResume(); + // The following call resumes a paused rendering thread. + // If you de-allocated graphic objects for onPause() + // this is a good place to re-allocate them. + mGLView.onResume(); + } +} + +class HelloOpenGLES20SurfaceView extends GLSurfaceView { + + public HelloOpenGLES20SurfaceView(Context context){ + super(context); + + // Create an OpenGL ES 2.0 context. + setEGLContextClientVersion(2); + // Set the Renderer for drawing on the GLSurfaceView + setRenderer(new HelloOpenGLES20Renderer()); + } +} +</pre> + <p class="note"><strong>Note:</strong> You will get a compile error for the {@code +HelloOpenGLES20Renderer} class reference. That's expected; you will fix this error in the next step. + </p> + + <p>As shown above, this activity uses a single {@link android.opengl.GLSurfaceView} for its +view. Notice that this activity implements crucial lifecycle callbacks for pausing and resuming its +work.</p> + + <p>The {@code HelloOpenGLES20SurfaceView} class in this example code above is just a thin wrapper +for an instance of {@link android.opengl.GLSurfaceView} and is not strictly necessary for this +example. However, if you want your application to monitor and respond to touch screen +events—and we are guessing you do—you must extend {@link android.opengl.GLSurfaceView} +to add touch event listeners, which you will learn how to do in the <a href="#touch">Reponding to +Touch Events</a> section.</p> + + <p>In order to draw graphics in the {@link android.opengl.GLSurfaceView}, you must define an +implementation of {@link android.opengl.GLSurfaceView.Renderer}. In the next step, you create +a renderer class to complete this OpenGL application.</p> + </li> + + <li>Create a new file for the following class {@code HelloOpenGLES20Renderer}, 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(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT); + } + + public void onSurfaceChanged(GL10 unused, int width, int height) { + GLES20.glViewport(0, 0, width, height); + } + +} +</pre> + <p>This minimal implementation of {@link android.opengl.GLSurfaceView.Renderer} provides the +code structure needed to use OpenGL drawing methods: +<ul> + <li>{@link + android.opengl.GLSurfaceView.Renderer#onSurfaceCreated(javax.microedition.khronos.opengles.GL10, + javax.microedition.khronos.egl.EGLConfig) onSurfaceCreated()} is called once to set up the +{@link android.opengl.GLSurfaceView} +environment.</li> + <li>{@link + android.opengl.GLSurfaceView.Renderer#onDrawFrame(javax.microedition.khronos.opengles.GL10) + onDrawFrame()} is called for each redraw of the {@link +android.opengl.GLSurfaceView}.</li> + <li>{@link + android.opengl.GLSurfaceView.Renderer#onSurfaceChanged(javax.microedition.khronos.opengles.GL10, + int, int) onSurfaceChanged()} is called if the geometry of the {@link +android.opengl.GLSurfaceView} changes, for example when the device's screen orientation +changes.</li> +</ul> + </p> + <p>For more information about these methods, see the <a +href="{@docRoot}guide/topics/graphics/opengl.html">3D with OpenGL</a> document. +</p> + </li> +</ol> + +<p>The code example above creates a simple Android application that displays a grey screen using +OpenGL ES 2.0 calls. While this application does not do anything very interesting, by creating these +classes, you have layed the foundation needed to start drawing graphic elements with OpenGL ES +2.0.</p> + +<p>If you are familiar with the OpenGL ES APIs, these classes should give you enough information +to use the OpenGL ES 2.0 API and create graphics. However, if you need a bit more help getting +started with OpenGL, head on to the next sections for a few more hints.</p> + +<p class="note"><strong>Note:</strong> If your application requires OpenGL 2.0, make sure you +declare this in your manifest:</p> +<pre> + <!-- Tell the system this app requires OpenGL ES 2.0. --> + <uses-feature android:glEsVersion="0x00020000" android:required="true" /> +</pre> +<p>For more information, see <a +href="{@docRoot}guide/topics/graphics/opengl.html#manifest">OpenGL manifest declarations</a> in the +<em>3D with OpenGL</em> document.</p> + + +<h2 id="drawing">Draw a Shape on GLSurfaceView</h2> + +<p>Once you have implemented a {@link android.opengl.GLSurfaceView.Renderer}, the next step is to +draw something with it. This section shows you how to define and draw a triangle.</p> + +<h3 id="define-triangle">Define 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} 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()} 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());// use the device hardware's native byte order + triangleVB = vbb.asFloatBuffer(); // create a floating point buffer from the ByteBuffer + triangleVB.put(triangleCoords); // add the coordinates to the FloatBuffer + triangleVB.position(0); // set the buffer to read the first coordinate + + } +</pre> + <p>This method defines a two-dimensional triangle shape with three equal sides.</p> + </li> + <li>Modify your {@code onSurfaceCreated()} 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="caution"><strong>Caution:</strong> Shapes and other static objects should be initialized + once in your {@code onSurfaceCreated()} method for best performance. Avoid initializing the + new objects in {@code onDrawFrame()}, as this causes the system to re-create the objects + for every frame redraw and slows down your application. + </p> + </li> + +</ol> + +<p>You have now defined a triangle shape, but if you run the application, nothing appears. What?! +You also have to tell OpenGL to draw the triangle, which you'll do in the next section. +</p> + + +<h3 id="draw-triangle">Draw the Triangle</h3> + +<p>The OpenGL ES 2.0 requires a bit more code than OpenGL ES 1.0/1.1 in order to draw objects. In +this section, you'll create vertex and fragment shaders, a shader loader, apply the shaders, enable +the use of vertex arrays for your triangle and, finally, draw it on screen.</p> + +<p>To draw the triangle:</p> + +<ol> + <li>In your {@code HelloOpenGLES20Renderer} class, define a vertex shader and a fragment +shader. Shader code is defined as a string which is compiled and run by the OpenGL ES 2.0 rendering +engine. +<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> + <p>The vertex shader controls how OpenGL positions and draws the vertices of shapes in space. +The fragment shader controls what OpenGL draws <em>between</em> the vertices of shapes.</p> + </li> + <li>In your {@code HelloOpenGLES20Renderer} class, create a method for loading the shaders. +<pre> + private int loadShader(int type, String shaderCode){ + + // create a vertex shader type (GLES20.GL_VERTEX_SHADER) + // or a fragment shader type (GLES20.GL_FRAGMENT_SHADER) + int shader = GLES20.glCreateShader(type); + + // add the source code to the shader and compile it + GLES20.glShaderSource(shader, shaderCode); + GLES20.glCompileShader(shader); + + return shader; + } +</pre> + </li> + + <li>Add the following members to your {@code HelloOpenGLES20Renderer} class for an OpenGL +Program and the positioning control for your triangle. +<pre> + private int mProgram; + private int maPositionHandle; +</pre> + <p>In OpenGL ES 2.0, you attach vertex and fragment shaders to a <em>Program</em> and then +apply the program to the OpenGL graphics pipeline.</p> + </li> + + <li>Add the following code to the end of your {@code onSurfaceCreated()} method to load the +shaders and attach them to an OpenGL Program. +<pre> + int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode); + int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode); + + mProgram = GLES20.glCreateProgram(); // create empty OpenGL Program + GLES20.glAttachShader(mProgram, vertexShader); // add the vertex shader to program + GLES20.glAttachShader(mProgram, fragmentShader); // add the fragment shader to program + GLES20.glLinkProgram(mProgram); // creates OpenGL program executables + + // get handle to the vertex shader's vPosition member + maPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition"); +</pre> + <p>At this point, you are ready to draw the triangle object in the OpenGL view.</p> + </li> + + <li>Add the following code to the end of your {@code onDrawFrame()} method apply the OpenGL +program you created, load the triangle object and draw the triangle. +<pre> + // Add program to OpenGL environment + 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> + </li> + <li id="squashed-triangle">Run the app! Your application should look something like this: + </li> +</ol> + +<img src="{@docRoot}images/opengl/helloopengl-es20-1.png"> +<p class="img-caption"> + <strong>Figure 1.</strong> Triangle drawn without a projection or camera view. +</p> + +<p>There are a few problems with this example. First of all, it is not going to impress your +friends. Secondly, the triangle is a bit squashed and changes shape when you change the screen +orientation of the 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. You'll fix that problem using a projection +and camera view in the next section.</p> + +<p>Lastly, because the triangle is stationary, the system is redrawing the object repeatedly in +exactly the same place, which is not the most efficient use of the OpenGL graphics pipeline. In the +<a href="#motion">Add Motion</a> section, you'll make this shape rotate and justify +this use of processing power.</p> + +<h2 id="projection-and-views">Apply Projection and Camera View</h2> + +<p>One of the basic problems in displaying graphics is that Android device displays are typically +not square and, by default, OpenGL happily maps a perfectly square, uniform coordinate +system onto your typically non-square screen. To solve this problem, you can apply an OpenGL +projection mode and camera view (eye point) to transform the coordinates of your graphic objects +so they have the correct proportions on any display. For more information about OpenGL coordinate +mapping, see <a href="{@docRoot}guide/topics/graphics/opengl.html#coordinate-mapping">Coordinate +Mapping for Drawn Objects</a>.</p> + +<p>To apply projection and camera view transformations to your triangle: +</p> +<ol> + <li>Add the following members to your {@code HelloOpenGLES20Renderer} 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} string to add a variable for a model view +projection matrix. +<pre> + private final String vertexShaderCode = + // This matrix member variable provides a hook to manipulate + // the coordinates of the objects that use this vertex shader + "uniform mat4 uMVPMatrix; \n" + + + "attribute vec4 vPosition; \n" + + "void main(){ \n" + + + // the matrix must be included as a modifier of gl_Position + " gl_Position = uMVPMatrix * vPosition; \n" + + + "} \n"; +</pre> + </li> + <li>Modify the {@code onSurfaceChanged()} method to calculate the device screen ratio 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; + + // this projection matrix is applied to object coodinates + // in the onDrawFrame() method + Matrix.frustumM(mProjMatrix, 0, -ratio, ratio, -1, 1, 3, 7); + } +</pre> + </li> + <li>Add the following code to the end of your {@code onSurfaceChanged()} method to +reference the {@code uMVPMatrix} shader matrix variable you added in step 2. +<pre> + muMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix"); +</pre> + </li> + <li>Add the following code to the end of your {@code onSurfaceChanged()} method to define +a camera view matrix. +<pre> + Matrix.setLookAtM(mVMatrix, 0, 0, 0, -3, 0f, 0f, 0f, 0f, 1.0f, 0.0f); +</pre> + </li> + <li>Finally, modify your {@code onDrawFrame()} method to combine the projection and +camera view matrices and then apply the combined transformation to the OpenGL rendering pipeline. +<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 + ... + } +</pre> + </li> + <li>Run the updated application and you should see something like this:</li> +</ol> + +<img src="{@docRoot}images/opengl/helloopengl-es20-2.png"> +<p class="img-caption"> + <strong>Figure 2.</strong> Triangle drawn with a projection and camera view applied. +</p> + +<p>Now that you have applied this transformation, the triangle has three equal sides, instead of the +<a href="#squashed-triangle">squashed triangle</a> in the earlier version.</p> + +<h2 id="motion">Add Motion</h2> + +<p>While it may be an interesting exercise to create static graphic objects with OpenGL ES, chances +are you want at least <em>some</em> of your objects to move. In this section, you'll add motion to +your 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} +class. + <pre> + private float[] mMMatrix = new float[16]; + </pre> + </li> + <li>Modify your {@code onDrawFrame()} 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 + GLES20.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, mMVPMatrix, 0); + + // Draw the triangle + ... + } +</pre> + </li> + <li>Run the application and your triangle should rotate around its center.</li> +</ol> + + +<h2 id="touch">Respond to Touch Events</h2> +<p>Making objects move according to a preset program like the rotating triangle is useful for +getting some attention, but what if you want to have users interact with your OpenGL graphics? In +this section, you'll learn how listen for touch events to let users interact with objects in your +{@code HelloOpenGLES20SurfaceView}.</p> + +<p>The key to making your OpenGL application touch interactive is expanding your implementation of +{@link android.opengl.GLSurfaceView} to override the {@link +android.view.View#onTouchEvent(android.view.MotionEvent) onTouchEvent()} to listen for touch events. +Before you do that, however, you'll modify the renderer class to expose the rotation angle of the +triangle. Afterwards, you'll modify the {@code HelloOpenGLES20SurfaceView} to process touch events +and pass that data to your renderer.</p> + +<p>To make your triangle rotate in response to touch events:</p> + +<ol> + <li>Modify your {@code HelloOpenGLES20Renderer} class to include a new, public member so that +your {@code HelloOpenGLES10SurfaceView} class is able to pass new rotation values your renderer: +<pre> + public float mAngle; +</pre> + </li> + <li>In your {@code onDrawFrame()} method, comment out the code that generates an angle and +replace the {@code angle} variable with {@code mAngle}. +<pre> + // Create a rotation for the triangle (Boring! Comment this out:) + // long time = SystemClock.uptimeMillis() % 4000L; + // float angle = 0.090f * ((int) time); + + // Use the mAngle member as the rotation value + Matrix.setRotateM(mMMatrix, 0, mAngle, 0, 0, 1.0f); +</pre> + </li> + <li>In your {@code HelloOpenGLES20SurfaceView} class, add the following member variables. +<pre> + private final float TOUCH_SCALE_FACTOR = 180.0f / 320; + private HelloOpenGLES20Renderer mRenderer; + private float mPreviousX; + private float mPreviousY; +</pre> + </li> + <li>In the constructor method for {@code HelloOpenGLES20SurfaceView}, set the {@code mRenderer} +member so you have a handle to pass in rotation input and set the render mode to {@link +android.opengl.GLSurfaceView#RENDERMODE_WHEN_DIRTY}.<pre> + public HelloOpenGLES20SurfaceView(Context context){ + super(context); + // Create an OpenGL ES 2.0 context. + setEGLContextClientVersion(2); + + // set the mRenderer member + mRenderer = new HelloOpenGLES20Renderer(); + setRenderer(mRenderer); + + // Render the view only when there is a change + setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY); + } +</pre> + </li> + <li>In your {@code HelloOpenGLES20SurfaceView} class, override the {@link +android.view.View#onTouchEvent(android.view.MotionEvent) onTouchEvent()} method to listen for touch +events and pass them to your renderer. +<pre> + @Override + public boolean onTouchEvent(MotionEvent e) { + // MotionEvent reports input details from the touch screen + // and other input controls. In this case, you are only + // interested in events where the touch position changed. + + float x = e.getX(); + float y = e.getY(); + + switch (e.getAction()) { + case MotionEvent.ACTION_MOVE: + + float dx = x - mPreviousX; + float dy = y - mPreviousY; + + // reverse direction of rotation above the mid-line + if (y > getHeight() / 2) { + dx = dx * -1 ; + } + + // reverse direction of rotation to left of the mid-line + if (x < getWidth() / 2) { + dy = dy * -1 ; + } + + mRenderer.mAngle += (dx + dy) * TOUCH_SCALE_FACTOR; + requestRender(); + } + + mPreviousX = x; + mPreviousY = y; + return true; + } +</pre> + <p class="note"><strong>Note:</strong> Touch events return pixel coordinates which <em>are not the +same</em> as OpenGL coordinates. Touch coordinate [0,0] is the bottom-left of the screen and the +highest value [max_X, max_Y] is the top-right corner of the screen. To match touch events to OpenGL +graphic objects, you must translate touch coordinates into OpenGL coordinates.</p> + </li> + <li>Run the application and drag your finger or cursor around the screen to rotate the +triangle.</li> +</ol> +<p>For another example of OpenGL touch event functionality, see <a +href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/graphics/ +TouchRotateActivity.html">TouchRotateActivity</a>.</p>
\ No newline at end of file |
