| Dirk Dougherty | 22558d0 | 2009-12-10 16:25:06 -0800 | [diff] [blame] | 1 | page.title=Introducing GLSurfaceView |
| Scott Main | 796ce77 | 2011-02-16 10:04:45 -0800 | [diff] [blame] | 2 | parent.title=Articles |
| 3 | parent.link=../browser.html?tag=article |
| Dirk Dougherty | 22558d0 | 2009-12-10 16:25:06 -0800 | [diff] [blame] | 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 | @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 | @Override |
| 51 | protected void onPause() { |
| 52 | super.onPause(); |
| 53 | mGLView.onPause(); |
| 54 | } |
| 55 | |
| 56 | @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 | @Override |
| 125 | protected void onCreate(Bundle savedInstanceState) { |
| 126 | super.onCreate(savedInstanceState); |
| 127 | mGLView = new ClearGLSurfaceView(this); |
| 128 | setContentView(mGLView); |
| 129 | } |
| 130 | |
| 131 | @Override |
| 132 | protected void onPause() { |
| 133 | super.onPause(); |
| 134 | mGLView.onPause(); |
| 135 | } |
| 136 | |
| 137 | @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> |