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>