quick get start
Key Classes
- GLSurfaceView
- Carriers drawn
- TextureView is available for small areas
- SurfaceView can theoretically be used
- GLSurfaceView.Renderer
- Essential Drawing Action
Keying a GLSurfaceView object
class MyGLSurfaceView extends GLSurfaceView { public MyGLSurfaceView(Context context){ super(context); // Create an OpenGL ES 2.0 context setEGLContextClientVersion(2); // Set Renderer to GLSurfaceView setRenderer(new MyRenderer()); // Draw view only when drawing data changes setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY); } }
GLSurfaceView.RENDERMODE_WHEN_DIRTY prevents the frames of GLSurfaceView from being drawn until you call requestRender(), which is very efficient.
Renderer drawing gray background
public class MyGL20Renderer implements GLSurfaceView.Renderer { // Called only once to set the OpenGLES environment for view public void onSurfaceCreated(GL10 unused, EGLConfig config) { // Set the color of the background GLES20.glClearColor(0.5f, 0.5f, 0.5f, 1.0f); } public void onDrawFrame(GL10 unused) { // Redraw Background Color GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); } public void onSurfaceChanged(GL10 unused, int width, int height) { GLES20.glViewport(0, 0, width, height); } }
These methods all have a GL10 parameter, but you are using the OpengGLES 2.0 API s, which are designed to make the Android framework easily compatible with OpenGLES versions.
Define Shape
With OpenGLES's coordinate system relative to Android, shapes can be defined.Triangles are the basis of all shapes, because many complex three-dimensional graphics can be thought of as a large number of triangles.Because we know how to define triangles, and then we make squares out of them.
Triangle
To define a triangle, you need to define the coordinates of its three vertices in three-dimensional space.In OpenGL, a typical way is to define an array of vertices of floating point type for coordinates.
For maximum efficiency, you should write these coordinates into a ByteBuffer, which will be passed to the OpenGLES graphics pipeline for processing.
By default, OpenGLES assumes that [0,0,0] is the center of the GLSurfaceView frame, [1,1,0] is the upper right corner, [-1, -1,0] is the lower left corner.The order of coordinates is (X, Y, Z).
class Triangle { private FloatBuffer vertexBuffer; // Number of coordinates per vertex in the array static final int COORDS_PER_VERTEX = 3; static float triangleCoords[] = { // In counterclockwise order: 0.0f, 0.622008459f, 0.0f, // top -0.5f, -0.311004243f, 0.0f, // bottom left 0.5f, -0.311004243f, 0.0f // bottom right }; // Set colors, red, green, blue and alpha (opacity) float color[] = { 0.63671875f, 0.76953125f, 0.22265625f, 1.0f }; public Triangle() { // Initialize vertex byte buffer to store shape coordinates ByteBuffer bb = ByteBuffer.allocateDirect( // (Number of coordinates * 4)float takes up four bytes triangleCoords.length * 4); // Set the native byte order of the device bb.order(ByteOrder.nativeOrder()); // Create a floating-point buffer from ByteBuffer vertexBuffer = bb.asFloatBuffer(); // Add coordinates to FloatBuffer vertexBuffer.put(triangleCoords); // Set buffer to read from first coordinate vertexBuffer.position(0); } }
The coordinates of this shape are defined counterclockwise.Drawing order is important because it defines which side is the front of the shape and which side is the back. With the cullface feature of OpenGLES, you can draw only the front side and not the back side.
Square
There are many ways to define a square, typically by using two triangles.
This square can be defined by defining four key coordinate points and the drawing order of two triangles as shown in the figure above.
class Square { private FloatBuffer vertexBuffer; private ShortBuffer drawListBuffer; // Number of coordinates per vertex static final int COORDS_PER_VERTEX = 3; static float squareCoords[] = { -0.5f, 0.5f, 0.0f, // top left -0.5f, -0.5f, 0.0f, // bottom left 0.5f, -0.5f, 0.0f, // bottom right 0.5f, 0.5f, 0.0f }; // top right private short drawOrder[] = { 0, 1, 2, 0, 2, 3 }; // Drawing order of vertices public Square() { // initialize vertex byte buffer for shape coordinates ByteBuffer bb = ByteBuffer.allocateDirect( // (Number of coordinates * 4) squareCoords.length * 4); bb.order(ByteOrder.nativeOrder()); vertexBuffer = bb.asFloatBuffer(); vertexBuffer.put(squareCoords); vertexBuffer.position(0); // Initialize byte buffer for drawing list ByteBuffer dlb = ByteBuffer.allocateDirect( // (Number of coordinates in corresponding order * 2)short is 2 bytes drawOrder.length * 2); dlb.order(ByteOrder.nativeOrder()); drawListBuffer = dlb.asShortBuffer(); drawListBuffer.put(drawOrder); drawListBuffer.position(0); } }
Draw Shape
OpenGL ES 2.0 requires a lot of code to draw a well-defined graph. Typically, you need to define the following:
- VertexShader - Vertices used to render shapes
- FragmentShader - Appearance (color or texture) used to render shapes
- Program-Contains the Shader you want to use to draw one or more shapes.
At least one VertexShader and one FragmentShader need to be protected in the drawing Program.
OpenGLShading Language
Following is the OpenGLShading Language (GLSL) code, Shader must be compiled to be added to the Program and then drawn through the Program.
private final String vertexShaderCode = "attribute vec4 vPosition;" + "void main() {" + " gl_Position = vPosition;" + "}"; private final String fragmentShaderCode = "precision mediump float;" + "uniform vec4 vColor;" + "void main() {" + " gl_FragColor = vColor;" + "}";
Shader can be compiled using the following static methods
public static int loadShader(int type, String shaderCode){ // Create a vertex shader type (GLES20.GL_VERTEX_SHADER) // Or fragment shader type (GLES20.GL_FRAGMENT_SHADER) int shader = GLES20.glCreateShader(type); // Add source code to shader and compile it GLES20.glShaderSource(shader, shaderCode); GLES20.glCompileShader(shader); return shader; }
Compiling OpenGLES shader s and linked programs is CPU intensive, so you should avoid executing them multiple times.
Draw
draw() is generally created in the shape class to draw.The following code sets the location and color values to the VertexShader and FragmentShader of the shape and then performs the drawing function.
private final int vertexCount = triangleCoords.length; // COORDS_PER_VERTEX; private final int vertexStride = COORDS_PER_VERTEX * 4; // bytes per vertex public void draw() { // Add program to OpenGL ES environment GLES20.glUseProgram(mProgram); // Get handle pointing to member vPosition of vertex shader mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition"); // Enable handle on an array of vertices pointing to a triangle GLES20.glEnableVertexAttribArray(mPositionHandle); // Preparing coordinate data for triangles GLES20.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, vertexStride, vertexBuffer); // Get handle pointing to member vColor of fragment shader mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor"); // Set the color of the triangle GLES20.glUniform4fv(mColorHandle, 1, color, 0); // Draw a triangle GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount); // Disable vertex arrays pointing to triangles GLES20.glDisableVertexAttribArray(mPositionHandle); }
Ultimately, you only need to call the draw() method on Renderer's onDrawFrame() to complete the shape drawing.
Square's drawing logic is similar to Triangle's, requiring only modifications to key drawing methods.
//GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount); GLES20.glDrawElements(GLES20.GL_TRIANGLES, drawOrder.length, GLES20.GL_UNSIGNED_SHORT, drawListBuffer);