├── .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 | --------------------------------------------------------------------------------