├── .gitignore
├── AndroidManifest.xml
├── README.md
├── ant.properties
├── build.xml
├── project.properties
├── res
├── drawable-hdpi
│ ├── ic_launcher.png
│ └── skull.png
├── drawable-ldpi
│ ├── ic_launcher.png
│ └── skull.png
├── drawable-mdpi
│ ├── ic_launcher.png
│ └── skull.png
└── values
│ └── strings.xml
└── src
└── ibraim
└── opengles2
├── EpilepsyActivity.java
├── MainActivity.java
├── TextureActivity.java
├── Triangle2dActivity.java
└── TriangleColorActivity.java
/.gitignore:
--------------------------------------------------------------------------------
1 | local.properties
2 | gen/
3 | out/
4 | *.iml
5 | .idea/
6 |
--------------------------------------------------------------------------------
/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
18 |
19 |
20 |
21 |
23 |
24 |
25 |
26 |
28 |
29 |
30 |
31 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## About
2 |
3 | `opengles2-2d-demos` is a simple android 4.0 application that showcases some simple 2d examples in OpenGL ES 2.0. This project serve as a sample for how to effectively use OpenGL ES 2.0 for a 2d application (ex: a game) and to track my personal progress on the subject. All samples are made in the most legible possible way I could find and are full of comments explaining the important stuff.
4 |
5 | Be warned that **every** file is standalone - this generate a lot of code duplication, *BUT* it's slightly easier to follow (you only need to look to the file that shows what you're interested, no need to "hunt" initialization routines, etc.). In short, don't use this project structure to do anything serious or you will probably be murdered by your manager and/or teammates.
6 |
7 | ## The Samples
8 |
9 | Every sample can be accessed from the initial activity. The available samples are:
10 |
11 | 1. **Epilepsy**: The most basic sample, shows how to initialize OpenGL ES 2.0. Every frame, the background is changed to a random color. Useful for testing if you or your friends have epilepsy.
12 | 2. **Triangle2d**: Shows how to setup the coordinate system and draw a triangle in a fixed position. A must-see for anyone trying to work with 2D in Open GL ES 2.0.
13 | 3. **TriangleColor**: Same as `Triangle2d`, but this time each vertex has his own color and both the positions and colors are stored in the same array. This shows the optimal way to use OpenGL ES 2.0.
14 | 4. **Texture**: Shows how to load a png file as a texture in OpenGL and how to display it on screen.
15 |
16 | ## License
17 | BSD. Basically, you're free to do whathever you like with this code.
18 |
--------------------------------------------------------------------------------
/ant.properties:
--------------------------------------------------------------------------------
1 | # This file is used to override default values used by the Ant build system.
2 | #
3 | # This file must be checked into Version Control Systems, as it is
4 | # integral to the build system of your project.
5 |
6 | # This file is only used by the Ant script.
7 |
8 | # You can use this to override default values such as
9 | # 'source.dir' for the location of your java source folder and
10 | # 'out.dir' for the location of your output folder.
11 |
12 | # You can also use it define how the release builds are signed by declaring
13 | # the following properties:
14 | # 'key.store' for the location of your keystore and
15 | # 'key.alias' for the name of the key to use.
16 | # The password will be asked during the build when you use the 'release' target.
17 |
18 |
--------------------------------------------------------------------------------
/build.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
9 |
28 |
29 |
30 |
34 |
35 |
36 |
37 |
38 |
39 |
48 |
49 |
50 |
51 |
55 |
56 |
68 |
69 |
70 |
88 |
89 |
90 |
91 |
--------------------------------------------------------------------------------
/project.properties:
--------------------------------------------------------------------------------
1 | # This file is automatically generated by Android Tools.
2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED!
3 | #
4 | # This file must be checked in Version Control Systems.
5 | #
6 | # To customize properties used by the Ant build system edit
7 | # "ant.properties", and override values to adapt the script to your
8 | # project structure.
9 | #
10 | # To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
11 | #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
12 |
13 | # Project target.
14 | target=android-14
15 |
--------------------------------------------------------------------------------
/res/drawable-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ibraimgm/opengles2-2d-demos/999d203eca16b720879ef9b8aa4bedc11f9cf1b6/res/drawable-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/skull.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ibraimgm/opengles2-2d-demos/999d203eca16b720879ef9b8aa4bedc11f9cf1b6/res/drawable-hdpi/skull.png
--------------------------------------------------------------------------------
/res/drawable-ldpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ibraimgm/opengles2-2d-demos/999d203eca16b720879ef9b8aa4bedc11f9cf1b6/res/drawable-ldpi/ic_launcher.png
--------------------------------------------------------------------------------
/res/drawable-ldpi/skull.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ibraimgm/opengles2-2d-demos/999d203eca16b720879ef9b8aa4bedc11f9cf1b6/res/drawable-ldpi/skull.png
--------------------------------------------------------------------------------
/res/drawable-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ibraimgm/opengles2-2d-demos/999d203eca16b720879ef9b8aa4bedc11f9cf1b6/res/drawable-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/res/drawable-mdpi/skull.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ibraimgm/opengles2-2d-demos/999d203eca16b720879ef9b8aa4bedc11f9cf1b6/res/drawable-mdpi/skull.png
--------------------------------------------------------------------------------
/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | MainActivity
4 |
5 |
--------------------------------------------------------------------------------
/src/ibraim/opengles2/EpilepsyActivity.java:
--------------------------------------------------------------------------------
1 | package ibraim.opengles2;
2 |
3 | import android.app.Activity;
4 | import android.opengl.GLES20;
5 | import android.opengl.GLSurfaceView;
6 | import android.os.Bundle;
7 |
8 | import javax.microedition.khronos.egl.EGLConfig;
9 | import javax.microedition.khronos.opengles.GL10;
10 | import java.util.Random;
11 |
12 | public class EpilepsyActivity extends Activity
13 | {
14 | // this is the surface where the drawing will actually occur.
15 | private GLSurfaceView surface;
16 |
17 | @Override
18 | protected void onCreate(Bundle savedInstanceState)
19 | {
20 | super.onCreate(savedInstanceState);
21 |
22 | // We setup the view and enable the use of OpenGL ES 2.0
23 | // We also set the renderer class, responsible for the drawing logic
24 | surface = new GLSurfaceView(this);
25 | surface.setEGLContextClientVersion(2);
26 | surface.setRenderer(new EpilepsyRenderer());
27 |
28 | // The widget (view) of our gui is the OpenGL surface.
29 | setContentView(surface);
30 | }
31 |
32 | @Override
33 | protected void onResume()
34 | {
35 | // Don't forget to RESUME the view when the app resumes
36 | super.onResume();
37 | surface.onResume();
38 | }
39 |
40 | @Override
41 | protected void onPause()
42 | {
43 | // Don't forget to PAUSE the view when the app stops!
44 | // If you don't do this, the process will still be running,
45 | // totally screwing the performance and the battery life of the
46 | // device.
47 | super.onPause();
48 | surface.onPause();
49 | }
50 |
51 | // This is where the drawing logic lives
52 | private class EpilepsyRenderer implements GLSurfaceView.Renderer
53 | {
54 | // Random number generator used to set the background color
55 | private Random r = new Random();
56 |
57 | @Override
58 | public void onSurfaceCreated(GL10 gl, EGLConfig config)
59 | {
60 | // This method will be called EVERY TIME our application resumes.
61 | // When the app pauses, the surfaces and all of the OpenGL resources
62 | // are freed (textures, etc). Since we don't use anything special in
63 | // this sample, we don't need to do anything.
64 | }
65 |
66 | @Override
67 | public void onSurfaceChanged(GL10 gl, int width, int height)
68 | {
69 | // Called when the surface change size or right after it's created.
70 | // Generally, you will set Viewport and camera options here.
71 | GLES20.glViewport(0, 0, width, height);
72 | }
73 |
74 | @Override
75 | public void onDrawFrame(GL10 gl)
76 | {
77 | // This is the drawing of every app frame. Nothing really special here,
78 | // We just set the clearColor to random values and clear the screen using
79 | // that color.
80 | GLES20.glClearColor(r.nextFloat(), r.nextFloat(), r.nextFloat(), 1.0f);
81 | GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
82 | }
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/src/ibraim/opengles2/MainActivity.java:
--------------------------------------------------------------------------------
1 | package ibraim.opengles2;
2 |
3 | import android.app.Activity;
4 | import android.app.ListActivity;
5 | import android.content.Intent;
6 | import android.os.Bundle;
7 | import android.view.View;
8 | import android.widget.ArrayAdapter;
9 | import android.widget.ListView;
10 |
11 | public class MainActivity extends ListActivity
12 | {
13 | private final String tests[] =
14 | {"Epilepsy", "Triangle2d", "TriangleColor", "Texture"};
15 |
16 | /** Called when the activity is first created. */
17 | @Override
18 | public void onCreate(Bundle savedInstanceState)
19 | {
20 | super.onCreate(savedInstanceState);
21 | setListAdapter(new ArrayAdapter(this, android.R.layout.simple_list_item_1, tests));
22 | }
23 |
24 | @Override
25 | protected void onListItemClick(ListView l, View v, int position, long id)
26 | {
27 | super.onListItemClick(l, v, position, id);
28 | String name = tests[position];
29 |
30 | try
31 | {
32 | Class c = Class.forName("ibraim.opengles2." + name + "Activity");
33 | Intent i = new Intent(this, c);
34 | startActivity(i);
35 | }
36 | catch (ClassNotFoundException e)
37 | {
38 | e.printStackTrace();
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/ibraim/opengles2/TextureActivity.java:
--------------------------------------------------------------------------------
1 | package ibraim.opengles2;
2 |
3 | import android.app.Activity;
4 | import android.graphics.Bitmap;
5 | import android.graphics.BitmapFactory;
6 | import android.opengl.GLES20;
7 | import android.opengl.GLSurfaceView;
8 | import android.opengl.GLUtils;
9 | import android.os.Bundle;
10 |
11 | import javax.microedition.khronos.egl.EGLConfig;
12 | import javax.microedition.khronos.opengles.GL10;
13 | import java.nio.ByteBuffer;
14 | import java.nio.ByteOrder;
15 | import java.nio.FloatBuffer;
16 |
17 | public class TextureActivity extends Activity
18 | {
19 | private GLSurfaceView surface;
20 | private TextureRenderer renderer;
21 |
22 | @Override
23 | protected void onCreate(Bundle savedInstanceState)
24 | {
25 | super.onCreate(savedInstanceState);
26 |
27 | // If you don't know what we're doing here, take a look at the
28 | // epilepsy sample.
29 | surface = new GLSurfaceView(this);
30 | renderer = new TextureRenderer();
31 | surface.setEGLContextClientVersion(2);
32 | surface.setRenderer(renderer);
33 |
34 | setContentView(surface);
35 | }
36 |
37 | @Override
38 | protected void onResume()
39 | {
40 | super.onResume();
41 | surface.onResume();
42 | }
43 |
44 | @Override
45 | protected void onPause()
46 | {
47 | super.onPause();
48 | surface.onPause();
49 | renderer.tearDown();
50 | }
51 |
52 | private class TextureRenderer implements GLSurfaceView.Renderer
53 | {
54 | private int vertexHandle;
55 | private int fragmentHandle;
56 | private int programHandle = -1;
57 | private int[] textures = new int[1];
58 |
59 | // These two methods help to Load/Unload the shaders used by OpenGL Es 2.0
60 | // Remember that now OpenGL DOES NOT CONTAIN most of the 'old' OpenGL functions;
61 | // Now you need to create your own vertex and fragment shaders. Yay!
62 | public void setup()
63 | {
64 | // make sure there's nothing already created
65 | tearDown();
66 |
67 | // Vertex shader source.
68 | // Now things start to get interesting. Take note of a new attribute,
69 | // aTexPos, that will store the texture coordinate (the "places" of the texture that
70 | // we will use. We also have vTexPos, to pass the attribute value to the
71 | // fragment shader.
72 | String vertexSrc =
73 | "uniform mat4 uScreen;\n" +
74 | "attribute vec2 aPosition;\n" +
75 | "attribute vec2 aTexPos;\n" +
76 | "varying vec2 vTexPos;\n" +
77 | "void main() {\n" +
78 | " vTexPos = aTexPos;\n" +
79 | " gl_Position = uScreen * vec4(aPosition.xy, 0.0, 1.0);\n" +
80 | "}";
81 |
82 | // Our fragment shader.
83 | // Here we have a uniform (uTexture) that will hold the texture
84 | // for drawing. The 'color' of the vertex is calculated using the
85 | // texture coordinate (vTexPos) and the texture itself.
86 | String fragmentSrc =
87 | "precision mediump float;\n"+
88 | "uniform sampler2D uTexture;\n" +
89 | "varying vec2 vTexPos;\n" +
90 | "void main(void)\n" +
91 | "{\n" +
92 | " gl_FragColor = texture2D(uTexture, vTexPos);\n" +
93 | "}";
94 |
95 | // Lets load and compile our shaders, link the program
96 | // and tell OpenGL ES to use it for future drawing.
97 | vertexHandle = loadShader(GLES20.GL_VERTEX_SHADER, vertexSrc);
98 | fragmentHandle = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSrc);
99 | programHandle = createProgram(vertexHandle, fragmentHandle);
100 |
101 | GLES20.glUseProgram(programHandle);
102 | }
103 |
104 | public void tearDown()
105 | {
106 | if (programHandle != -1)
107 | {
108 | GLES20.glDeleteProgram(programHandle);
109 | GLES20.glDeleteShader(vertexHandle);
110 | GLES20.glDeleteShader(fragmentHandle);
111 | GLES20.glDeleteTextures(textures.length, textures, 0); // free the texture!
112 | }
113 | }
114 |
115 | // auxiliary shader functions. Doesn't matter WHAT you're trying to do, they're
116 | // always the same thing.
117 | private int loadShader(int shaderType, String shaderSource)
118 | {
119 | int handle = GLES20.glCreateShader(shaderType);
120 |
121 | if (handle == GLES20.GL_FALSE)
122 | throw new RuntimeException("Error creating shader!");
123 |
124 | // set and compile the shader
125 | GLES20.glShaderSource(handle, shaderSource);
126 | GLES20.glCompileShader(handle);
127 |
128 | // check if the compilation was OK
129 | int[] compileStatus = new int[1];
130 | GLES20.glGetShaderiv(handle, GLES20.GL_COMPILE_STATUS, compileStatus, 0);
131 |
132 | if (compileStatus[0] == 0)
133 | {
134 | String error = GLES20.glGetShaderInfoLog(handle);
135 | GLES20.glDeleteShader(handle);
136 | throw new RuntimeException("Error compiling shader: " + error);
137 | }
138 | else
139 | return handle;
140 | }
141 |
142 | private int createProgram(int vertexShader, int fragmentShader)
143 | {
144 | int handle = GLES20.glCreateProgram();
145 |
146 | if (handle == GLES20.GL_FALSE)
147 | throw new RuntimeException("Error creating program!");
148 |
149 | // attach the shaders and link the program
150 | GLES20.glAttachShader(handle, vertexShader);
151 | GLES20.glAttachShader(handle, fragmentShader);
152 | GLES20.glLinkProgram(handle);
153 |
154 | // check if the link was successful
155 | int[] linkStatus = new int[1];
156 | GLES20.glGetProgramiv(handle, GLES20.GL_LINK_STATUS, linkStatus, 0);
157 |
158 | if (linkStatus[0] == 0)
159 | {
160 | String error = GLES20.glGetProgramInfoLog(handle);
161 | GLES20.glDeleteProgram(handle);
162 | throw new RuntimeException("Error in program linking: " + error);
163 | }
164 | else
165 | return handle;
166 | }
167 |
168 | @Override
169 | public void onSurfaceCreated(GL10 gl, EGLConfig config)
170 | {
171 | // first, try to generate a texture handle
172 | GLES20.glGenTextures(1, textures, 0);
173 |
174 | if (textures[0] == GLES20.GL_FALSE)
175 | throw new RuntimeException("Error loading texture");
176 |
177 | // bind the texture and set parameters
178 | GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textures[0]);
179 | GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST);
180 | GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_NEAREST);
181 |
182 | // Load a bitmap from resources folder and pass it to OpenGL
183 | // in the end, we recycle it to free unneeded resources
184 | Bitmap b = BitmapFactory.decodeResource(getResources(), R.drawable.skull);
185 | GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, b, 0);
186 | b.recycle();
187 | }
188 |
189 | @Override
190 | public void onSurfaceChanged(GL10 gl, int width, int height)
191 | {
192 | // lets initialize everything
193 | setup();
194 |
195 | // discover the 'position' of the uScreen and uTexture
196 | int uScreenPos = GLES20.glGetUniformLocation(programHandle, "uScreen");
197 | int uTexture = GLES20.glGetUniformLocation(programHandle, "uTexture");
198 |
199 | // The uScreen matrix
200 | // This is explained in detail in the Triangle2d sample.
201 | float[] uScreen =
202 | {
203 | 2f/width, 0f, 0f, 0f,
204 | 0f, -2f/height, 0f, 0f,
205 | 0f, 0f, 0f, 0f,
206 | -1f, 1f, 0f, 1f
207 | };
208 |
209 | // Now, let's set the value.
210 | FloatBuffer b = ByteBuffer.allocateDirect(uScreen.length * 4).order(ByteOrder.nativeOrder()).asFloatBuffer();
211 | b.put(uScreen).position(0);
212 | GLES20.glUniformMatrix4fv(uScreenPos, b.limit() / uScreen.length, false, b);
213 |
214 | // Activate the first texture (GL_TEXTURE0) and bind it to our handle
215 | GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
216 | GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textures[0]);
217 | GLES20.glUniform1i(uTexture, 0);
218 |
219 | // set the viewport and a fixed, white background
220 | GLES20.glViewport(0, 0, width, height);
221 | GLES20.glClearColor(1f, 1f, 1f, 1f);
222 |
223 | // since we're using a PNG file with transparency, enable alpha blending.
224 | GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA);
225 | GLES20.glEnable(GLES20.GL_BLEND);
226 | }
227 |
228 | @Override
229 | public void onDrawFrame(GL10 gl)
230 | {
231 | // get the position of our attributes
232 | int aPosition = GLES20.glGetAttribLocation(programHandle, "aPosition");
233 | int aTexPos = GLES20.glGetAttribLocation(programHandle, "aTexPos");
234 |
235 | // Ok, now is the FUN part.
236 | // First of all, our image is a rectangle right? but in OpenGL, we can only draw
237 | // triangles! To remedy that we will use 4 vertices (V1 to V4) and draw using
238 | // the TRIANGLE_STRIP option. If you look closely to our positions, you will note
239 | // that we're drawing a 'N' (or 'Z') shaped line... and TRIANGLE_STRIP 'closes' the
240 | // remaining GAP between the vertices, so we have a rectangle (or square)! Yay!
241 | //
242 | // Apart from V1 to V4, we also specify the position IN THE TEXTURE. Each vertex
243 | // of our rectangle must relate to a position in the texture. The texture coordinates
244 | // are ALWAYS 0,0 on bottom-left and 1,1 on top-right. Take a look at the values
245 | // used and you will understand it easily. If not, mess a little bit with the values
246 | // and take a look at the result.
247 | float[] data =
248 | {
249 | 50f, 100f, //V1
250 | 0f, 0f, //Texture coordinate for V1
251 |
252 | 50f, 300f, //V2
253 | 0f, 1f,
254 |
255 | 300f, 100f, //V3
256 | 1f, 0f,
257 |
258 | 300f, 300f, //V4
259 | 1f, 1f
260 | };
261 |
262 | // constants. You know the drill by now.
263 | final int FLOAT_SIZE = 4;
264 | final int POSITION_SIZE = 2;
265 | final int TEXTURE_SIZE = 2;
266 | final int TOTAL_SIZE = POSITION_SIZE + TEXTURE_SIZE;
267 | final int POSITION_OFFSET = 0;
268 | final int TEXTURE_OFFSET = 2;
269 |
270 | // Again, a FloatBuffer will be used to pass the values
271 | FloatBuffer b = ByteBuffer.allocateDirect(data.length * FLOAT_SIZE).order(ByteOrder.nativeOrder()).asFloatBuffer();
272 | b.put(data);
273 |
274 | // Position of our image
275 | b.position(POSITION_OFFSET);
276 | GLES20.glVertexAttribPointer(aPosition, POSITION_SIZE, GLES20.GL_FLOAT, false, TOTAL_SIZE * FLOAT_SIZE, b);
277 | GLES20.glEnableVertexAttribArray(aPosition);
278 |
279 | // Positions of the texture
280 | b.position(TEXTURE_OFFSET);
281 | GLES20.glVertexAttribPointer(aTexPos, TEXTURE_SIZE, GLES20.GL_FLOAT, false, TOTAL_SIZE * FLOAT_SIZE, b);
282 | GLES20.glEnableVertexAttribArray(aTexPos);
283 |
284 | // Clear the screen and draw the rectangle
285 | GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
286 | GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
287 | }
288 | }
289 | }
290 |
--------------------------------------------------------------------------------
/src/ibraim/opengles2/Triangle2dActivity.java:
--------------------------------------------------------------------------------
1 | package ibraim.opengles2;
2 |
3 | import android.app.Activity;
4 | import android.opengl.GLES20;
5 | import android.opengl.GLSurfaceView;
6 | import android.os.Bundle;
7 |
8 | import javax.microedition.khronos.egl.EGLConfig;
9 | import javax.microedition.khronos.opengles.GL10;
10 | import java.nio.ByteBuffer;
11 | import java.nio.ByteOrder;
12 | import java.nio.FloatBuffer;
13 |
14 | public class Triangle2dActivity extends Activity
15 | {
16 | private GLSurfaceView surface;
17 | private Triangle2dRenderer renderer;
18 |
19 | @Override
20 | protected void onCreate(Bundle savedInstanceState)
21 | {
22 | super.onCreate(savedInstanceState);
23 |
24 | // If you don't know what we're doing here, take a look at the
25 | // epilepsy sample.
26 | surface = new GLSurfaceView(this);
27 | renderer = new Triangle2dRenderer();
28 | surface.setEGLContextClientVersion(2);
29 | surface.setRenderer(renderer);
30 |
31 | setContentView(surface);
32 | }
33 |
34 | @Override
35 | protected void onResume()
36 | {
37 | super.onResume();
38 | surface.onResume();
39 | }
40 |
41 | @Override
42 | protected void onPause()
43 | {
44 | super.onPause();
45 | surface.onPause();
46 | renderer.tearDown();
47 | }
48 |
49 | private class Triangle2dRenderer implements GLSurfaceView.Renderer
50 | {
51 | private int vertexHandle;
52 | private int fragmentHandle;
53 | private int programHandle = -1;
54 |
55 | // These two methods help to Load/Unload the shaders used by OpenGL Es 2.0
56 | // Remember that now OpenGL DOES NOT CONTAIN most of the 'old' OpenGL functions;
57 | // Now you need to create your own vertex and fragment shaders. Yay!
58 | public void setup()
59 | {
60 | // make sure there's nothing already created
61 | tearDown();
62 |
63 | // Vertex shader source.
64 | // This shader uses a constant 4x4 matrix 'uScreen' and multiplies it to
65 | // the parameter aPosition. The x and y values of aPosition will be filled with
66 | // the vertices of our triangle. uScreen will be a matrix that, when multiplied
67 | // with the values of our position will CONVERT these values to the OpenGL coordinate
68 | // system. This way we can, say, inform our coordinates in 'pixels' and let OpenGL
69 | // figure out were the hell the pixels are. More on this later.
70 | String vertexSrc =
71 | "uniform mat4 uScreen;\n" +
72 | "attribute vec2 aPosition;\n" +
73 | "void main() {\n" +
74 | " gl_Position = uScreen * vec4(aPosition.xy, 0.0, 1.0);\n" +
75 | "}";
76 |
77 | // Our fragment shader. Always returns the color RED.
78 | String fragmentSrc =
79 | "precision mediump float;\n"+
80 | "void main(void)\n" +
81 | "{\n" +
82 | " gl_FragColor = vec4(1, 0, 0, 1);\n" +
83 | "}";
84 |
85 | // Lets load and compile our shaders, link the program
86 | // and tell OpenGL ES to use it for future drawing.
87 | vertexHandle = loadShader(GLES20.GL_VERTEX_SHADER, vertexSrc);
88 | fragmentHandle = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSrc);
89 | programHandle = createProgram(vertexHandle, fragmentHandle);
90 |
91 | GLES20.glUseProgram(programHandle);
92 | }
93 |
94 | public void tearDown()
95 | {
96 | if (programHandle != -1)
97 | {
98 | GLES20.glDeleteProgram(programHandle);
99 | GLES20.glDeleteShader(vertexHandle);
100 | GLES20.glDeleteShader(fragmentHandle);
101 | }
102 | }
103 |
104 | // auxiliary shader functions. Doesn't matter WHAT you're trying to do, they're
105 | // always the same thing.
106 | private int loadShader(int shaderType, String shaderSource)
107 | {
108 | int handle = GLES20.glCreateShader(shaderType);
109 |
110 | if (handle == GLES20.GL_FALSE)
111 | throw new RuntimeException("Error creating shader!");
112 |
113 | // set and compile the shader
114 | GLES20.glShaderSource(handle, shaderSource);
115 | GLES20.glCompileShader(handle);
116 |
117 | // check if the compilation was OK
118 | int[] compileStatus = new int[1];
119 | GLES20.glGetShaderiv(handle, GLES20.GL_COMPILE_STATUS, compileStatus, 0);
120 |
121 | if (compileStatus[0] == 0)
122 | {
123 | String error = GLES20.glGetShaderInfoLog(handle);
124 | GLES20.glDeleteShader(handle);
125 | throw new RuntimeException("Error compiling shader: " + error);
126 | }
127 | else
128 | return handle;
129 | }
130 |
131 | private int createProgram(int vertexShader, int fragmentShader)
132 | {
133 | int handle = GLES20.glCreateProgram();
134 |
135 | if (handle == GLES20.GL_FALSE)
136 | throw new RuntimeException("Error creating program!");
137 |
138 | // attach the shaders and link the program
139 | GLES20.glAttachShader(handle, vertexShader);
140 | GLES20.glAttachShader(handle, fragmentShader);
141 | GLES20.glLinkProgram(handle);
142 |
143 | // check if the link was successful
144 | int[] linkStatus = new int[1];
145 | GLES20.glGetProgramiv(handle, GLES20.GL_LINK_STATUS, linkStatus, 0);
146 |
147 | if (linkStatus[0] == 0)
148 | {
149 | String error = GLES20.glGetProgramInfoLog(handle);
150 | GLES20.glDeleteProgram(handle);
151 | throw new RuntimeException("Error in program linking: " + error);
152 | }
153 | else
154 | return handle;
155 | }
156 |
157 | @Override
158 | public void onSurfaceCreated(GL10 gl, EGLConfig config)
159 | {
160 | // as usual, nothing to do here.
161 | }
162 |
163 | @Override
164 | public void onSurfaceChanged(GL10 gl, int width, int height)
165 | {
166 | // lets initialize everything
167 | setup();
168 |
169 | // discover the 'position' of the uScreen value
170 | int uScreenPos = GLES20.glGetUniformLocation(programHandle, "uScreen");
171 |
172 | // The uScreen matrix
173 | // Let's stop for a minute and think on what we're doing here.
174 | //
175 | // First of all, the only coordinate system that OpenGL understands
176 | // put the center of the screen at the 0,0 position. The maximum value of
177 | // the X axis is 1 (rightmost part of the screen) and the minimum is -1
178 | // (leftmost part of the screen). The same thing goes for the Y axis,
179 | // where 1 is the top of the screen and -1 the bottom.
180 | //
181 | // However, when you're doing a 2d application you often need to think in 'pixels'
182 | // (or something like that). If you have a 300x300 screen, you want to see the center
183 | // at 150,150 not 0,0!
184 | //
185 | // The solution to this 'problem' is to multiply a matrix with your position to
186 | // another matrix that will convert 'your' coordinates to the one OpenGL expects.
187 | // There's no magic in this, only a bit of math. Try to multiply the uScreen matrix
188 | // to the 150,150 position in a sheet of paper and look at the results.
189 | //
190 | // IMPORTANT: When trying to calculate the matrix on paper, you should treat the
191 | // uScreen ROWS as COLUMNS and vice versa. This happens because OpenGL expect the
192 | // matrix values ordered in a more efficient way, that unfortunately is different
193 | // from the mathematical notation :(
194 | float[] uScreen =
195 | {
196 | 2f/width, 0f, 0f, 0f,
197 | 0f, -2f/height, 0f, 0f,
198 | 0f, 0f, 0f, 0f,
199 | -1f, 1f, 0f, 1f
200 | };
201 |
202 | // Now, let's set the value.
203 | FloatBuffer b = ByteBuffer.allocateDirect(uScreen.length * 4).order(ByteOrder.nativeOrder()).asFloatBuffer();
204 | b.put(uScreen).position(0);
205 | GLES20.glUniformMatrix4fv(uScreenPos, b.limit() / uScreen.length, false, b);
206 |
207 | // set the viewport and a fixed, white background
208 | GLES20.glViewport(0, 0, width, height);
209 | GLES20.glClearColor(1f, 1f, 1f, 1f);
210 | }
211 |
212 | @Override
213 | public void onDrawFrame(GL10 gl)
214 | {
215 | // get the position of 'aPosition'
216 | int aPos = GLES20.glGetAttribLocation(programHandle, "aPosition");
217 |
218 | // The triangle vertices. Note how I'm using
219 | // a 'pixel' coordinate system. This is not in the center of the
220 | // screen or anything; this is in absolute position, will vary depending
221 | // on the size of your display.
222 | float[] data =
223 | {
224 | 50f, 100f,
225 | 300f, 100f,
226 | 200f, 170f,
227 | };
228 |
229 | // Again, a FloatBuffer will be used to pass the values
230 | FloatBuffer b = ByteBuffer.allocateDirect(data.length * 4).order(ByteOrder.nativeOrder()).asFloatBuffer();
231 | b.put(data).position(0);
232 |
233 | // Enable and set the vertex attribute to accept our array.
234 | // This makes possible to inform all of the vertices in one call.
235 | GLES20.glVertexAttribPointer(aPos, 2, GLES20.GL_FLOAT, false, 0, b);
236 | GLES20.glEnableVertexAttribArray(aPos);
237 |
238 | // Clear the screen and draw the triangle
239 | GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
240 | GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, 3);
241 | }
242 | }
243 | }
244 |
--------------------------------------------------------------------------------
/src/ibraim/opengles2/TriangleColorActivity.java:
--------------------------------------------------------------------------------
1 | package ibraim.opengles2;
2 |
3 | import android.app.Activity;
4 | import android.opengl.GLES20;
5 | import android.opengl.GLSurfaceView;
6 | import android.os.Bundle;
7 |
8 | import javax.microedition.khronos.egl.EGLConfig;
9 | import javax.microedition.khronos.opengles.GL10;
10 | import java.nio.ByteBuffer;
11 | import java.nio.ByteOrder;
12 | import java.nio.FloatBuffer;
13 |
14 | public class TriangleColorActivity extends Activity
15 | {
16 | private GLSurfaceView surface;
17 | private TriangleColorRenderer renderer;
18 |
19 | @Override
20 | protected void onCreate(Bundle savedInstanceState)
21 | {
22 | super.onCreate(savedInstanceState);
23 |
24 | // If you don't know what we're doing here, take a look at the
25 | // epilepsy sample.
26 | surface = new GLSurfaceView(this);
27 | renderer = new TriangleColorRenderer();
28 | surface.setEGLContextClientVersion(2);
29 | surface.setRenderer(renderer);
30 |
31 | setContentView(surface);
32 | }
33 |
34 | @Override
35 | protected void onResume()
36 | {
37 | super.onResume();
38 | surface.onResume();
39 | }
40 |
41 | @Override
42 | protected void onPause()
43 | {
44 | super.onPause();
45 | surface.onPause();
46 | renderer.tearDown();
47 | }
48 |
49 | private class TriangleColorRenderer implements GLSurfaceView.Renderer
50 | {
51 | private int vertexHandle;
52 | private int fragmentHandle;
53 | private int programHandle = -1;
54 |
55 | // These two methods help to Load/Unload the shaders used by OpenGL Es 2.0
56 | // Remember that now OpenGL DOES NOT CONTAIN most of the 'old' OpenGL functions;
57 | // Now you need to create your own vertex and fragment shaders. Yay!
58 | public void setup()
59 | {
60 | // make sure there's nothing already created
61 | tearDown();
62 |
63 | // Vertex shader source.
64 | // This is the the same one used in the Triangle2d sample, but with
65 | // an extra attribute: aColor, that will hold a RGB value for the color
66 | // of the vertex. This value will be passed directly to vColor
67 | String vertexSrc =
68 | "uniform mat4 uScreen;\n" +
69 | "attribute vec2 aPosition;\n" +
70 | "attribute vec3 aColor;\n" +
71 | "varying vec3 vColor;\n" +
72 | "void main() {\n" +
73 | " gl_Position = uScreen * vec4(aPosition.xy, 0.0, 1.0);\n" +
74 | " vColor = aColor;\n" +
75 | "}";
76 |
77 | // Our fragment shader. Just return vColor.
78 | // If you look at this source and just said 'WTF?', remember
79 | // that all the attributes are defined in the VERTEX shader and
80 | // all the 'varying' vars are considered OUTPUT of vertex shader
81 | // and INPUT of the fragment shader. Here we just use the color
82 | // we received and add a alpha value of 1.
83 | String fragmentSrc =
84 | "precision mediump float;\n"+
85 | "varying vec3 vColor;\n" +
86 | "void main(void)\n" +
87 | "{\n" +
88 | " gl_FragColor = vec4(vColor.xyz, 1);\n" +
89 | "}";
90 |
91 | // Lets load and compile our shaders, link the program
92 | // and tell OpenGL ES to use it for future drawing.
93 | vertexHandle = loadShader(GLES20.GL_VERTEX_SHADER, vertexSrc);
94 | fragmentHandle = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSrc);
95 | programHandle = createProgram(vertexHandle, fragmentHandle);
96 |
97 | GLES20.glUseProgram(programHandle);
98 | }
99 |
100 | public void tearDown()
101 | {
102 | if (programHandle != -1)
103 | {
104 | GLES20.glDeleteProgram(programHandle);
105 | GLES20.glDeleteShader(vertexHandle);
106 | GLES20.glDeleteShader(fragmentHandle);
107 | }
108 | }
109 |
110 | // auxiliary shader functions. Doesn't matter WHAT you're trying to do, they're
111 | // always the same thing.
112 | private int loadShader(int shaderType, String shaderSource)
113 | {
114 | int handle = GLES20.glCreateShader(shaderType);
115 |
116 | if (handle == GLES20.GL_FALSE)
117 | throw new RuntimeException("Error creating shader!");
118 |
119 | // set and compile the shader
120 | GLES20.glShaderSource(handle, shaderSource);
121 | GLES20.glCompileShader(handle);
122 |
123 | // check if the compilation was OK
124 | int[] compileStatus = new int[1];
125 | GLES20.glGetShaderiv(handle, GLES20.GL_COMPILE_STATUS, compileStatus, 0);
126 |
127 | if (compileStatus[0] == 0)
128 | {
129 | String error = GLES20.glGetShaderInfoLog(handle);
130 | GLES20.glDeleteShader(handle);
131 | throw new RuntimeException("Error compiling shader: " + error);
132 | }
133 | else
134 | return handle;
135 | }
136 |
137 | private int createProgram(int vertexShader, int fragmentShader)
138 | {
139 | int handle = GLES20.glCreateProgram();
140 |
141 | if (handle == GLES20.GL_FALSE)
142 | throw new RuntimeException("Error creating program!");
143 |
144 | // attach the shaders and link the program
145 | GLES20.glAttachShader(handle, vertexShader);
146 | GLES20.glAttachShader(handle, fragmentShader);
147 | GLES20.glLinkProgram(handle);
148 |
149 | // check if the link was successful
150 | int[] linkStatus = new int[1];
151 | GLES20.glGetProgramiv(handle, GLES20.GL_LINK_STATUS, linkStatus, 0);
152 |
153 | if (linkStatus[0] == 0)
154 | {
155 | String error = GLES20.glGetProgramInfoLog(handle);
156 | GLES20.glDeleteProgram(handle);
157 | throw new RuntimeException("Error in program linking: " + error);
158 | }
159 | else
160 | return handle;
161 | }
162 |
163 | @Override
164 | public void onSurfaceCreated(GL10 gl, EGLConfig config)
165 | {
166 | // as usual, nothing to do here.
167 | }
168 |
169 | @Override
170 | public void onSurfaceChanged(GL10 gl, int width, int height)
171 | {
172 | // lets initialize everything
173 | setup();
174 |
175 | // discover the 'position' of the uScreen value
176 | int uScreenPos = GLES20.glGetUniformLocation(programHandle, "uScreen");
177 |
178 | // The uScreen matrix
179 | // This is explained in detail in the Triangle2d sample.
180 | float[] uScreen =
181 | {
182 | 2f/width, 0f, 0f, 0f,
183 | 0f, -2f/height, 0f, 0f,
184 | 0f, 0f, 0f, 0f,
185 | -1f, 1f, 0f, 1f
186 | };
187 |
188 | // Now, let's set the value.
189 | FloatBuffer b = ByteBuffer.allocateDirect(uScreen.length * 4).order(ByteOrder.nativeOrder()).asFloatBuffer();
190 | b.put(uScreen).position(0);
191 | GLES20.glUniformMatrix4fv(uScreenPos, b.limit() / uScreen.length, false, b);
192 |
193 | // set the viewport and a fixed, white background
194 | GLES20.glViewport(0, 0, width, height);
195 | GLES20.glClearColor(1f, 1f, 1f, 1f);
196 | }
197 |
198 | @Override
199 | public void onDrawFrame(GL10 gl)
200 | {
201 | // get the position of our attributes
202 | int aPosition = GLES20.glGetAttribLocation(programHandle, "aPosition");
203 | int aColor = GLES20.glGetAttribLocation(programHandle, "aColor");
204 |
205 | // The triangle vertices. Note how I'm putting the
206 | // vertex position and the color on the same array.
207 | // This ensures maximum performance for this kind of operation.
208 | float[] data =
209 | {
210 | // XY, RGB
211 | 50f, 100f,
212 | 1f, 0f, 0f,
213 |
214 | 300f, 100f,
215 | 0f, 1f, 0f,
216 |
217 | 200f, 170f,
218 | 0f, 0f, 1f
219 | };
220 |
221 | // Now we will declare some constants just to make things easier to
222 | // understand. The values should be obvious if you look at the data array
223 | final int FLOAT_SIZE = 4;
224 | final int POSITION_SIZE = 2;
225 | final int COLOR_SIZE = 3;
226 | final int TOTAL_SIZE = POSITION_SIZE + COLOR_SIZE;
227 | final int POSITION_OFFSET = 0; // the start of the first position
228 | final int COLOR_OFFSET = 2; // the start of the first color
229 |
230 | // Again, a FloatBuffer will be used to pass the values
231 | FloatBuffer b = ByteBuffer.allocateDirect(data.length * FLOAT_SIZE).order(ByteOrder.nativeOrder()).asFloatBuffer();
232 | b.put(data);
233 |
234 | // Enable and set the vertex attribute to accept our array.
235 | // This will set ONLY the positions
236 | b.position(POSITION_OFFSET);
237 | GLES20.glVertexAttribPointer(aPosition, POSITION_SIZE, GLES20.GL_FLOAT, false, TOTAL_SIZE * FLOAT_SIZE, b);
238 | GLES20.glEnableVertexAttribArray(aPosition);
239 |
240 | // Now, we do the same thing for the color attribute
241 | b.position(COLOR_OFFSET);
242 | GLES20.glVertexAttribPointer(aColor, COLOR_SIZE, GLES20.GL_FLOAT, false, TOTAL_SIZE * FLOAT_SIZE, b);
243 | GLES20.glEnableVertexAttribArray(aColor);
244 |
245 | // Clear the screen and draw the triangle
246 | GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
247 | GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, 3);
248 | }
249 | }
250 | }
251 |
--------------------------------------------------------------------------------