diff options
Diffstat (limited to 'docs/html/training/graphics/opengl')
| -rw-r--r-- | docs/html/training/graphics/opengl/draw.jd | 195 | ||||
| -rw-r--r-- | docs/html/training/graphics/opengl/environment.jd | 223 | ||||
| -rw-r--r-- | docs/html/training/graphics/opengl/index.jd | 75 | ||||
| -rw-r--r-- | docs/html/training/graphics/opengl/motion.jd | 92 | ||||
| -rw-r--r-- | docs/html/training/graphics/opengl/projection.jd | 152 | ||||
| -rw-r--r-- | docs/html/training/graphics/opengl/shapes.jd | 153 | ||||
| -rw-r--r-- | docs/html/training/graphics/opengl/touch.jd | 145 |
7 files changed, 1035 insertions, 0 deletions
diff --git a/docs/html/training/graphics/opengl/draw.jd b/docs/html/training/graphics/opengl/draw.jd new file mode 100644 index 0000000..156ff70 --- /dev/null +++ b/docs/html/training/graphics/opengl/draw.jd @@ -0,0 +1,195 @@ +page.title=Drawing Shapes +parent.title=Displaying Graphics with OpenGL ES +parent.link=index.html + +trainingnavtop=true +previous.title=Defining Shapes +previous.link=environment.html +next.title=Applying Projection and Camera Views +next.link=projection.html + +@jd:body + +<div id="tb-wrapper"> +<div id="tb"> + +<h2>This lesson teaches you to</h2> +<ol> + <li><a href="#initialize">Initialize Shapes</a></li> + <li><a href="#draw">Draw a Shape</a></li> +</ol> + +<h2>You should also read</h2> +<ul> + <li><a href="{@docRoot}guide/topics/graphics/opengl.html">OpenGL</a></li> +</ul> + +<div class="download-box"> + <a href="{@docRoot}shareables/training/OpenGLES.zip" +class="button">Download the sample</a> + <p class="filename">OpenGLES.zip</p> +</div> + +</div> +</div> + +<p>After you define shapes to be drawn with OpenGL, you probably want to draw them. Drawing shapes +with the OpenGL ES 2.0 takes a bit more code than you might imagine, because the API provides a +great deal of control over the graphics rendering pipeline.</p> + +<p>This lesson explains how to draw the shapes you defined in the previous lesson using the OpenGL +ES 2.0 API.</p> + + +<h2 id="initialize">Initialize Shapes</h2> + +<p>Before you do any drawing, you must initialize and load the shapes you plan to draw. Unless the +structure (the original coordinates) of the shapes you use in your program change during the course +of execution, you should initialize them in the {@link +android.opengl.GLSurfaceView.Renderer#onSurfaceCreated onSurfaceCreated()} method of your renderer +for memory and processing efficiency.</p> + +<pre> +public void onSurfaceCreated(GL10 unused, EGLConfig config) { + ... + + // initialize a triangle + mTriangle = new Triangle(); + // initialize a square + mSquare = new Square(); +} +</pre> + + +<h2 id="draw">Draw a Shape</h2> + +<p>Drawing a defined shape using OpenGL ES 2.0 requires a significant amount of code, because you +must provide a lot of details to the graphics rendering pipeline. Specifically, you must define the +following:</p> + +<ul> + <li><em>Vertex Shader</em> - OpenGL ES graphics code for rendering the vertices of a shape.</li> + <li><em>Fragment Shader</em> - OpenGL ES code for rendering the face of a shape with colors or +textures.</li> + <li><em>Program</em> - An OpenGL ES object that contains the shaders you want to use for drawing +one or more shapes.</li> +</ul> + +<p>You need at least one vertex shader to draw a shape and one fragment shader to color that shape. +These shaders must be complied and then added to an OpenGL ES program, which is then used to draw +the shape. Here is an example of how to define basic shaders you can use to draw a shape:</p> + +<pre> +private final String vertexShaderCode = + "attribute vec4 vPosition;" + + "void main() {" + + " gl_Position = vPosition;" + + "}"; + +private final String fragmentShaderCode = + "precision mediump float;" + + "uniform vec4 vColor;" + + "void main() {" + + " gl_FragColor = vColor;" + + "}"; +</pre> + +<p>Shaders contain OpenGL Shading Language (GLSL) code that must be compiled prior to using it in +the OpenGL ES environment. To compile this code, create a utility method in your renderer class:</p> + +<pre> +public static 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> + +<p>In order to draw your shape, you must compile the shader code, add them to a OpenGL ES program +object and then link the program. Do this in your drawn object’s constructor, so it is only done +once.</p> + +<p class="note"><strong>Note:</strong> Compiling OpenGL ES shaders and linking programs is expensive +in terms of CPU cycles and processing time, so you should avoid doing this more than once. If you do +not know the content of your shaders at runtime, you should build your code such that they only +get created once and then cached for later use.</p> + +<pre> +public Triangle() { + ... + + int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode); + int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode); + + mProgram = GLES20.glCreateProgram(); // create empty OpenGL ES 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 ES program executables +} +</pre> + +<p>At this point, you are ready to add the actual calls that draw your shape. Drawing shapes with +OpenGL ES requires that you specify several parameters to tell the rendering pipeline what you want +to draw and how to draw it. Since drawing options can vary by shape, it's a good idea to have your +shape classes contain their own drawing logic.</p> + +<p>Create a {@code draw()} method for drawing the shape. This code sets the position and +color values to the shape’s vertex shader and fragment shader, and then executes the drawing +function.</p> + +<pre> +public void draw() { + // Add program to OpenGL ES environment + GLES20.glUseProgram(mProgram); + + // get handle to vertex shader's vPosition member + mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition"); + + // Enable a handle to the triangle vertices + GLES20.glEnableVertexAttribArray(mPositionHandle); + + // Prepare the triangle coordinate data + GLES20.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX, + GLES20.GL_FLOAT, false, + vertexStride, vertexBuffer); + + // get handle to fragment shader's vColor member + mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor"); + + // Set color for drawing the triangle + GLES20.glUniform4fv(mColorHandle, 1, color, 0); + + // Draw the triangle + GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount); + + // Disable vertex array + GLES20.glDisableVertexAttribArray(mPositionHandle); +} +</pre> + +<p>Once you have all this code in place, drawing this object just requires a call to the +{@code draw()} method from within your renderer’s {@link +android.opengl.GLSurfaceView.Renderer#onDrawFrame onDrawFrame()} method. When you run the +application, it should look something like this:</p> + +<img src="{@docRoot}images/opengl/ogl-triangle.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 code 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’s +vertices have not been corrected for the proportions of the screen area where the {@link +android.opengl.GLSurfaceView} is displayed. You can fix that problem using a projection and camera +view in the next lesson.</p> + +<p>Lastly, the triangle is stationary, which is a bit boring. In the <a href="motion.html">Adding +Motion</a> lesson, you make this shape rotate and make more interesting use of the OpenGL ES +graphics pipeline.</p>
\ No newline at end of file diff --git a/docs/html/training/graphics/opengl/environment.jd b/docs/html/training/graphics/opengl/environment.jd new file mode 100644 index 0000000..e1e2c8a --- /dev/null +++ b/docs/html/training/graphics/opengl/environment.jd @@ -0,0 +1,223 @@ +page.title=Building an OpenGL ES Environment +parent.title=Displaying Graphics with OpenGL ES +parent.link=index.html + +trainingnavtop=true +previous.title=Displaying Graphics with OpenGL ES +previous.link=index.html +next.title=Defining Shapes +next.link=shapes.html + +@jd:body + +<div id="tb-wrapper"> +<div id="tb"> + +<h2>This lesson teaches you to</h2> +<ol> + <li><a href="#manifest">Declare OpenGL ES Use in the Manifest</a></li> + <li><a href="#activity">Create an Activity for OpenGL ES Graphics</a></li> + <li><a href="#glsurfaceview">Build a GLSurfaceView Object</a></li> + <li><a href="#renderer">Build a Renderer Class</a></li> +</ol> + +<h2>You should also read</h2> +<ul> + <li><a href="{@docRoot}guide/topics/graphics/opengl.html">OpenGL</a></li> +</ul> + +<h2>Try it out</h2> + +<div class="download-box"> + <a href="{@docRoot}shareables/training/OpenGLES.zip" +class="button">Download the sample</a> + <p class="filename">OpenGLES.zip</p> +</div> + +</div> +</div> + + +<p>In order to draw graphics with OpenGL ES in your Android application, you must create a +view container for them. One of the more straight-forward ways to do this is to implement both a +{@link android.opengl.GLSurfaceView} and a {@link android.opengl.GLSurfaceView.Renderer}. A {@link +android.opengl.GLSurfaceView} is a view container for graphics drawn with OpenGL and {@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">OpenGL ES</a> +developer guide.</p> + +<p>{@link android.opengl.GLSurfaceView} is just one way to incorporate OpenGL ES graphics into your +application. For a full-screen or near-full screen graphics view, it is a reasonable choice. +Developers who want to incorporate OpenGL ES graphics in a small portion of their layouts should +take a look at {@link android.view.TextureView}. For real, do-it-yourself developers, it is also +possible to build up an OpenGL ES view using {@link android.view.SurfaceView}, but this requires +writing quite a bit of additional code.</p> + +<p>This lesson explains how to complete a minimal implementation of {@link +android.opengl.GLSurfaceView} and {@link android.opengl.GLSurfaceView.Renderer} in a simple +application activity.</p> + + +<h2 id="manifest">Declare OpenGL ES Use in the Manifest</h2> + +<p>In order for your application to use the OpenGL ES 2.0 API, you must add the following +declaration to your manifest:</p> + +<pre> +<uses-feature android:glEsVersion="0x00020000" android:required="true" /> +</pre> + +<p>If your application uses texture compression, you must also declare which compression formats +you support so that devices that do not support theses formats do not try to run your +application:</p> + +<pre> +<supports-gl-texture android:name="GL_OES_compressed_ETC1_RGB8_texture" /> +<supports-gl-texture android:name="GL_OES_compressed_paletted_texture" /> +</pre> + +<p>For more information about texture compression formats, see the +<a href="{@docRoot}guide/topics/graphics/opengl.html#textures">OpenGL</a> developer guide.</p> + + +<h2 id="activity">Create an Activity for OpenGL ES Graphics</h2> + +<p>Android applications that use OpenGL ES have activities just like any other application that has +a user interface. The main difference from other applications is what you put in the layout for your +activity. While in many applications you might use {@link android.widget.TextView}, {@link +android.widget.Button} and {@link android.widget.ListView}, in an app that uses OpenGL ES, you can +also add a {@link android.opengl.GLSurfaceView}.</p> + +<p>The following code example shows a minimal implementation of an activity that uses a +{@link android.opengl.GLSurfaceView} as its primary view:</p> + +<pre> +public class OpenGLES20 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 MyGLSurfaceView(this); + setContentView(mGLView); + } +} +</pre> + +<p class="note"><strong>Note:</strong> OpenGL ES 2.0 requires Android 2.2 (API Level 8) or higher, +so make sure your Android project targets that API or higher.</p> + + +<h2 id="glsurfaceview">Build a GLSurfaceView Object</h2> + +<p>A {@link android.opengl.GLSurfaceView} is a specialized view where you can draw OpenGL ES +graphics. +It does not do much by itself. The actual drawing of objects is controlled in the {@link +android.opengl.GLSurfaceView.Renderer} that you set on this view. In fact, the code for this object +is so thin, you may be tempted to skip extending it and just create an unmodified {@link +android.opengl.GLSurfaceView} instance, but don’t do that. You need to extend this class in +order to capture touch events, which is covered in the <a href="#touch.html">Responding to Touch +Events</a> lesson.</p> + +<p>The essential code for a {@link android.opengl.GLSurfaceView} is minimal, so for a quick +implementation, it is common to +just create an inner class in the activity that uses it:</p> + +<pre> +class MyGLSurfaceView extends GLSurfaceView { + + public MyGLSurfaceView(Context context){ + super(context); + + // Set the Renderer for drawing on the GLSurfaceView + setRenderer(new MyRenderer()); + } +} +</pre> + +<p>When using OpenGL ES 2.0, you must add another call to your {@link android.opengl.GLSurfaceView} +constructor, specifying that you want to use the 2.0 API:</p> + +<pre> +// Create an OpenGL ES 2.0 context +setEGLContextClientVersion(2); +</pre> + +<p class="note"><strong>Note:</strong> If you are using the OpenGL ES 2.0 API, make sure you declare +this in your application manifest. For more information, see <a href="#manifest">Declare OpenGL ES +Use +in the Manifest</a>.</p> + +<p>One other optional addition to your {@link android.opengl.GLSurfaceView} implementation is to set +the render mode to only draw the view when there is a change to your drawing data using the +{@link android.opengl.GLSurfaceView#RENDERMODE_WHEN_DIRTY GLSurfaceView.RENDERMODE_WHEN_DIRTY} +setting:</p> + +<pre> +// Render the view only when there is a change in the drawing data +setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY); +</pre> + +<p>This setting prevents the {@link android.opengl.GLSurfaceView} frame from being redrawn until you +call {@link android.opengl.GLSurfaceView#requestRender requestRender()}, which is more +efficient for this sample app.</p> + + +<h2 id="renderer">Build a Renderer Class</h2> + +<p>The implementation of the {@link android.opengl.GLSurfaceView.Renderer} class, or renderer, +within an application that uses OpenGL ES is where things start to get interesting. This class +controls +what gets drawn on the {@link android.opengl.GLSurfaceView} with which it is associated. There are +three methods in a renderer that are called by the Android system in order to figure out what and +how to draw on a {@link android.opengl.GLSurfaceView}:</p> + +<ul> + <li>{@link android.opengl.GLSurfaceView.Renderer#onSurfaceCreated onSurfaceCreated()} - +Called once to set up the view's OpenGL ES environment.</li> + <li>{@link android.opengl.GLSurfaceView.Renderer#onDrawFrame onDrawFrame()} - Called for each +redraw of the view.</li> + <li>{@link android.opengl.GLSurfaceView.Renderer#onSurfaceChanged onSurfaceChanged()} - Called if +the geometry of the view changes, for example when the device's screen orientation changes. + </li> +</ul> + +<p>Here is a very basic implementation of an OpenGL ES renderer, that does nothing more than draw a +gray background in the {@link android.opengl.GLSurfaceView}:</p> + +<pre> +public class MyGL20Renderer 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); + } + + public void onSurfaceChanged(GL10 unused, int width, int height) { + GLES20.glViewport(0, 0, width, height); + } +} +</pre> + +<p>That’s all there is to it! The code examples above create a simple Android application that +displays a gray screen using OpenGL. While this code does not do anything very interesting, by +creating these classes, you have laid the foundation you need to start drawing graphic elements with +OpenGL.</p> + +<p class="note"><strong>Note:</strong> You may wonder why these methods have a {@link +javax.microedition.khronos.opengles.GL10} parameter, when you are using the OpengGL ES 2.0 APIs. +These method signatures are simply reused for the 2.0 APIs to keep the Android framework code +simpler.</p> + +<p>If you are familiar with the OpenGL ES APIs, you should now be able to set up a OpenGL ES +environment in your app and start drawing graphics. However, if you need a bit more help getting +started with OpenGL, head on to the next lessons for a few more hints.</p> diff --git a/docs/html/training/graphics/opengl/index.jd b/docs/html/training/graphics/opengl/index.jd new file mode 100644 index 0000000..23a734a --- /dev/null +++ b/docs/html/training/graphics/opengl/index.jd @@ -0,0 +1,75 @@ +page.title=Displaying Graphics with OpenGL ES +trainingnavtop=true +next.title=Building an OpenGL ES Environment +next.link=environment.html + +@jd:body + +<div id="tb-wrapper"> +<div id="tb"> + +<h2>Dependencies and prerequisites</h2> +<ul> + <li>Android 2.2 (API Level 8) or higher</li> + <li>Experience building an <a href="{@docRoot}training/basics/firstapp/index.html">Android +app</a></li> +</ul> + +<h2>You should also read</h2> +<ul> + <li><a href="{@docRoot}guide/topics/graphics/opengl.html">OpenGL</a></li> +</ul> + +<h2>Try it out</h2> + +<div class="download-box"> + <a href="{@docRoot}shareables/training/OpenGLES.zip" +class="button">Download the sample</a> + <p class="filename">OpenGLES.zip</p> +</div> + +</div> +</div> + +<p>The Android framework provides plenty of standard tools for creating attractive, functional +graphical user interfaces. However, if you want more control of what your application draws on +screen, or are venturing into three dimensional graphics, you need to use a different tool. The +OpenGL ES APIs provided by the Android framework offers a set of tools for displaying high-end, +animated graphics that are limited only by your imagination and can also benefit from the +acceleration of graphics processing units (GPUs) provided on many Android devices.</p> + +<p>This class walks you through the basics of developing applications that use OpenGL, including +setup, drawing objects, moving drawn elements and responding to touch input.</p> + +<p>The example code in this class uses the OpenGL ES 2.0 APIs, which is the recommended API version +to use with current Android devices. For more information about versions of OpenGL ES, see the <a +href="{@docRoot}guide/topics/graphics/opengl.html#choosing-version">OpenGL</a> +developer guide.</p> + +<p class="note"><strong>Note:</strong> Be careful not to mix OpenGL ES 1.x API calls with OpenGL +ES 2.0 methods! The two APIs are not interchangeable and trying to use them together only results in +frustration and sadness.</p> + + +<h2>Lessons</h2> + +<dl> + <dt><b><a href="environment.html">Building an OpenGL ES Environment</a></b></dt> + <dd>Learn how to set up an Android application to be able to draw OpenGL graphics.</dd> + + <dt><b><a href="shapes.html">Defining Shapes</a></b></dt> + <dd>Learn how to define shapes and why you need to know about faces and winding.</dd> + + <dt><b><a href="draw.html">Drawing Shapes</a></b></dt> + <dd>Learn how to draw OpenGL shapes in your application.</dd> + + <dt><b><a href="projection.html">Applying Projection and Camera Views</a></b></dt> + <dd>Learn how to use projection and camera views to get a new perspective on your drawn +objects.</dd> + + <dt><b><a href="motion.html">Adding Motion</a></b></dt> + <dd>Learn how to do basic movement and animation of drawn objects with OpenGL.</dd> + + <dt><b><a href="touch.html">Responding to Touch Events</a></b></dt> + <dd>Learn how to do basic interaction with OpenGL graphics.</dd> +</dl> diff --git a/docs/html/training/graphics/opengl/motion.jd b/docs/html/training/graphics/opengl/motion.jd new file mode 100644 index 0000000..6888235 --- /dev/null +++ b/docs/html/training/graphics/opengl/motion.jd @@ -0,0 +1,92 @@ +page.title=Adding Motion +parent.title=Displaying Graphics with OpenGL ES +parent.link=index.html + +trainingnavtop=true +previous.title=Applying Projection and Camera Views +previous.link=projection.html +next.title=Responding to Touch Events +next.link=touch.html + +@jd:body + +<div id="tb-wrapper"> +<div id="tb"> + +<h2>This lesson teaches you to</h2> +<ol> + <li><a href="#rotate-gl1">Rotate a Shape</a></li> + <li><a href="#cont-render">Enable Continuous Rendering</a></li> +</ol> + +<h2>You should also read</h2> +<ul> + <li><a href="{@docRoot}guide/topics/graphics/opengl.html">OpenGL</a></li> +</ul> + +<div class="download-box"> + <a href="http://developer.android.com/shareables/training/OpenGLES.zip" +class="button">Download the sample</a> + <p class="filename">OpenGLES.zip</p> +</div> + +</div> +</div> + +<p>Drawing objects on screen is a pretty basic feature of OpenGL, but you can do this with other +Android graphics framwork classes, including {@link android.graphics.Canvas} and +{@link android.graphics.drawable.Drawable} objects. OpenGL ES provides additional capabilities for +moving and transforming drawn objects in three dimensions or in other unique ways to create +compelling user experiences.</p> + +<p>In this lesson, you take another step forward into using OpenGL ES by learning how to add motion +to a shape with rotation.</p> + + +<h2 id="rotate">Rotate a Shape</h2> + +<p>Rotating a drawing object with OpenGL ES 2.0 is relatively simple. You create another +transformation matrix (a rotation matrix) and then combine it with your projection and +camera view tranformation matrices:</p> + +<pre> +private float[] mRotationMatrix = new float[16]; +public void onDrawFrame(GL10 gl) { + ... + // Create a rotation transformation for the triangle + long time = SystemClock.uptimeMillis() % 4000L; + float angle = 0.090f * ((int) time); + Matrix.setRotateM(mRotationMatrix, 0, mAngle, 0, 0, -1.0f); + + // Combine the rotation matrix with the projection and camera view + Matrix.multiplyMM(mMVPMatrix, 0, mRotationMatrix, 0, mMVPMatrix, 0); + + // Draw triangle + mTriangle.draw(mMVPMatrix); +} +</pre> + +<p>If your triangle does not rotate after making these changes, make sure you have commented out the +{@link android.opengl.GLSurfaceView#RENDERMODE_WHEN_DIRTY GLSurfaceView.RENDERMODE_WHEN_DIRTY} +setting, as described in the next section.</p> + + +<h2 id="cont-render">Enable Continuous Rendering</h2> + +<p>If you have diligently followed along with the example code in this class to this point, make +sure you comment out the line that sets the render mode only draw when dirty, otherwise OpenGL +rotates the shape only one increment and then waits for a call to {@link +android.opengl.GLSurfaceView#requestRender requestRender()} from the {@link +android.opengl.GLSurfaceView} container:</p> + +<pre> +public MyGLSurfaceView(Context context) { + ... + // Render the view only when there is a change in the drawing data + //setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY); // comment out for auto-rotation +} +</pre> + +<p>Unless you have objects changing without any user interaction, it’s usually a good idea have this +flag turned on. Be ready to uncomment this code, because the next lesson makes this call applicable +once again.</p> diff --git a/docs/html/training/graphics/opengl/projection.jd b/docs/html/training/graphics/opengl/projection.jd new file mode 100644 index 0000000..2a91093 --- /dev/null +++ b/docs/html/training/graphics/opengl/projection.jd @@ -0,0 +1,152 @@ +page.title=Applying Projection and Camera Views +parent.title=Displaying Graphics with OpenGL ES +parent.link=index.html + +trainingnavtop=true +previous.title=Drawing Shapes +previous.link=draw.html +next.title=Applying Projection and Camera Views +next.link=projection.html + +@jd:body + +<div id="tb-wrapper"> +<div id="tb"> + +<h2>This lesson teaches you to</h2> +<ol> + <li><a href="#projection">Define a Projection</a></li> + <li><a href="#camera-view">Define a Camera View</a></li> + <li><a href="#transform">Apply Projection and Camera Transformations</a></li> +</ol> + +<h2>You should also read</h2> +<ul> + <li><a href="{@docRoot}guide/topics/graphics/opengl.html">OpenGL</a></li> +</ul> + +<div class="download-box"> + <a href="{@docRoot}shareables/training/OpenGLES.zip" +class="button">Download the sample</a> + <p class="filename">OpenGLES.zip</p> +</div> + +</div> +</div> + +<p>In the OpenGL ES environment, projection and camera views allow you to display drawn objects in a +way that more closely resembles how you see physical objects with your eyes. This simulation of +physical viewing is done with mathematical transformations of drawn object coordinates:</p> + +<ul> + <li><em>Projection</em> - This transformation adjusts the coordinates of drawn objects based on +the width and height of the {@link android.opengl.GLSurfaceView} where they are displayed. Without +this calculation, objects drawn by OpenGL ES are skewed by the unequal proportions of the view +window. A projection transformation typically only has to be calculated when the proportions of the +OpenGL view are established or changed in the {@link +android.opengl.GLSurfaceView.Renderer#onSurfaceChanged +onSurfaceChanged()} method of your renderer. For more information about OpenGL ES projections and +coordinate mapping, see <a +href="{@docRoot}guide/topics/graphics/opengl.html#coordinate-mapping">Mapping Coordinates for Drawn +Objects</a>.</li> + <li><em>Camera View</em> - This transformation adjusts the coordinates of drawn objects based on a +virtual camera position. It’s important to note that OpenGL ES does not define an actual camera +object, but instead provides utility methods that simulate a camera by transforming the display of +drawn objects. A camera view transformation might be calculated only once when you establish your +{@link android.opengl.GLSurfaceView}, or might change dynamically based on user actions or your +application’s function.</li> +</ul> + +<p>This lesson describes how to create a projection and camera view and apply it to shapes drawn in +your {@link android.opengl.GLSurfaceView}.</p> + + +<h2 id="projection">Define a Projection</h2> + +<p>The data for a projection transformation is calculated in the {@link +android.opengl.GLSurfaceView.Renderer#onSurfaceChanged onSurfaceChanged()} +method of your {@link android.opengl.GLSurfaceView.Renderer} class. The following example code +takes the height and width of the {@link android.opengl.GLSurfaceView} and uses it to populate a +projection transformation {@link android.opengl.Matrix} using the {@link +android.opengl.Matrix#frustumM Matrix.frustumM()} method:</p> + +<pre> +@Override +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 coordinates + // in the onDrawFrame() method + Matrix.frustumM(mProjMatrix, 0, -ratio, ratio, -1, 1, 3, 7); +} +</pre> + +<p>This code populates a projection matrix, {@code mProjMatrix} which you can then combine with a +camera view transformation in the {@link android.opengl.GLSurfaceView.Renderer#onDrawFrame +onDrawFrame()} method, which is shown in the next section.</p> + +<p class="note"><strong>Note:</strong> Just applying a projection transformation to your +drawing objects typically results in a very empty display. In general, you must also apply a camera +view transformation in order for anything to show up on screen.</p> + + +<h2 id="camera-view">Define a Camera View</h2> + +<p>Complete the process of transforming your drawn objects by adding a camera view transformation as +part of the drawing process. In the following example code, the camera view transformation is +calculated using the {@link android.opengl.Matrix#setLookAtM Matrix.setLookAtM()} method and then +combined with the previously calculated projection matrix. The combined transformation matrices +are then passed to the drawn shape.</p> + +<pre> +@Override +public void onDrawFrame(GL10 unused) { + ... + + // Set the camera position (View matrix) + Matrix.setLookAtM(mVMatrix, 0, 0, 0, -3, 0f, 0f, 0f, 0f, 1.0f, 0.0f); + + // Calculate the projection and view transformation + Matrix.multiplyMM(mMVPMatrix, 0, mProjMatrix, 0, mVMatrix, 0); + + // Draw shape + mTriangle.draw(mMVPMatrix); +} +</pre> + + +<h2 id="#transform">Apply Projection and Camera Transformations</h2> + +<p>In order to use the combined projection and camera view transformation matrix shown in the +previews sections, modify the {@code draw()} method of your graphic objects to accept the combined +transformation matrix and apply it to the shape:</p> + +<pre> +public void draw(float[] mvpMatrix) { // pass in the calculated transformation matrix + ... + + // get handle to shape's transformation matrix + mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix"); + + // Apply the projection and view transformation + GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mvpMatrix, 0); + + // Draw the triangle + GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount); + ... +} +</pre> + +<p>Once you have correctly calulated and applied the projection and camera view transformations, +your graphic objects are drawn in correct proportions and should look like this:</p> + + +<img src="{@docRoot}images/opengl/ogl-triangle-projected.png"> +<p class="img-caption"> +<strong>Figure 1.</strong> Triangle drawn with a projection and camera view applied.</p> + + +<p>Now that you have an application that displays your shapes in correct proportions, it's time to +add motion to your shapes.</p> diff --git a/docs/html/training/graphics/opengl/shapes.jd b/docs/html/training/graphics/opengl/shapes.jd new file mode 100644 index 0000000..98381cc --- /dev/null +++ b/docs/html/training/graphics/opengl/shapes.jd @@ -0,0 +1,153 @@ +page.title=Defining Shapes +parent.title=Displaying Graphics with OpenGL ES +parent.link=index.html + +trainingnavtop=true +previous.title=Building an OpenGL ES Environment +previous.link=environment.html +next.title=Drawing Shapes +next.link=draw.html + +@jd:body + +<div id="tb-wrapper"> +<div id="tb"> + +<h2>This lesson teaches you to</h2> +<ol> + <li><a href="#triangle">Define a Triangle</a></li> + <li><a href="#square">Define a Square</a></li> +</ol> + +<h2>You should also read</h2> +<ul> + <li><a href="{@docRoot}guide/topics/graphics/opengl.html">OpenGL</a></li> +</ul> + +<div class="download-box"> + <a href="{@docRoot}shareables/training/OpenGLES.zip" +class="button">Download the sample</a> + <p class="filename">OpenGLES.zip</p> +</div> + +</div> +</div> + +<p>Being able to define shapes to be drawn in the context of an OpenGL ES view is the first step in +creating your high-end graphics masterpiece. Drawing with OpenGL ES can be a little tricky without +knowing a few basic things about how OpenGL ES expects you to define graphic objects.</p> + +<p>This lesson explains the OpenGL ES coordinate system relative to an Android device screen, the +basics of defining a shape, shape faces, as well as defining a triangle and a square.</p> + + +<h2 id="triangle">Define a Triangle</h2> + +<p>OpenGL ES allows you to define drawn 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 of floating point numbers for the coordinates. For maximum +efficiency, you write these coordinates into a {@link java.nio.ByteBuffer}, that is passed into the +OpenGL ES graphics pipeline for processing.</p> + +<pre> +class Triangle { + + private FloatBuffer vertexBuffer; + + // number of coordinates per vertex in this array + static final int COORDS_PER_VERTEX = 3; + static float triangleCoords[] = { // in counterclockwise order: + 0.0f, 0.622008459f, 0.0f, // top + -0.5f, -0.311004243f, 0.0f, // bottom left + 0.5f, -0.311004243f, 0.0f // bottom right + }; + + // Set color with red, green, blue and alpha (opacity) values + float color[] = { 0.63671875f, 0.76953125f, 0.22265625f, 1.0f }; + + public Triangle() { + // initialize vertex byte buffer for shape coordinates + ByteBuffer bb = ByteBuffer.allocateDirect( + // (number of coordinate values * 4 bytes per float) + triangleCoords.length * 4); + // use the device hardware's native byte order + bb.order(ByteOrder.nativeOrder()); + + // create a floating point buffer from the ByteBuffer + vertexBuffer = bb.asFloatBuffer(); + // add the coordinates to the FloatBuffer + vertexBuffer.put(triangleCoords); + // set the buffer to read the first coordinate + vertexBuffer.position(0); + } +} +</pre> + +<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. For an illustration of this coordinate system, see the +<a href="{@docRoot}guide/topics/graphics/opengl.html#coordinate-mapping">OpenGL ES</a> developer +guide.</p> + +<p>Note that the coordinates of this shape are defined in a counterclockwise order. The drawing +order is important because it defines which side is the front face of the shape, which you typically +want to have drawn, and the back face, which you can choose to not draw using the OpenGL ES cull +face feature. For more information about faces and culling, see the <a +href="{@docRoot}guide/topics/graphics/opengl.html#faces-winding">OpenGL ES</a> developer guide.</p> + + +<h2 id="square">Define a Square</h2> + +<p>Defining triangles is pretty easy in OpenGL, but what if you want to get a just a little more +complex? Say, a square? There are a number of ways to do this, but a typical path to drawing such a +shape in OpenGL ES is to use two triangles drawn together:</p> + +<img src="{@docRoot}images/opengl/ccw-square.png"> +<p class="img-caption"> + <strong>Figure 1.</strong> Drawing a square using two triangles.</p> + +<p>Again, you should define the vertices in a counterclockwise order for both triangles that +represent this shape, and put the values in a {@link java.nio.ByteBuffer}. In order to avoid +defining the two coordinates shared by each triangle twice, use a drawing list to tell the +OpenGL ES graphics pipeline how to draw these vertices. Here’s the code for this shape:</p> + +<pre> +class Square { + + private FloatBuffer vertexBuffer; + private ShortBuffer drawListBuffer; + + // number of coordinates per vertex in this array + static final int COORDS_PER_VERTEX = 3; + static float squareCoords[] = { -0.5f, 0.5f, 0.0f, // top left + -0.5f, -0.5f, 0.0f, // bottom left + 0.5f, -0.5f, 0.0f, // bottom right + 0.5f, 0.5f, 0.0f }; // top right + + private short drawOrder[] = { 0, 1, 2, 0, 2, 3 }; // order to draw vertices + + public Square() { + // initialize vertex byte buffer for shape coordinates + ByteBuffer bb = ByteBuffer.allocateDirect( + // (# of coordinate values * 4 bytes per float) + squareCoords.length * 4); + bb.order(ByteOrder.nativeOrder()); + vertexBuffer = bb.asFloatBuffer(); + vertexBuffer.put(squareCoords); + vertexBuffer.position(0); + + // initialize byte buffer for the draw list + ByteBuffer dlb = ByteBuffer.allocateDirect( + // (# of coordinate values * 2 bytes per short) + drawOrder.length * 2); + dlb.order(ByteOrder.nativeOrder()); + drawListBuffer = dlb.asShortBuffer(); + drawListBuffer.put(drawOrder); + drawListBuffer.position(0); + } +} +</pre> + +<p>This example gives you a peek at what it takes to create more complex shapes with OpenGL. In +general, you use collections of triangles to draw objects. In the next lesson, you learn how to draw +these shapes on screen.</p> diff --git a/docs/html/training/graphics/opengl/touch.jd b/docs/html/training/graphics/opengl/touch.jd new file mode 100644 index 0000000..c058a59 --- /dev/null +++ b/docs/html/training/graphics/opengl/touch.jd @@ -0,0 +1,145 @@ +page.title= Responding to Touch Events +parent.title=Displaying Graphics with OpenGL ES +parent.link=index.html + +trainingnavtop=true +previous.title=Adding Motion +previous.link=motion.html + +@jd:body + +<div id="tb-wrapper"> +<div id="tb"> + +<h2>This lesson teaches you to</h2> +<ol> + <li><a href="#listener">Setup a Touch Listener</a></li> + <li><a href="#angle">Expose the Rotation Angle</a></li> + <li><a href="#rotate">Apply Rotation</a></li> +</ol> + +<h2>You should also read</h2> +<ul> + <li><a href="{@docRoot}guide/topics/graphics/opengl.html">OpenGL</a></li> +</ul> + +<div class="download-box"> + <a href="http://developer.android.com/shareables/training/OpenGLES.zip" +class="button">Download the sample</a> + <p class="filename">OpenGLES.zip</p> +</div> + +</div> +</div> + +<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 ES graphics? +The key to making your OpenGL ES application touch interactive is expanding your implementation of +{@link android.opengl.GLSurfaceView} to override the {@link +android.opengl.GLSurfaceView#onTouchEvent onTouchEvent()} to listen for touch events.</p> + +<p>This lesson shows you how to listen for touch events to let users rotate an OpenGL ES object.</p> + + +<h2 id="listener">Setup a Touch Listener</h2> + +<p>In order to make your OpenGL ES application respond to touch events, you must implement the +{@link android.opengl.GLSurfaceView#onTouchEvent onTouchEvent()} method in your +{@link android.opengl.GLSurfaceView} class. The example implementation below shows how to listen for +{@link android.view.MotionEvent#ACTION_MOVE MotionEvent.ACTION_MOVE} events and translate them to +an angle of rotation for a shape.</p> + +<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; // = 180.0f / 320 + requestRender(); + } + + mPreviousX = x; + mPreviousY = y; + return true; +} +</pre> + +<p>Notice that after calculating the rotation angle, this method calls {@link +android.opengl.GLSurfaceView#requestRender requestRender()} to tell the +renderer that it is time to render the frame. This approach is the most efficient in this example +because the frame does not need to be redrawn unless there is a change in the rotation. However, it +does not have any impact on efficiency unless you also request that the renderer only redraw when +the data changes using the {@link android.opengl.GLSurfaceView#setRenderMode setRenderMode()} +method, so make sure this line is uncommented in the renderer:</p> + +<pre> +public MyGLSurfaceView(Context context) { + ... + // Render the view only when there is a change in the drawing data + <strong>setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);</strong> +} +</pre> + +<h2 id="angle">Expose the Rotation Angle</h2> + +<p>The example code above requires that you expose the rotation angle through your renderer by +adding a public member. Since the renderer code is running on a separate thread from the main user +interface thread of your application, you must declare this public variable as {@code volatile}. +Here is the code to do that:</p> + +<pre> +public class MyGLRenderer implements GLSurfaceView.Renderer { + ... + public volatile float mAngle; +</pre> + + +<h2 id="rotate">Apply Rotation</h2> + +<p>To apply the rotation generated by touch input, comment out the code that generates an angle and +add {@code mAngle}, which contains the touch input generated angle:</p> + +<pre> +public void onDrawFrame(GL10 gl) { + ... + // Create a rotation for the triangle + // long time = SystemClock.uptimeMillis() % 4000L; + // float angle = 0.090f * ((int) time); + <strong>Matrix.setRotateM(mRotationMatrix, 0, mAngle, 0, 0, -1.0f);</strong> + + // Combine the rotation matrix with the projection and camera view + Matrix.multiplyMM(mMVPMatrix, 0, mRotationMatrix, 0, mMVPMatrix, 0); + + // Draw triangle + mTriangle.draw(mMVPMatrix); +} +</pre> + +<p>When you have completed the steps described above, run the program and drag your finger over the +screen to rotate the triangle:</p> + +<img src="{@docRoot}images/opengl/ogl-triangle-touch.png"> +<p class="img-caption"> +<strong>Figure 1.</strong> Triangle being rotated with touch input (circle shows touch +location).</p> |
