1 page.title=Drawing Shapes 2 parent.title=Displaying Graphics with OpenGL ES 3 parent.link=index.html 4 5 trainingnavtop=true 6 previous.title=Defining Shapes 7 previous.link=environment.html 8 next.title=Applying Projection and Camera Views 9 next.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" 29 class="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 37 with the OpenGL ES 2.0 takes a bit more code than you might imagine, because the API provides a 38 great 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 41 ES 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 47 structure (the original coordinates) of the shapes you use in your program change during the course 48 of execution, you should initialize them in the {@link 49 android.opengl.GLSurfaceView.Renderer#onSurfaceCreated onSurfaceCreated()} method of your renderer 50 for memory and processing efficiency.</p> 51 52 <pre> 53 public 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 75 must provide a lot of details to the graphics rendering pipeline. Specifically, you must define the 76 following:</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 81 textures.</li> 82 <li><em>Program</em> - An OpenGL ES object that contains the shaders you want to use for drawing 83 one 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. 87 These shaders must be complied and then added to an OpenGL ES program, which is then used to draw 88 the 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> 92 public 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 112 the OpenGL ES environment. To compile this code, create a utility method in your renderer class:</p> 113 114 <pre> 115 public 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 130 object and then link the program. Do this in your drawn objects constructor, so it is only done 131 once.</p> 132 133 <p class="note"><strong>Note:</strong> Compiling OpenGL ES shaders and linking programs is expensive 134 in terms of CPU cycles and processing time, so you should avoid doing this more than once. If you do 135 not know the content of your shaders at runtime, you should build your code such that they only 136 get created once and then cached for later use.</p> 137 138 <pre> 139 public 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 168 OpenGL ES requires that you specify several parameters to tell the rendering pipeline what you want 169 to draw and how to draw it. Since drawing options can vary by shape, it's a good idea to have your 170 shape 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 173 color values to the shapes vertex shader and fragment shader, and then executes the drawing 174 function.</p> 175 176 <pre> 177 private int mPositionHandle; 178 private int mColorHandle; 179 180 private final int vertexCount = triangleCoords.length / COORDS_PER_VERTEX; 181 private final int vertexStride = COORDS_PER_VERTEX * 4; // 4 bytes per vertex 182 183 public 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 renderers {@link 214 android.opengl.GLSurfaceView.Renderer#onDrawFrame onDrawFrame()} method: 215 216 <pre> 217 public 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 231 friends. Secondly, the triangle is a bit squashed and changes shape when you change the screen 232 orientation of the device. The reason the shape is skewed is due to the fact that the objects 233 vertices have not been corrected for the proportions of the screen area where the {@link 234 android.opengl.GLSurfaceView} is displayed. You can fix that problem using a projection and camera 235 view 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 238 Motion</a> lesson, you make this shape rotate and make more interesting use of the OpenGL ES 239 graphics pipeline.</p>