1page.title=Drawing Shapes 2parent.title=Displaying Graphics with OpenGL ES 3parent.link=index.html 4 5trainingnavtop=true 6previous.title=Defining Shapes 7previous.link=environment.html 8next.title=Applying Projection and Camera Views 9next.link=projection.html 10 11@jd:body 12 13<div id="tb-wrapper"> 14<div id="tb"> 15 16<h2>This lesson teaches you to</h2> 17<ol> 18 <li><a href="#initialize">Initialize Shapes</a></li> 19 <li><a href="#draw">Draw a Shape</a></li> 20</ol> 21 22<h2>You should also read</h2> 23<ul> 24 <li><a href="{@docRoot}guide/topics/graphics/opengl.html">OpenGL</a></li> 25</ul> 26 27<div class="download-box"> 28 <a href="{@docRoot}shareables/training/OpenGLES.zip" 29class="button">Download the sample</a> 30 <p class="filename">OpenGLES.zip</p> 31</div> 32 33</div> 34</div> 35 36<p>After you define shapes to be drawn with OpenGL, you probably want to draw them. Drawing shapes 37with the OpenGL ES 2.0 takes a bit more code than you might imagine, because the API provides a 38great deal of control over the graphics rendering pipeline.</p> 39 40<p>This lesson explains how to draw the shapes you defined in the previous lesson using the OpenGL 41ES 2.0 API.</p> 42 43 44<h2 id="initialize">Initialize Shapes</h2> 45 46<p>Before you do any drawing, you must initialize and load the shapes you plan to draw. Unless the 47structure (the original coordinates) of the shapes you use in your program change during the course 48of execution, you should initialize them in the {@link 49android.opengl.GLSurfaceView.Renderer#onSurfaceCreated onSurfaceCreated()} method of your renderer 50for memory and processing efficiency.</p> 51 52<pre> 53public class MyGLRenderer implements GLSurfaceView.Renderer { 54 55 ... 56 private Triangle mTriangle; 57 private Square mSquare; 58 59 public void onSurfaceCreated(GL10 unused, EGLConfig config) { 60 ... 61 62 // initialize a triangle 63 mTriangle = new Triangle(); 64 // initialize a square 65 mSquare = new Square(); 66 } 67 ... 68} 69</pre> 70 71 72<h2 id="draw">Draw a Shape</h2> 73 74<p>Drawing a defined shape using OpenGL ES 2.0 requires a significant amount of code, because you 75must provide a lot of details to the graphics rendering pipeline. Specifically, you must define the 76following:</p> 77 78<ul> 79 <li><em>Vertex Shader</em> - OpenGL ES graphics code for rendering the vertices of a shape.</li> 80 <li><em>Fragment Shader</em> - OpenGL ES code for rendering the face of a shape with colors or 81textures.</li> 82 <li><em>Program</em> - An OpenGL ES object that contains the shaders you want to use for drawing 83one or more shapes.</li> 84</ul> 85 86<p>You need at least one vertex shader to draw a shape and one fragment shader to color that shape. 87These shaders must be complied and then added to an OpenGL ES program, which is then used to draw 88the shape. Here is an example of how to define basic shaders you can use to draw a shape in the 89<code>Triangle</code> class:</p> 90 91<pre> 92public class Triangle { 93 94 private final String vertexShaderCode = 95 "attribute vec4 vPosition;" + 96 "void main() {" + 97 " gl_Position = vPosition;" + 98 "}"; 99 100 private final String fragmentShaderCode = 101 "precision mediump float;" + 102 "uniform vec4 vColor;" + 103 "void main() {" + 104 " gl_FragColor = vColor;" + 105 "}"; 106 107 ... 108} 109</pre> 110 111<p>Shaders contain OpenGL Shading Language (GLSL) code that must be compiled prior to using it in 112the OpenGL ES environment. To compile this code, create a utility method in your renderer class:</p> 113 114<pre> 115public static int loadShader(int type, String shaderCode){ 116 117 // create a vertex shader type (GLES20.GL_VERTEX_SHADER) 118 // or a fragment shader type (GLES20.GL_FRAGMENT_SHADER) 119 int shader = GLES20.glCreateShader(type); 120 121 // add the source code to the shader and compile it 122 GLES20.glShaderSource(shader, shaderCode); 123 GLES20.glCompileShader(shader); 124 125 return shader; 126} 127</pre> 128 129<p>In order to draw your shape, you must compile the shader code, add them to a OpenGL ES program 130object and then link the program. Do this in your drawn object’s constructor, so it is only done 131once.</p> 132 133<p class="note"><strong>Note:</strong> Compiling OpenGL ES shaders and linking programs is expensive 134in terms of CPU cycles and processing time, so you should avoid doing this more than once. If you do 135not know the content of your shaders at runtime, you should build your code such that they only 136get created once and then cached for later use.</p> 137 138<pre> 139public class Triangle() { 140 ... 141 142 private final int mProgram; 143 144 public Triangle() { 145 ... 146 147 int vertexShader = MyGLRenderer.loadShader(GLES20.GL_VERTEX_SHADER, 148 vertexShaderCode); 149 int fragmentShader = MyGLRenderer.loadShader(GLES20.GL_FRAGMENT_SHADER, 150 fragmentShaderCode); 151 152 // create empty OpenGL ES Program 153 mProgram = GLES20.glCreateProgram(); 154 155 // add the vertex shader to program 156 GLES20.glAttachShader(mProgram, vertexShader); 157 158 // add the fragment shader to program 159 GLES20.glAttachShader(mProgram, fragmentShader); 160 161 // creates OpenGL ES program executables 162 GLES20.glLinkProgram(mProgram); 163 } 164} 165</pre> 166 167<p>At this point, you are ready to add the actual calls that draw your shape. Drawing shapes with 168OpenGL ES requires that you specify several parameters to tell the rendering pipeline what you want 169to draw and how to draw it. Since drawing options can vary by shape, it's a good idea to have your 170shape classes contain their own drawing logic.</p> 171 172<p>Create a {@code draw()} method for drawing the shape. This code sets the position and 173color values to the shape’s vertex shader and fragment shader, and then executes the drawing 174function.</p> 175 176<pre> 177private int mPositionHandle; 178private int mColorHandle; 179 180private final int vertexCount = triangleCoords.length / COORDS_PER_VERTEX; 181private final int vertexStride = COORDS_PER_VERTEX * 4; // 4 bytes per vertex 182 183public void draw() { 184 // Add program to OpenGL ES environment 185 GLES20.glUseProgram(mProgram); 186 187 // get handle to vertex shader's vPosition member 188 mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition"); 189 190 // Enable a handle to the triangle vertices 191 GLES20.glEnableVertexAttribArray(mPositionHandle); 192 193 // Prepare the triangle coordinate data 194 GLES20.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX, 195 GLES20.GL_FLOAT, false, 196 vertexStride, vertexBuffer); 197 198 // get handle to fragment shader's vColor member 199 mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor"); 200 201 // Set color for drawing the triangle 202 GLES20.glUniform4fv(mColorHandle, 1, color, 0); 203 204 // Draw the triangle 205 GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount); 206 207 // Disable vertex array 208 GLES20.glDisableVertexAttribArray(mPositionHandle); 209} 210</pre> 211 212<p>Once you have all this code in place, drawing this object just requires a call to the 213{@code draw()} method from within your renderer’s {@link 214android.opengl.GLSurfaceView.Renderer#onDrawFrame onDrawFrame()} method: 215 216<pre> 217public void onDrawFrame(GL10 unused) { 218 ... 219 220 mTriangle.draw(); 221} 222</pre> 223 224<p>When you run the application, it should look something like this:</p> 225 226<img src="{@docRoot}images/opengl/ogl-triangle.png"> 227<p class="img-caption"> 228<strong>Figure 1.</strong> Triangle drawn without a projection or camera view.</p> 229 230<p>There are a few problems with this code example. First of all, it is not going to impress your 231friends. Secondly, the triangle is a bit squashed and changes shape when you change the screen 232orientation of the device. The reason the shape is skewed is due to the fact that the object’s 233vertices have not been corrected for the proportions of the screen area where the {@link 234android.opengl.GLSurfaceView} is displayed. You can fix that problem using a projection and camera 235view in the next lesson.</p> 236 237<p>Lastly, the triangle is stationary, which is a bit boring. In the <a href="motion.html">Adding 238Motion</a> lesson, you make this shape rotate and make more interesting use of the OpenGL ES 239graphics pipeline.</p>