| 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 class 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> |