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 void onSurfaceCreated(GL10 unused, EGLConfig config) { 54 ... 55 56 // initialize a triangle 57 mTriangle = new Triangle(); 58 // initialize a square 59 mSquare = new Square(); 60 } 61 </pre> 62 63 64 <h2 id="draw">Draw a Shape</h2> 65 66 <p>Drawing a defined shape using OpenGL ES 2.0 requires a significant amount of code, because you 67 must provide a lot of details to the graphics rendering pipeline. Specifically, you must define the 68 following:</p> 69 70 <ul> 71 <li><em>Vertex Shader</em> - OpenGL ES graphics code for rendering the vertices of a shape.</li> 72 <li><em>Fragment Shader</em> - OpenGL ES code for rendering the face of a shape with colors or 73 textures.</li> 74 <li><em>Program</em> - An OpenGL ES object that contains the shaders you want to use for drawing 75 one or more shapes.</li> 76 </ul> 77 78 <p>You need at least one vertex shader to draw a shape and one fragment shader to color that shape. 79 These shaders must be complied and then added to an OpenGL ES program, which is then used to draw 80 the shape. Here is an example of how to define basic shaders you can use to draw a shape:</p> 81 82 <pre> 83 private final String vertexShaderCode = 84 "attribute vec4 vPosition;" + 85 "void main() {" + 86 " gl_Position = vPosition;" + 87 "}"; 88 89 private final String fragmentShaderCode = 90 "precision mediump float;" + 91 "uniform vec4 vColor;" + 92 "void main() {" + 93 " gl_FragColor = vColor;" + 94 "}"; 95 </pre> 96 97 <p>Shaders contain OpenGL Shading Language (GLSL) code that must be compiled prior to using it in 98 the OpenGL ES environment. To compile this code, create a utility method in your renderer class:</p> 99 100 <pre> 101 public static int loadShader(int type, String shaderCode){ 102 103 // create a vertex shader type (GLES20.GL_VERTEX_SHADER) 104 // or a fragment shader type (GLES20.GL_FRAGMENT_SHADER) 105 int shader = GLES20.glCreateShader(type); 106 107 // add the source code to the shader and compile it 108 GLES20.glShaderSource(shader, shaderCode); 109 GLES20.glCompileShader(shader); 110 111 return shader; 112 } 113 </pre> 114 115 <p>In order to draw your shape, you must compile the shader code, add them to a OpenGL ES program 116 object and then link the program. Do this in your drawn objects constructor, so it is only done 117 once.</p> 118 119 <p class="note"><strong>Note:</strong> Compiling OpenGL ES shaders and linking programs is expensive 120 in terms of CPU cycles and processing time, so you should avoid doing this more than once. If you do 121 not know the content of your shaders at runtime, you should build your code such that they only 122 get created once and then cached for later use.</p> 123 124 <pre> 125 public Triangle() { 126 ... 127 128 int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode); 129 int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode); 130 131 mProgram = GLES20.glCreateProgram(); // create empty OpenGL ES Program 132 GLES20.glAttachShader(mProgram, vertexShader); // add the vertex shader to program 133 GLES20.glAttachShader(mProgram, fragmentShader); // add the fragment shader to program 134 GLES20.glLinkProgram(mProgram); // creates OpenGL ES program executables 135 } 136 </pre> 137 138 <p>At this point, you are ready to add the actual calls that draw your shape. Drawing shapes with 139 OpenGL ES requires that you specify several parameters to tell the rendering pipeline what you want 140 to draw and how to draw it. Since drawing options can vary by shape, it's a good idea to have your 141 shape classes contain their own drawing logic.</p> 142 143 <p>Create a {@code draw()} method for drawing the shape. This code sets the position and 144 color values to the shapes vertex shader and fragment shader, and then executes the drawing 145 function.</p> 146 147 <pre> 148 public void draw() { 149 // Add program to OpenGL ES environment 150 GLES20.glUseProgram(mProgram); 151 152 // get handle to vertex shader's vPosition member 153 mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition"); 154 155 // Enable a handle to the triangle vertices 156 GLES20.glEnableVertexAttribArray(mPositionHandle); 157 158 // Prepare the triangle coordinate data 159 GLES20.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX, 160 GLES20.GL_FLOAT, false, 161 vertexStride, vertexBuffer); 162 163 // get handle to fragment shader's vColor member 164 mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor"); 165 166 // Set color for drawing the triangle 167 GLES20.glUniform4fv(mColorHandle, 1, color, 0); 168 169 // Draw the triangle 170 GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount); 171 172 // Disable vertex array 173 GLES20.glDisableVertexAttribArray(mPositionHandle); 174 } 175 </pre> 176 177 <p>Once you have all this code in place, drawing this object just requires a call to the 178 {@code draw()} method from within your renderers {@link 179 android.opengl.GLSurfaceView.Renderer#onDrawFrame onDrawFrame()} method. When you run the 180 application, it should look something like this:</p> 181 182 <img src="{@docRoot}images/opengl/ogl-triangle.png"> 183 <p class="img-caption"> 184 <strong>Figure 1.</strong> Triangle drawn without a projection or camera view.</p> 185 186 <p>There are a few problems with this code example. First of all, it is not going to impress your 187 friends. Secondly, the triangle is a bit squashed and changes shape when you change the screen 188 orientation of the device. The reason the shape is skewed is due to the fact that the objects 189 vertices have not been corrected for the proportions of the screen area where the {@link 190 android.opengl.GLSurfaceView} is displayed. You can fix that problem using a projection and camera 191 view in the next lesson.</p> 192 193 <p>Lastly, the triangle is stationary, which is a bit boring. In the <a href="motion.html">Adding 194 Motion</a> lesson, you make this shape rotate and make more interesting use of the OpenGL ES 195 graphics pipeline.</p>