All the preparation is complete. It is now time to render to the screen. The OGL commands are:
1 2 3 4 5 | GLES20.glUseProgram( shaderProgram ); GLES20.glUniformMatrix4fv( uMVPVariableLocation, 1, false, MVPMatrix, 0 ); GLES20.glVertexAttribPointer( positionVariableLocation, 3, GLES20.GL_FLOAT, false, 0, fBuffer ); GLES20.glEnableVertexAttribArray( positionVariableLocation ); GLES20.glDrawArrays( GLES20.GL_POINTS, 0, 3 ); |
LibGDX accomplishes that this way:
1 2 3 4 5 6 7 8 9 | shaderProgram.begin(); // internally calls Gdx.gl.glUseProgram(); shaderProgram.setUniformMatrix( u_projViewTrans, cam.combined ); shaderProgram.setUniformMatrix( u_worldTrans, idt ); Gdx.gl.glEnable( Gdx.gl.GL_VERTEX_PROGRAM_POINT_SIZE ); Gdx.gl.glEnable( Gdx.gl.GL_DEPTH_TEST ); Gdx.gl.glDrawArrays( Gdx.gl.GL_POINTS, 0, vertisees.getNumVertices() ); shaderProgram.end(); |
There isn't much to break down here since we've already covered the shader program class in the last section. Line 1 in both listings are equivalent. Line 2 in Listing #1 is equivalent to lines 3 and 4 in Listing #2; we are telling the shader program the two matrices (we retrieved their references at the end of the last step) we are using to translate to/from camera coordinates and to/from world coordinates. Lines 3 - 4 in Listing #1 are called internally by the VBO's bind() function which we saw in step #3 (you'll see how it's called in the complete program below). Lines 5 - 8 in Listing #2 are non-libGDX OGL commands. Line 5 in Listing #1 is obviously equivalent to line 7 in Listing #2. And that's everything.
Let's finish with a complete listing of all the libGDX code from the past couple of sections:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 | import com.badlogic.gdx.ApplicationAdapter; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.graphics.Camera; import com.badlogic.gdx.graphics.GL20; import com.badlogic.gdx.graphics.PerspectiveCamera; import com.badlogic.gdx.graphics.VertexAttribute; import com.badlogic.gdx.graphics.VertexAttributes; import com.badlogic.gdx.graphics.glutils.ShaderProgram; import com.badlogic.gdx.graphics.glutils.VertexBufferObject; import com.badlogic.gdx.math.MathUtils; import com.badlogic.gdx.math.Matrix4; public class Test extends ApplicationAdapter { public float vertices[]; public VertexBufferObject vertisees; public String vertexShader; public String fragmentShader; public ShaderProgram shaderProgram; //transform matrix private Matrix4 idt; private Camera cam; private int u_projViewTrans, u_worldTrans; private VertexAttributes vAs; @Override public void create() { //identity matrix used for world transform idt = new Matrix4().idt(); //create some points with color vertices = new float[]{ 0.0f, 0.0f, 0.0f, // Vertex 1 (x, y, z), (r, g, b, a) MathUtils.random(0, 1f), MathUtils.random(0, 1f), MathUtils.random(0, 1f), MathUtils.random(0, 1f), 3f, 2f, -2.0f, // Vertex 2 (x, y, z), (r, g, b, a) MathUtils.random(0, 1f), MathUtils.random(0, 1f), MathUtils.random(0, 1f), MathUtils.random(0, 1f), 5f, 5f, 5f, // Vertex 3 (x, y, z), (...) MathUtils.random(0, 1f), MathUtils.random(0, 1f), MathUtils.random(0, 1f), MathUtils.random(0, 1f), 3, 4, 2, //Vertex 4 (...), (...) MathUtils.random(0, 1f), MathUtils.random(0, 1f), MathUtils.random(0, 1f), MathUtils.random(0, 1f), 5, 6, 7, //Vertex 5 ... MathUtils.random(0, 1f), MathUtils.random(0, 1f), MathUtils.random(0, 1f), MathUtils.random(0, 1f), 1, 3, 8, //... MathUtils.random(0, 1f), MathUtils.random(0, 1f), MathUtils.random(0, 1f), MathUtils.random(0, 1f), 2, 2, 2, MathUtils.random(0, 1f), MathUtils.random(0, 1f), MathUtils.random(0, 1f), MathUtils.random(0, 1f) }; //create vertex attributes which define how data is accessed in VBO VertexAttribute vA = new VertexAttribute(VertexAttributes.Usage.Position, 3, "a_position"); VertexAttribute vC = new VertexAttribute(VertexAttributes.Usage.Position, 4, "a_color"); vAs = new VertexAttributes( new VertexAttribute[]{vA, vC} ); //create VBO and pass in vertex attributes object vertisees = new VertexBufferObject( false, (vertices.length / 7 ), vAs ); //set up window into our virtual space cam = new PerspectiveCamera( 67, Gdx.graphics.getWidth(), Gdx.graphics.getHeight() ); cam.position.set( 0f, 0f, 20f ); cam.lookAt( 0, 0, 0 ); cam.near = 0.1f; cam.far = 1000f; cam.update(); //create and compile shaders vertexShader = Gdx.files.internal("vertexShader.glsl").readString(); fragmentShader = Gdx.files.internal("fragmentShader.glsl").readString(); shaderProgram = new ShaderProgram( vertexShader, fragmentShader ); //get shader code variable pointers u_projViewTrans = shaderProgram.getUniformLocation("u_projViewTrans"); u_worldTrans = shaderProgram.getUniformLocation("u_worldTrans"); //let OGL know where vertex data is vertisees.bind( shaderProgram ); //gdx.gl.bindbuffer && gdx.gl.glBufferData vertisees.setVertices( vertices, 0, vertices.length ); } @Override public void render () { Gdx.gl.glViewport( 0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight() ); Gdx.gl.glClear( GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT ); shaderProgram.begin(); //Gdx.gl.glUseProgram( shadProgram ); shaderProgram.setUniformMatrix( u_projViewTrans, cam.combined ); shaderProgram.setUniformMatrix( u_worldTrans, idt ); Gdx.gl.glEnable( Gdx.gl.GL_VERTEX_PROGRAM_POINT_SIZE ); Gdx.gl.glEnable( Gdx.gl.GL_DEPTH_TEST ); //draw the points on the screen Gdx.gl.glDrawArrays( Gdx.gl.GL_POINTS, 0, vertisees.getNumVertices() ); shaderProgram.end(); } public void dispose(){ vertisees.dispose(); shaderProgram.dispose(); } } |
And our two .glsl files:
vertexShader.glsl
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | attribute vec3 a_position; attribute vec4 a_color; uniform mat4 u_worldTrans; uniform mat4 u_projViewTrans; varying vec4 v_color; void main() { v_color = a_color; gl_Position = u_projViewTrans * u_worldTrans * vec4(a_position, 1.0); vec3 ndc = gl_Position.xyz / gl_Position.w ; // perspective divide. float zDist = 1.0 - ndc.z; // 1 is close to camera, 0 is far gl_PointSize = 500.0 * zDist; // between 0 and 50 now. } |
fragmentShader.glsl
1 2 3 4 5 6 7 8 9 10 11 | #ifdef GL_FRAGMENT_PRECISION_HIGH precision highp float; #else precision mediump float; #endif varying vec4 v_color; void main() { gl_FragColor = vec4(v_color); } |
And our output should be seven multi-colored square points. The colors will vary since they are randomly generated:
We have now covered the basics of libGDX's abstraction layer for OGL. For breaking down higher levels of abstraction involving models, cameras, lighting, materials, etc. head over to xoppa's blog which is also linked in the "Useful Reading" link in the sidebar.
Next.