268 lines
9.9 KiB
Plaintext
268 lines
9.9 KiB
Plaintext
|
page.title=Introducing GLSurfaceView
|
||
|
@jd:body
|
||
|
|
||
|
|
||
|
<p>The {@link android android.opengl.GLSurfaceView} class makes it
|
||
|
easier for you to use OpenGL ES rendering in your applications by:</p>
|
||
|
|
||
|
<ul>
|
||
|
<li>Providing the glue code to connect OpenGL ES to the {@link
|
||
|
android.view.View} system.</li>
|
||
|
<li>Providing the glue code to make OpenGL ES work with the {@link
|
||
|
android.app.Activity} life-cycle.</li>
|
||
|
<li>Making it easy to choose an appropriate frame buffer pixel format.</li>
|
||
|
<li>Creating and managing a separate rendering thread, to enable smooth
|
||
|
animation.</li>
|
||
|
<li>Providing easy-to-use debugging tools for tracing OpenGL ES API calls and
|
||
|
checking for errors.</li>
|
||
|
</ul>
|
||
|
|
||
|
<p>GLSurfaceView is a good base for building an application that uses OpenGL ES
|
||
|
for part or all of its rendering. A 2D or 3D action game would be a good
|
||
|
candidate, as would a 2D or 3D data visualization application such as <a
|
||
|
href="http://www.youtube.com/watch?v=4PRfVKzuUJ4&fmt=18" title="Google Maps
|
||
|
StreetView">Google Maps StreetView</a>.</p>
|
||
|
|
||
|
<h3>A simple GLSurfaceView application</h3>
|
||
|
|
||
|
<p>Here's the source code to the simplest possible OpenGL ES application:</p>
|
||
|
|
||
|
<pre>package com.example.android.apis.graphics;
|
||
|
|
||
|
import javax.microedition.khronos.egl.EGLConfig;
|
||
|
import javax.microedition.khronos.opengles.GL10;
|
||
|
|
||
|
import android.app.Activity;
|
||
|
import android.opengl.GLSurfaceView;
|
||
|
import android.os.Bundle;
|
||
|
|
||
|
public class ClearActivity extends Activity {
|
||
|
@Override
|
||
|
protected void onCreate(Bundle savedInstanceState) {
|
||
|
super.onCreate(savedInstanceState);
|
||
|
mGLView = new GLSurfaceView(this);
|
||
|
mGLView.setRenderer(new ClearRenderer());
|
||
|
setContentView(mGLView);
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
protected void onPause() {
|
||
|
super.onPause();
|
||
|
mGLView.onPause();
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
protected void onResume() {
|
||
|
super.onResume();
|
||
|
mGLView.onResume();
|
||
|
}
|
||
|
|
||
|
private GLSurfaceView mGLView;
|
||
|
}
|
||
|
|
||
|
class ClearRenderer implements GLSurfaceView.Renderer {
|
||
|
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
|
||
|
// Do nothing special.
|
||
|
}
|
||
|
|
||
|
public void onSurfaceChanged(GL10 gl, int w, int h) {
|
||
|
gl.glViewport(0, 0, w, h);
|
||
|
}
|
||
|
|
||
|
public void onDrawFrame(GL10 gl) {
|
||
|
gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
|
||
|
}
|
||
|
}</pre>
|
||
|
|
||
|
<p>This program doesn't do much: it clears the screen to black on every frame.
|
||
|
But it is a complete OpenGL application that correctly implements the
|
||
|
Android activity life-cycle. It pauses rendering when the activity is
|
||
|
paused, and resumes it when the activity is resumed. You could use this
|
||
|
application as the basis for non-interactive demonstration programs.
|
||
|
Just add more OpenGL calls to the <code>ClearRenderer.onDrawFrame()</code> method.
|
||
|
Notice that you don't even need to subclass the <code>GLSurfaceView</code> view.</p>
|
||
|
|
||
|
<p>The {@link android.opengl.GLSurfaceView.Renderer} interface has three methods:</p>
|
||
|
|
||
|
<ul>
|
||
|
<li>The
|
||
|
<code>onSurfaceCreated()</code> method is called at the start of rendering, and
|
||
|
whenever the OpenGL ES drawing context has to be recreated. (The
|
||
|
drawing context is typically lost and recreated when the activity is
|
||
|
paused and resumed.) <code>OnSurfaceCreated()</code> is a good place to create
|
||
|
long-lived OpenGL resources such as textures.</li>
|
||
|
<li>The <code>onSurfaceChanged()</code>
|
||
|
method is called when the surface changes size. It's a good place to
|
||
|
set your OpenGL viewport. You may also want to set your camera here, if
|
||
|
it's a fixed camera that doesn't move around the scene.</li>
|
||
|
<li>The <code>onDrawFrame()</code> method is called every frame, and is
|
||
|
responsible for drawing the scene. You would typically start by calling
|
||
|
<code>glClear</code> to clear the framebuffer, followed by other OpenGL ES calls
|
||
|
to draw the current scene.</li>
|
||
|
</ul>
|
||
|
|
||
|
<h3>How about user input?</h3>
|
||
|
|
||
|
<p>If you want an interactive application (such as a game), you will typically
|
||
|
subclass <code>GLSurfaceView</code>, because that's an easy way of obtaining
|
||
|
input events. Here's a slightly longer example showing how to do that:</p>
|
||
|
|
||
|
<pre>package com.google.android.ClearTest;
|
||
|
|
||
|
import javax.microedition.khronos.egl.EGLConfig;
|
||
|
import javax.microedition.khronos.opengles.GL10;
|
||
|
|
||
|
import android.app.Activity;
|
||
|
import android.content.Context;
|
||
|
import android.opengl.GLSurfaceView;
|
||
|
import android.os.Bundle;
|
||
|
import android.view.MotionEvent;
|
||
|
|
||
|
public class ClearActivity extends Activity {
|
||
|
@Override
|
||
|
protected void onCreate(Bundle savedInstanceState) {
|
||
|
super.onCreate(savedInstanceState);
|
||
|
mGLView = new ClearGLSurfaceView(this);
|
||
|
setContentView(mGLView);
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
protected void onPause() {
|
||
|
super.onPause();
|
||
|
mGLView.onPause();
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
protected void onResume() {
|
||
|
super.onResume();
|
||
|
mGLView.onResume();
|
||
|
}
|
||
|
|
||
|
private GLSurfaceView mGLView;
|
||
|
}
|
||
|
|
||
|
class ClearGLSurfaceView extends GLSurfaceView {
|
||
|
public ClearGLSurfaceView(Context context) {
|
||
|
super(context);
|
||
|
mRenderer = new ClearRenderer();
|
||
|
setRenderer(mRenderer);
|
||
|
}
|
||
|
|
||
|
public boolean onTouchEvent(final MotionEvent event) {
|
||
|
queueEvent(new Runnable(){
|
||
|
public void run() {
|
||
|
mRenderer.setColor(event.getX() / getWidth(),
|
||
|
event.getY() / getHeight(), 1.0f);
|
||
|
}});
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
ClearRenderer mRenderer;
|
||
|
}
|
||
|
|
||
|
class ClearRenderer implements GLSurfaceView.Renderer {
|
||
|
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
|
||
|
// Do nothing special.
|
||
|
}
|
||
|
|
||
|
public void onSurfaceChanged(GL10 gl, int w, int h) {
|
||
|
gl.glViewport(0, 0, w, h);
|
||
|
}
|
||
|
|
||
|
public void onDrawFrame(GL10 gl) {
|
||
|
gl.glClearColor(mRed, mGreen, mBlue, 1.0f);
|
||
|
gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
|
||
|
}
|
||
|
|
||
|
public void setColor(float r, float g, float b) {
|
||
|
mRed = r;
|
||
|
mGreen = g;
|
||
|
mBlue = b;
|
||
|
}
|
||
|
|
||
|
private float mRed;
|
||
|
private float mGreen;
|
||
|
private float mBlue;
|
||
|
}</pre>
|
||
|
|
||
|
<p>This application clears the screen for every frame. When you tap on the
|
||
|
screen, it sets the clear color based on the (x,y) coordinates of your touch
|
||
|
event. Note the use of <code>queueEvent()</code> in
|
||
|
<code>ClearGLSurfaceView.onTouchEvent()</code>. The <code>queueEvent()</code>
|
||
|
method is used to safely communicate between the UI thread and the rendering
|
||
|
thread. If you prefer, you can use some other Java cross-thread communication
|
||
|
technique, such as synchronized methods on the <code>Renderer</code> class
|
||
|
itself. However, queueing events is often the simplest way of dealing with
|
||
|
cross-thread communication.</p>
|
||
|
|
||
|
<h3>Other GLSurfaceView samples</h3>
|
||
|
|
||
|
<p>Tired
|
||
|
of just clearing the screen? You can find more interesting samples in
|
||
|
the API Demos sample included in the Android SDK. All the OpenGL ES samples have been
|
||
|
converted to use the <code>GLSurfaceView</code> view:</p>
|
||
|
|
||
|
<ul>
|
||
|
<li>GLSurfaceView - a spinning triangle</li>
|
||
|
<li>Kube - a cube puzzle demo</li>
|
||
|
<li>Translucent GLSurfaceView - shows how to display 3D graphics on a translucent background</li>
|
||
|
<li>Textured Triangle - shows how to draw a textured 3D triangle</li>
|
||
|
<li>Sprite Text - shows how to draw text into a texture and then composite it into a 3D scene</li>
|
||
|
<li>Touch Rotate - shows how to rotate a 3D object in response to user input.</li>
|
||
|
</ul>
|
||
|
|
||
|
<h3>Choosing a surface</h3>
|
||
|
|
||
|
<p><code>GLSurfaceView</code>
|
||
|
helps you choose the type of surface to render to. Different Android
|
||
|
devices support different types of surfaces, with no common subset.
|
||
|
This makes it tricky problem to choose the best available surface on
|
||
|
each device. </p>
|
||
|
|
||
|
<p>By default, <code>GLSurfaceView</code> tries to find a surface that's as
|
||
|
close as possible to a 16-bit RGB frame buffer with a 16-bit depth
|
||
|
buffer. Depending upon your application's needs you may want to change
|
||
|
this behavior. For example, the Translucent GLSurfaceView sample needs
|
||
|
an Alpha channel in order to render translucent data. <code>GLSurfaceView</code>
|
||
|
provides an overloaded <code>setEGLSurfaceChooser()</code> method to give
|
||
|
you control over which surface type is chosen:</p>
|
||
|
|
||
|
<dl>
|
||
|
<dt><code>setEGLConfigChooser(boolean needDepth)</code></dt>
|
||
|
<dd>Choose a config that's closest to R5G6B5 with or without a 16-bit framebuffer</dd>
|
||
|
<dt><code>setEGLConfigChooser(int redSize, int greenSize,int blueSize,
|
||
|
int alphaSize,int depthSize, int stencilSize)</code></dt>
|
||
|
<dd>Choose the config with the fewest number of bits per pixel that has at least
|
||
|
as many bits-per-channel as specified in the constructor.</dd>
|
||
|
<dt><code>setEGLConfigChooser(EGLConfigChooser configChooser)</code></dt>
|
||
|
<dd>Allow total control over choosing a configuration. You pass in your own
|
||
|
implementation of <code>EGLConfigChooser</code>, which gets to inspect the
|
||
|
device's capabilities and choose a configuration.</dd>
|
||
|
</dl>
|
||
|
|
||
|
<h3>Continuous rendering versus render-when-dirty</h3>
|
||
|
|
||
|
<p>Most 3D applications, such as games or simulations, are continuously
|
||
|
animated. But some 3D applications are more reactive: they wait passively until
|
||
|
the user does something, and then react to it. For those types of applications,
|
||
|
the default <code>GLSurfaceView</code> behavior of continuously redrawing the
|
||
|
screen is a waste of time. If you are developing a reactive application, you can
|
||
|
call <code>GLSurfaceView.setRenderMode(RENDERMODE_WHEN_DIRTY)</code>, which
|
||
|
turns off the continuous animation. Then you call
|
||
|
<code>GLSurfaceView.requestRender()</code> whenever you want to re-render.</p>
|
||
|
|
||
|
<h3>Help With debugging</h3>
|
||
|
|
||
|
<p><code>GLSurfaceView</code> has a handy built-in feature for debugging OpenGL ES
|
||
|
applications: the <code>GLSurfaceView.setDebugFlags()</code> method can be used
|
||
|
to enable logging and/or error checking your OpenGL ES calls. Call this method
|
||
|
in your <code>GLSurfaceView</code>'s constructor, before calling
|
||
|
<code>setRenderer()</code>:</p>
|
||
|
|
||
|
<pre>public ClearGLSurfaceView(Context context) {
|
||
|
super(context);
|
||
|
// Turn on error-checking and logging
|
||
|
setDebugFlags(DEBUG_CHECK_GL_ERROR | DEBUG_LOG_GL_CALLS);
|
||
|
mRenderer = new ClearRenderer();
|
||
|
setRenderer(mRenderer);
|
||
|
}</pre>
|