Vertex color material and shaders

The Cube component needs a Material to render it on the display. Our Cube has separate colors for each face, defined as separate vertex colors. We'll define a VertexColorMaterial instance and the corresponding shaders.

Vertex color shaders

At a minimum, the OpenGL pipeline requires that we define a vertex shader, which transforms vertices from 3D space to 2D, and a fragment shader, which calculates the pixel color values for a raster segment. Similar to the simple shaders that we created in Chapter 3, Cardboard Box, we'll create two files, vertex_color_vertex.shader and vertex_color_fragment.shader. Unless you have done so already, create a new Android resource directory with the raw type and name it raw. Then, for each file, right-click on the directory, and go to New | File. Use the following code for each of the two files. The code for the vertex shader is as follows:

// File:res/raw/vertex_color_vertex.shader
uniform mat4 u_Model;
uniform mat4 u_MVP;

attribute vec4 a_Position;
attribute vec4 a_Color;

varying vec4 v_Color;

void main() {
   v_Color = a_Color;
   gl_Position = u_MVP * a_Position;
}

The code for the fragment shader is as follows:

//File: res/raw/vertex_color_fragment.shader
precision mediump float;
varying vec4 v_Color;

void main() {
    gl_FragColor = v_Color;
}

The vertex shader transforms each vertex by the u_MVP matrix, which will be supplied by the Material class's draw function. The fragment shader simply passes through the color specified by the vertex shader.

VertexColorMaterial

Now, we're ready to implement our first material, the VertexColorMaterial class. Create a new Java class named VertexColorMaterial in the renderbox/materials/ directory. Define the class as extends Material:

public class VertexColorMaterial extends Material {

The methods we're going to implement are as follows:

  • VertexColorMaterial: These are constructors
  • setupProgram: This creates the shader program and gets its OpenGL variable locations
  • setBuffers: This sets the allocated buffer used in rendering
  • draw: This draws a model from a view perspective

Here's the complete code:

public class VertexColorMaterial extends Material {
    static int program = -1;

    static int positionParam;
    static int colorParam;
    static int modelParam;
    static int MVPParam;

    FloatBuffer vertexBuffer;
    FloatBuffer colorBuffer;
    int numIndices;

    public VertexColorMaterial(){
        super();
        setupProgram();
    }

    public static void setupProgram(){
        //Already setup?
		if (program != -1) return;
        //Create shader program
        program = createProgram(R.raw.vertex_color_vertex, R.raw.vertex_color_fragment);

        //Get vertex attribute parameters
        positionParam = GLES20.glGetAttribLocation(program, "a_Position");
        colorParam = GLES20.glGetAttribLocation(program, "a_Color");

        //Enable vertex attribute parameters
        GLES20.glEnableVertexAttribArray(positionParam);
        GLES20.glEnableVertexAttribArray(colorParam);

        //Shader-specific parameters
        modelParam = GLES20.glGetUniformLocation(program, "u_Model");
        MVPParam = GLES20.glGetUniformLocation(program, "u_MVP");

        RenderBox.checkGLError("Solid Color Lighting params");
    }

    public void setBuffers(FloatBuffer vertexBuffer,  FloatBuffer colorBuffer, int numIndices){
        this.vertexBuffer = vertexBuffer;
        this.colorBuffer = colorBuffer;
        this.numIndices = numIndices;
    }

    @Override
    public void draw(float[] view, float[] perspective) {
        Matrix.multiplyMM(modelView, 0, view, 0, RenderObject.model, 0);
        Matrix.multiplyMM(modelViewProjection, 0, perspective, 0, modelView, 0);

        GLES20.glUseProgram(program);

        // Set the Model in the shader, used to calculate lighting
        GLES20.glUniformMatrix4fv(modelParam, 1, false, RenderObject.model, 0);

        // Set the position of the cube
        GLES20.glVertexAttribPointer(positionParam, 3, GLES20.GL_FLOAT, false, 0, vertexBuffer);

        // Set the ModelViewProjection matrix in the shader.
        GLES20.glUniformMatrix4fv(MVPParam, 1, false, modelViewProjection, 0);

        // Set the normal positions of the cube, again for shading
        GLES20.glVertexAttribPointer(colorParam, 4, GLES20.GL_FLOAT, false, 0, colorBuffer);

        // Set the ModelViewProjection matrix in the shader.
        GLES20.glUniformMatrix4fv(MVPParam, 1, false, modelViewProjection, 0);

        GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, numIndices);
    }

    public static void destroy(){
        program = -1;
    }
}

The setupProgram method creates an OpenGL ES program for the two shaders that we created in res/raw/ directory—vertex_color_vertex and vertex_color_fragment. It then gets references to the positionParam, colorParam, and MVPParm shader variables using the GetAttribLocation and GetUniformLocation calls that provide memory locations within the shader program, which are used later for drawing.

The setBuffers method sets the memory buffers for vertices that define an object that will be drawn using this material. The method assumes that an object model consists of a set of 3D vertices (X, Y, and Z coordinates).

The draw() method renders the object specified in the buffers with a given set of model-view-perspective (MVP) transformation matrices. (Refer to the 3D camera, perspective, and head rotation section of Chapter 3, Cardboard Box, for detailed explanations.)

You may have noticed that we aren't using that ShortBuffer function mentioned earlier. Later on, materials will use the glDrawElements call along with an index buffer. glDrawArrays is essentially a degenerate form of glDrawElements, which assumes a sequential index buffer (that is, 0, 1, 2, 3, and so on). It is more efficient with complex models to reuse vertices between triangles, which necessitates an index buffer.

For completeness, we will also provide a destroy() method for each of the Material classes. We will come to know exactly why the material must be destroyed a little later.

As you can see, Material encapsulates much of the lower level OpenGL ES 2.0 calls to compile the shader script, create a render program, set the model-view-perspective matrices in the shader, and draw the 3D graphic elements.

We can now implement the Camera component.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset