Home | History | Annotate | Download | only in articles
      1 page.title=Introducing GLSurfaceView
      2 parent.title=Articles
      3 parent.link=../browser.html?tag=article
      4 @jd:body
      5 
      6 
      7 <p>The {@link android android.opengl.GLSurfaceView} class makes it 
      8 easier for you to use OpenGL ES rendering in your applications by:</p>
      9 
     10 <ul>
     11 <li>Providing the glue code to connect OpenGL ES to the {@link
     12 android.view.View} system.</li>
     13 <li>Providing the glue code to make OpenGL ES work with the {@link
     14 android.app.Activity} life-cycle.</li>
     15 <li>Making it easy to choose an appropriate frame buffer pixel format.</li>
     16 <li>Creating and managing a separate rendering thread, to enable smooth
     17 animation.</li>
     18 <li>Providing easy-to-use debugging tools for tracing OpenGL ES API calls and
     19 checking for errors.</li>
     20 </ul>
     21 
     22 <p>GLSurfaceView is a good base for building an application that uses OpenGL ES
     23 for part or all of its rendering. A 2D or 3D action game would be a good
     24 candidate, as would a 2D or 3D data visualization application such as <a
     25 href="http://www.youtube.com/watch?v=4PRfVKzuUJ4&fmt=18" title="Google Maps
     26 StreetView">Google Maps StreetView</a>.</p>
     27 
     28 <h3>A simple GLSurfaceView application</h3>
     29 
     30 <p>Here's the source code to the simplest possible OpenGL ES application:</p>
     31 
     32 <pre>package com.example.android.apis.graphics;
     33 
     34 import javax.microedition.khronos.egl.EGLConfig;
     35 import javax.microedition.khronos.opengles.GL10;
     36 
     37 import android.app.Activity;
     38 import android.opengl.GLSurfaceView;
     39 import android.os.Bundle;
     40 
     41 public class ClearActivity extends Activity {
     42     &#64;Override
     43     protected void onCreate(Bundle savedInstanceState) {
     44         super.onCreate(savedInstanceState);
     45         mGLView = new GLSurfaceView(this);
     46         mGLView.setRenderer(new ClearRenderer());
     47         setContentView(mGLView);
     48     }
     49 
     50     &#64;Override
     51     protected void onPause() {
     52         super.onPause();
     53         mGLView.onPause();
     54     }
     55 
     56     &#64;Override
     57     protected void onResume() {
     58         super.onResume();
     59         mGLView.onResume();
     60     }
     61 
     62     private GLSurfaceView mGLView;
     63 }
     64 
     65 class ClearRenderer implements GLSurfaceView.Renderer {
     66     public void onSurfaceCreated(GL10 gl, EGLConfig config) {
     67         // Do nothing special.
     68     }
     69 
     70     public void onSurfaceChanged(GL10 gl, int w, int h) {
     71         gl.glViewport(0, 0, w, h);
     72     }
     73 
     74     public void onDrawFrame(GL10 gl) {
     75         gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
     76     }
     77 }</pre>
     78 
     79 <p>This program doesn't do much: it clears the screen to black on every frame.
     80 But it is a complete OpenGL application that correctly implements the
     81 Android activity life-cycle. It pauses rendering when the activity is
     82 paused, and resumes it when the activity is resumed. You could use this
     83 application as the basis for non-interactive demonstration programs.
     84 Just add more OpenGL calls to the <code>ClearRenderer.onDrawFrame()</code> method.
     85 Notice that you don't even need to subclass the <code>GLSurfaceView</code> view.</p>
     86 
     87 <p>The {@link android.opengl.GLSurfaceView.Renderer} interface has three methods:</p>
     88 
     89 <ul>
     90 <li>The
     91 <code>onSurfaceCreated()</code> method is called at the start of rendering, and
     92 whenever the OpenGL ES drawing context has to be recreated. (The
     93 drawing context is typically lost and recreated when the activity is
     94 paused and resumed.) <code>OnSurfaceCreated()</code> is a good place to create
     95 long-lived OpenGL resources such as textures.</li>
     96 <li>The <code>onSurfaceChanged()</code>
     97 method is called when the surface changes size. It's a good place to
     98 set your OpenGL viewport. You may also want to set your camera here, if
     99 it's a fixed camera that doesn't move around the scene.</li>
    100 <li>The <code>onDrawFrame()</code> method is called every frame, and is
    101 responsible for drawing the scene. You would typically start by calling
    102 <code>glClear</code> to clear the framebuffer, followed by other OpenGL ES calls
    103 to draw the current scene.</li>
    104 </ul>
    105 
    106 <h3>How about user input?</h3>
    107 
    108 <p>If you want an interactive application (such as a game), you will typically
    109 subclass <code>GLSurfaceView</code>, because that's an easy way of obtaining
    110 input events. Here's a slightly longer example showing how to do that:</p>
    111 
    112 <pre>package com.google.android.ClearTest;
    113 
    114 import javax.microedition.khronos.egl.EGLConfig;
    115 import javax.microedition.khronos.opengles.GL10;
    116 
    117 import android.app.Activity;
    118 import android.content.Context;
    119 import android.opengl.GLSurfaceView;
    120 import android.os.Bundle;
    121 import android.view.MotionEvent;
    122 
    123 public class ClearActivity extends Activity {
    124     &#64;Override
    125     protected void onCreate(Bundle savedInstanceState) {
    126         super.onCreate(savedInstanceState);
    127         mGLView = new ClearGLSurfaceView(this);
    128         setContentView(mGLView);
    129     }
    130 
    131     &#64;Override
    132     protected void onPause() {
    133         super.onPause();
    134         mGLView.onPause();
    135     }
    136 
    137     &#64;Override
    138     protected void onResume() {
    139         super.onResume();
    140         mGLView.onResume();
    141     }
    142 
    143     private GLSurfaceView mGLView;
    144 }
    145 
    146 class ClearGLSurfaceView extends GLSurfaceView {
    147     public ClearGLSurfaceView(Context context) {
    148         super(context);
    149         mRenderer = new ClearRenderer();
    150         setRenderer(mRenderer);
    151     }
    152 
    153     public boolean onTouchEvent(final MotionEvent event) {
    154         queueEvent(new Runnable(){
    155             public void run() {
    156                 mRenderer.setColor(event.getX() / getWidth(),
    157                         event.getY() / getHeight(), 1.0f);
    158             }});
    159             return true;
    160         }
    161 
    162         ClearRenderer mRenderer;
    163 }
    164 
    165 class ClearRenderer implements GLSurfaceView.Renderer {
    166     public void onSurfaceCreated(GL10 gl, EGLConfig config) {
    167         // Do nothing special.
    168     }
    169 
    170     public void onSurfaceChanged(GL10 gl, int w, int h) {
    171         gl.glViewport(0, 0, w, h);
    172     }
    173 
    174     public void onDrawFrame(GL10 gl) {
    175         gl.glClearColor(mRed, mGreen, mBlue, 1.0f);
    176         gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
    177     }
    178 
    179     public void setColor(float r, float g, float b) {
    180         mRed = r;
    181         mGreen = g;
    182         mBlue = b;
    183     }
    184 
    185     private float mRed;
    186     private float mGreen;
    187     private float mBlue;
    188 }</pre>
    189 
    190 <p>This application clears the screen for every frame. When you tap on the
    191 screen, it sets the clear color based on the (x,y) coordinates of your touch
    192 event. Note the use of <code>queueEvent()</code> in
    193 <code>ClearGLSurfaceView.onTouchEvent()</code>. The <code>queueEvent()</code>
    194 method is used to safely communicate between the UI thread and the rendering
    195 thread. If you prefer, you can use some other Java cross-thread communication
    196 technique, such as synchronized methods on the <code>Renderer</code> class 
    197 itself. However, queueing events is often the simplest way of dealing with 
    198 cross-thread communication.</p>
    199 
    200 <h3>Other GLSurfaceView samples</h3>
    201 
    202 <p>Tired
    203 of just clearing the screen? You can find more interesting samples in
    204 the API Demos sample included in the Android SDK. All the OpenGL ES samples have been
    205 converted to use the <code>GLSurfaceView</code> view:</p>
    206 
    207 <ul>
    208 <li>GLSurfaceView - a spinning triangle</li>
    209 <li>Kube - a cube puzzle demo</li>
    210 <li>Translucent GLSurfaceView - shows how to display 3D graphics on a translucent background</li>
    211 <li>Textured Triangle - shows how to draw a textured 3D triangle</li>
    212 <li>Sprite Text - shows how to draw text into a texture and then composite it into a 3D scene</li>
    213 <li>Touch Rotate - shows how to rotate a 3D object in response to user input.</li>
    214 </ul>
    215 
    216 <h3>Choosing a surface</h3>
    217 
    218 <p><code>GLSurfaceView</code>
    219 helps you choose the type of surface to render to. Different Android
    220 devices support different types of surfaces, with no common subset.
    221 This makes it tricky problem to choose the best available surface on
    222 each device. </p>
    223 
    224 <p>By default, <code>GLSurfaceView</code> tries to find a surface that's as
    225 close as possible to a 16-bit RGB frame buffer with a 16-bit depth
    226 buffer. Depending upon your application's needs you may want to change
    227 this behavior. For example, the Translucent GLSurfaceView sample needs
    228 an Alpha channel in order to render translucent data. <code>GLSurfaceView</code>
    229 provides an overloaded <code>setEGLSurfaceChooser()</code> method to give 
    230 you control over which surface type is chosen:</p>
    231 
    232 <dl>
    233 <dt><code>setEGLConfigChooser(boolean needDepth)</code></dt>
    234 <dd>Choose a config that's closest to R5G6B5 with or without a 16-bit framebuffer</dd>
    235 <dt><code>setEGLConfigChooser(int redSize, int greenSize,int blueSize, 
    236 int alphaSize,int depthSize, int stencilSize)</code></dt>
    237 <dd>Choose the config with the fewest number of bits per pixel that has at least
    238 as many bits-per-channel as specified in the constructor.</dd>
    239 <dt><code>setEGLConfigChooser(EGLConfigChooser configChooser)</code></dt>
    240 <dd>Allow total control over choosing a configuration. You pass in your own
    241 implementation of <code>EGLConfigChooser</code>, which gets to inspect the
    242 device's capabilities and choose a configuration.</dd>
    243 </dl>
    244 
    245 <h3>Continuous rendering versus render-when-dirty</h3>
    246 
    247 <p>Most 3D applications, such as games or simulations, are continuously
    248 animated. But some 3D applications are more reactive: they wait passively until
    249 the user does something, and then react to it. For those types of applications,
    250 the default <code>GLSurfaceView</code> behavior of continuously redrawing the
    251 screen is a waste of time. If you are developing a reactive application, you can
    252 call <code>GLSurfaceView.setRenderMode(RENDERMODE_WHEN_DIRTY)</code>, which
    253 turns off the continuous animation. Then you call
    254 <code>GLSurfaceView.requestRender()</code> whenever you want to re-render.</p>
    255 
    256 <h3>Help With debugging</h3>
    257 
    258 <p><code>GLSurfaceView</code> has a handy built-in feature for debugging OpenGL ES
    259 applications: the <code>GLSurfaceView.setDebugFlags()</code> method can be used
    260 to enable logging and/or error checking your OpenGL ES calls. Call this method
    261 in your <code>GLSurfaceView</code>'s constructor, before calling
    262 <code>setRenderer()</code>:</p>
    263 
    264 <pre>public ClearGLSurfaceView(Context context) {
    265     super(context);
    266     // Turn on error-checking and logging
    267     setDebugFlags(DEBUG_CHECK_GL_ERROR | DEBUG_LOG_GL_CALLS);
    268     mRenderer = new ClearRenderer();
    269     setRenderer(mRenderer);
    270 }</pre>