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 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 67must provide a lot of details to the graphics rendering pipeline. Specifically, you must define the 68following:</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 73textures.</li> 74 <li><em>Program</em> - An OpenGL ES object that contains the shaders you want to use for drawing 75one 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. 79These shaders must be complied and then added to an OpenGL ES program, which is then used to draw 80the shape. Here is an example of how to define basic shaders you can use to draw a shape:</p> 81 82<pre> 83private final String vertexShaderCode = 84 "attribute vec4 vPosition;" + 85 "void main() {" + 86 " gl_Position = vPosition;" + 87 "}"; 88 89private 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 98the OpenGL ES environment. To compile this code, create a utility method in your renderer class:</p> 99 100<pre> 101public 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 116object and then link the program. Do this in your drawn object’s constructor, so it is only done 117once.</p> 118 119<p class="note"><strong>Note:</strong> Compiling OpenGL ES shaders and linking programs is expensive 120in terms of CPU cycles and processing time, so you should avoid doing this more than once. If you do 121not know the content of your shaders at runtime, you should build your code such that they only 122get created once and then cached for later use.</p> 123 124<pre> 125public 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 139OpenGL ES requires that you specify several parameters to tell the rendering pipeline what you want 140to draw and how to draw it. Since drawing options can vary by shape, it's a good idea to have your 141shape 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 144color values to the shape’s vertex shader and fragment shader, and then executes the drawing 145function.</p> 146 147<pre> 148public 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 renderer’s {@link 179android.opengl.GLSurfaceView.Renderer#onDrawFrame onDrawFrame()} method. When you run the 180application, 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 187friends. Secondly, the triangle is a bit squashed and changes shape when you change the screen 188orientation of the device. The reason the shape is skewed is due to the fact that the object’s 189vertices have not been corrected for the proportions of the screen area where the {@link 190android.opengl.GLSurfaceView} is displayed. You can fix that problem using a projection and camera 191view 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 194Motion</a> lesson, you make this shape rotate and make more interesting use of the OpenGL ES 195graphics pipeline.</p>