Simple Directional Lighting

I have combined per-vertex and per-pixel lighting effects in one program, so that we can have good look at what is the difference in output. This lighting is very basic and does not involve shadows and specular lighting.

Output looks like

Menu

Per-Vertex

Per-Pixel

if observe in above screens, per-pixel shading is much smoother than per-vertex.

Directional Lighting, illuminates all objects equally from a given direction, like an area light of infinite size and infinite distance from the scene; there is shading, but cannot be any distance falloff

For detailed theory, you can refer here and here which offers great theoretical part of lightings. I have nothing new to explain.

In this post I’ll try to show coding difference between per-vertex and per-pixel shading.

First we’ll look at shader code and then remaining rendering part

Vertex Shader


Per-Vertex

Per-Pixel

String strVShaderPV = 
"attribute vec4 a_position;" +
"attribute vec3 a_normals;" +
"attribute vec2 a_texCoords;" +
"uniform mat4 u_ModelViewMatrix;" +
"uniform mat3 u_MVNormalsMatrix;" +
"uniform vec3 u_LightDir;" +
"uniform vec3 u_LightColor;" +
"varying vec3 v_colorWeight;" +
"varying vec2 v_texCoords;" +
"void main()" +
"{" +
       "gl_Position = u_ModelViewMatrix * a_position;" +
      "v_texCoords = a_texCoords;" +
	"vec3 normal = normalize(u_MVNormalsMatrix * a_normals);" +
	"vec3 lightNorm = normalize(u_LightDir);" +
	"float lightWeight = max(dot(normal,lightNorm),0.0);" +
	"v_colorWeight = vec3(0.2,0.2,0.2) + (lightWeight * u_LightColor);" +
"}";
String strVShaderPP =
 "attribute vec4 a_position;" +
"attribute vec3 a_normals;" +
"attribute vec2 a_texCoords;" +
"uniform mat4 u_ModelViewMatrix;" +
"uniform mat3 u_MVNormalsMatrix;" +
"varying vec3 u_Normals;" +
"varying vec2 v_texCoords;" +
"void main()" +
"{" +
	"v_texCoords = a_texCoords;" +
	"u_Normals = u_MVNormalsMatrix * a_normals;" +
	"gl_Position = u_ModelViewMatrix * a_position;" +
"}";

In Per-Vertex approach we are calculating color weight based in the object normals and light normal. but in Per-Pixel approach we are transforming normals and passing to Fragment shader. we need to transform the normals as we are transforming position vertices co-ordinates.

For vertex shader, we are taking vertex normals of Sphere along with vertices as input. As usual passing model-view matrix for calculating object vertex position with respective to current projection.

We also need normals for model view matrix for transforming vertex normals, which we will use for calculating lighting brightness. I’ll explain how to calculate this normals later in below sections.

We need two more variable for lighting parameters such as light direction, light color. I assumed ambience color as (r:0.2, g:0,2, b:0,2) for calculations.

A varying variable, v_colorWeight, for passing the calculated result to fragment shader.

Calculations are very simple, we are here transforming normals and calculating the light color weight for modifying objects’s color.

Fragment Shader

Per-Vertex

Per-Pixel

String strFShaderPV = 
"precision mediump float;" +
"varying vec3 v_colorWeight;" +
"varying vec2 v_texCoords;" +
"uniform sampler2D u_texId;" +
"void main()" +
"{" +
	"vec4 texColor = texture2D(u_texId, v_texCoords);" +
	"gl_FragColor = vec4(texColor.rgb * v_colorWeight, texColor.a);" +
"}";
String strFShaderPP =
 "precision mediump float;" +
"uniform vec3 u_LightDir;" +
"uniform vec3 u_LightColor;" +
"uniform sampler2D u_texId;" +
"varying vec2 v_texCoords;" +
"varying vec3 u_Normals;" +
"void main()" +
"{" +
	"vec3 LNorm = normalize(u_LightDir);" +
	"vec3 normal = normalize(u_Normals);" +
	"float intensity = max(dot(LNorm, normal),0.0);" +
	"vec4 texColor = texture2D(u_texId, v_texCoords);" +
	"vec3 calcColor = vec3(0.2,0.2,0.2) + u_LightColor * intensity;" +
	"gl_FragColor = vec4(texColor.rgb * calcColor, texColor.a);" +
"}";

in Per-Vertex approach we are combining calculated color weight from vertex shader. but in Per-Pixel approach we taking the normals and performing all the calculations in Fragment Shader.

Let’s look at remaining code part.

Two new classes I have created in common package for Mat3 for 3X3 matrix calculation and Mesh class for creating sphere and cube (as of now, will add loading 3DS, OBJ and other file support on the go).

Renderer

Mesh sphere;
Mat3 normalMat;	
public LightRenderer(ES2SurfaceView view) 
{
	sphere = new Mesh();
	sphere.Sphere(4, 10);
	curView = view;
	normalMat = new Mat3();
	cubeBuffer = sphere.getVertexBuffer();
	normalsBuffer = sphere.getNormalsBuffer();
	indexBuffer = sphere.getIndecesBuffer();
	texBuffer = sphere.getTextureBuffer();
}

in constructor of renderer we are creating a sphere and getting vertex, texture coordinates and index buffers for local reference.

public void onDrawFrame(GL10 gl) {
	GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
	GLES20.glUseProgram(iProgId);
		
	cubeBuffer.position(0);
	GLES20.glVertexAttribPointer(iPosition, 3, GLES20.GL_FLOAT, false, 0, cubeBuffer);
	GLES20.glEnableVertexAttribArray(iPosition);
		
	texBuffer.position(0);
	GLES20.glVertexAttribPointer(iTexCoords, 2, GLES20.GL_FLOAT, false, 0,texBuffer);
	GLES20.glEnableVertexAttribArray(iTexCoords);
		
	normalsBuffer.position(0);
	GLES20.glVertexAttribPointer(iNormals, 3, GLES20.GL_FLOAT, false, 0, normalsBuffer);
	GLES20.glEnableVertexAttribArray(iNormals);
		
	GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
	GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, iTexId);
	GLES20.glUniform1i(iTexLoc, 0);
		
	GLES20.glUniform3fv(iLightColor, 1, m_fLightColor, 0);
	GLES20.glUniform3fv(iLightDirection, 1, m_fLightDir, 0);
		
		
	Matrix.setIdentityM(m_fIdentity, 0);
	Matrix.rotateM(m_fIdentity, 0, -xAngle, 0, 1, 0);
	Matrix.rotateM(m_fIdentity, 0, -yAngle, 1, 0, 0);
		
	Matrix.multiplyMM(m_fVPMatrix, 0, m_fViewMatrix, 0, m_fIdentity, 0);
	Matrix.multiplyMM(m_fVPMatrix, 0, m_fProjMatrix, 0, m_fVPMatrix, 0);
	
	normalMat.SetFrom4X4(m_fVPMatrix);
	normalMat.invert();
	normalMat.transpose();
	GLES20.glUniformMatrix3fv(iVNormMat, 1, false, normalMat.values, 0);
		
	GLES20.glUniformMatrix4fv(iVPMatrix, 1, false, m_fVPMatrix, 0);
	
	GLES20.glDrawElements(GLES20.GL_TRIANGLES, sphere.m_nIndeces, GLES20.GL_UNSIGNED_SHORT, indexBuffer);

}

highlighted code is the only difference part compared to previous posts. which creates a 3X3 normals matrix for Model View projection matrix, which is used in shaders for transforming sphere normals.

Normals are calculated for top- left 3X3 matrix.

SetFrom4X4 function will copy 3X3 matrix from passed 4X4 matrix

Inverse this 3X3 matrix and then transpose will give us normals matrix.

If you like to know how to mathematically calculate, refer here.

A Little bit of Android, Creating Options Menu

There are two ways of loading menus. One from coding and other from resource.

Here I have done in code, as we have only two items for menu

We have to override two methods in activity.

  • public boolean onCreateOptionsMenu(Menu menu)
  • public boolean onOptionsItemSelected(MenuItem item)

onCreateOptionMenu method will be called when menu button is pressed on phone. We will create menu in this function.

public boolean onCreateOptionsMenu(Menu menu) {
     menu.add(Menu.NONE, 0, Menu.NONE, "Per Vertex");
     menu.add(Menu.NONE, 1, Menu.NONE, "Per Pixel");
      return super.onCreateOptionsMenu(menu);

    }

With this we create menu with two items “Per-Pixel” and “Per-Vertex” with ids 0 and 1 respectively.

To handle menu event we have to implement onOptionsItemSelected method.

public boolean onOptionsItemSelected(MenuItem item) {
    
     if (item.getItemId() == 0)
     {
      view.LoadProgram(item.getItemId());
     } else if (item.getItemId() == 1)
     {
      view.LoadProgram(item.getItemId());
     } else {
      return super.onOptionsItemSelected(item);
     }
     return true;
    }

In this method I’m calling view’s LoadProgram function to switch between Per-Vertex and Per-Pixel shader programs.

That’s it in this post. next come point lights.

leave a comment if you have any thing to say Smile

Multi-Texturing

Multi-Texturing is nothing but mixing multiple texture as per your requirement. here I’ll mix these two textures to get the resulting screen below. its up to coder to mix textures to get desired effect.

Texture1:

Texture2:

and mixing result:

Let’s look at the code below. First we will look at shaders because major change is in shaders only.

Vertex Shader:

String strVShader =
"attribute vec4 a_position;" +
"attribute vec2 a_texCoords;" +
"varying vec2 v_texCoords;" +
"void main()" +
"{" +
	"v_texCoords = a_texCoords;" +
	"gl_Position = a_position;" +
"}";

Nothing fancy here, simple shader that passes texture co-ordinates to Fragment shader and sets vertex position.

Fragment shader:

This is where we will make few changes to mix/blend textures.
String strFShader =
"precision mediump float;" +
"varying vec2 v_texCoords;" +
"uniform sampler2D u_texId1;" +
"uniform sampler2D u_texId2;" +
"void main()" +
"{" +
        "vec4 color1 = texture2D(u_texId1, v_texCoords);" +
        "vec4 color2 = texture2D(u_texId2, v_texCoords);" +
        "gl_FragColor = color2 * color1;" +
"}";

Here, get the color values from the texture at the co-ordinates and manipulate them as you like. here i just multiplied 2 color vectors.

There is no much changes in remaining renderer code. but, we will see draw function.

public void onDrawFrame(GL10 gl) {
		GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
		GLES20.glUseProgram(iProgId);

		GLES20.glVertexAttribPointer(iPosition, 3, GLES20.GL_FLOAT, false, 0, vertexBuffer);
		GLES20.glEnableVertexAttribArray(iPosition);

		GLES20.glVertexAttribPointer(iTexCoords1, 2, GLES20.GL_FLOAT, false, 0, texBuffer);
		GLES20.glEnableVertexAttribArray(iTexCoords1);

		GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
		GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, iTexIds[0]);
		GLES20.glUniform1i(iTex1, 0);

		GLES20.glActiveTexture(GLES20.GL_TEXTURE1);
		GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, iTexIds[1]);
		GLES20.glUniform1i(iTex2, 1);

//		GLES20.glDrawArrays(GLES20.GL_TRIANGLE_FAN, 0, 4);
		GLES20.glDrawElements(GLES20.GL_TRIANGLES, 6, GLES20.GL_UNSIGNED_SHORT, indecesBuffer);

	}

Apart from passing vertex positions and texture co-ordinates, we have activated 2 textures (GL_TEXTURE0 and GL_TEXTURE1) for passing texture ids to shaders. we can go upto 32 textures. and as usual draw triangles or triangle fan to draw quad.

Texturing Cube: Different Textures on each face

Today after lot of struggle for at least for a  day, i could able to get cube Map running, here it is how it looks

Here is the procedure for achieving cube map. firstly, we have to create a cube map with series of texture2D maps.

public int CreateCubeTexture()
{
    ByteBuffer fcbuffer = null;

    int[] cubeTex = new int[1];

            GLES20.glGenTextures(1, cubeTex, 0);
            GLES20.glBindTexture(GLES20.GL_TEXTURE_CUBE_MAP,cubeTex[0]);

            Bitmap img = null;
            img = BitmapFactory.decodeResource(curView.getResources(), R.raw.brick1);
            fcbuffer = ByteBuffer.allocateDirect(img.getHeight() * img.getWidth() * 4);

            img.copyPixelsToBuffer(fcbuffer);
            fcbuffer.position(0);
            Log.d("alpha",""+img.hasAlpha());
            GLES20.glTexImage2D(GLES20.GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0, GLES20.GL_RGBA,
              img.getWidth(),img.getHeight() , 0,GLES20.GL_RGBA ,GLES20.GL_UNSIGNED_BYTE, fcbuffer);
            fcbuffer = null;
            img.recycle();

            img = BitmapFactory.decodeResource(curView.getResources(), R.raw.brick2);
            fcbuffer = ByteBuffer.allocateDirect(img.getHeight() * img.getWidth() * 4);
            img.copyPixelsToBuffer(fcbuffer);
            fcbuffer.position(0);
            GLES20.glTexImage2D(GLES20.GL_TEXTURE_CUBE_MAP_NEGATIVE_X, 0, GLES20.GL_RGBA,
               img.getWidth(),img.getHeight(), 0,GLES20.GL_RGBA ,GLES20.GL_UNSIGNED_BYTE, fcbuffer);
            fcbuffer = null;
            img.recycle();

            img = BitmapFactory.decodeResource(curView.getResources(), R.raw.brick3);
            fcbuffer = ByteBuffer.allocateDirect(img.getHeight() * img.getWidth() * 4);
            img.copyPixelsToBuffer(fcbuffer);
            fcbuffer.position(0);
            GLES20.glTexImage2D(GLES20.GL_TEXTURE_CUBE_MAP_POSITIVE_Y, 0, GLES20.GL_RGBA,
              img.getWidth(),img.getHeight(), 0,GLES20.GL_RGBA ,GLES20.GL_UNSIGNED_BYTE, fcbuffer);
            fcbuffer = null;
            img.recycle();


            img = BitmapFactory.decodeResource(curView.getResources(), R.raw.brick4);
            fcbuffer = ByteBuffer.allocateDirect(img.getHeight() * img.getWidth() * 4);
            img.copyPixelsToBuffer(fcbuffer);
            fcbuffer.position(0);
            GLES20.glTexImage2D(GLES20.GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, 0, GLES20.GL_RGBA,
               img.getWidth(),img.getHeight(), 0,GLES20.GL_RGBA ,GLES20.GL_UNSIGNED_BYTE, fcbuffer);
            fcbuffer = null;
            img.recycle();

            img = BitmapFactory.decodeResource(curView.getResources(), R.raw.brick5);
            fcbuffer = ByteBuffer.allocateDirect(img.getHeight() * img.getWidth() * 4);
            img.copyPixelsToBuffer(fcbuffer);
            fcbuffer.position(0);
            GLES20.glTexImage2D(GLES20.GL_TEXTURE_CUBE_MAP_POSITIVE_Z, 0, GLES20.GL_RGBA,
                img.getWidth(),img.getHeight(), 0,GLES20.GL_RGBA ,GLES20.GL_UNSIGNED_BYTE, fcbuffer);
            fcbuffer = null;
            img.recycle();

            img = BitmapFactory.decodeResource(curView.getResources(), R.raw.brick6);
            fcbuffer = ByteBuffer.allocateDirect(img.getHeight() * img.getWidth() * 4);
            img.copyPixelsToBuffer(fcbuffer);
            fcbuffer.position(0);
            GLES20.glTexImage2D(GLES20.GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, GLES20.GL_RGBA,
               img.getWidth(),img.getHeight(), 0,GLES20.GL_RGBA ,GLES20.GL_UNSIGNED_BYTE, fcbuffer);
            fcbuffer = null;
            img.recycle();

            GLES20.glGenerateMipmap(GLES20.GL_TEXTURE_CUBE_MAP);
            GLES20.glTexParameteri(GLES20.GL_TEXTURE_CUBE_MAP,
                               GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST);
            GLES20.glTexParameteri(GLES20.GL_TEXTURE_CUBE_MAP,
                               GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_NEAREST);
            GLES20.glTexParameteri(GLES20.GL_TEXTURE_CUBE_MAP,
                               GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
            GLES20.glTexParameteri(GLES20.GL_TEXTURE_CUBE_MAP,
                               GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
            return cubeTex[0];
    }

in the above function, we have created a texture with glGenTexture and bind it to GL_TEXTURE_CUBE_MAP to tell opengl that this is a cube map.

next, I’m loading image from resource with Bitmap.

we have to define texture for each side/face of cube. that we can do with series of 2D Textures. Since we are using texture2D, we have to mention which side this texture belongs to. that can be done by specifying GL_TEXTURE_CUBE_MAP_POSITIVE_X,  GL_TEXTURE_CUBE_MAP_NEGATIVE_X, GL_TEXTURE_CUBE_MAP_POSITIVE_Y, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, GL_TEXTURE_CUBE_MAP_POSITIVE_Z and GL_TEXTURE_CUBE_MAP_NEGATIVE_Z.

Now Let’s look at shaders.

Vertex Shader

String strVShader = "attribute vec4 a_position;" +
        "uniform mat4 u_VPMatrix;" +
        "varying vec3 v_texCoords;" +
        "attribute vec3 a_texCoords;" +
        "void main()" +
        "{" +
// "v_texCoords = a_position.xyz;" +
          "v_texCoords = a_texCoords;" +
          "gl_Position = u_VPMatrix * a_position;" +
        "}";

Nothing great in vertex shader, we are just assigning texture co-ordinates to varying variable for using in fragment shader. and as usual calculating vertex position by multiplying view projection matrix with vertex position.

Fragment Shader

String strFShader = "precision mediump float;" +
        "uniform samplerCube u_texId;" +
        "varying vec3 v_texCoords;" +
        "void main()" +
        "{" +
          "gl_FragColor = textureCube(u_texId, v_texCoords);" +
        "}";

Here comes the main part in Fragment  shader, till now we declared  sampler2D variable for texture handle, but here we have declared new type  samplerCube. so that we can use cube map that we have created for texturing our cube.

There is one more important change in Fragment shader i.e., textureCube function. texturecube function expects 3D texture co-ordinates for locating fragment color in cube map.

Finally Let’s take look at drawing functionality also

public void onDrawFrame(GL10 arg0) {
    GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
    GLES20.glUseProgram(iProgId);

    cubeBuffer.position(0);
    GLES20.glVertexAttribPointer(iPosition, 3, GLES20.GL_FLOAT, false, 0, cubeBuffer);
    GLES20.glEnableVertexAttribArray(iPosition);

    texBuffer.position(0);
    GLES20.glVertexAttribPointer(iTexCoords, 3, GLES20.GL_FLOAT, false, 0, texBuffer);
    GLES20.glEnableVertexAttribArray(iTexCoords);

    GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
    GLES20.glBindTexture(GLES20.GL_TEXTURE_CUBE_MAP, iTexId);
    GLES20.glUniform1i(iTexLoc, 0);

    Matrix.setIdentityM(m_fIdentity, 0);
    Matrix.rotateM(m_fIdentity, 0, -xAngle, 0, 1, 0);
    Matrix.rotateM(m_fIdentity, 0, -yAngle, 1, 0, 0);
    Matrix.multiplyMM(m_fVPMatrix, 0, m_fViewMatrix, 0, m_fIdentity, 0);
    Matrix.multiplyMM(m_fVPMatrix, 0, m_fProjMatrix, 0, m_fVPMatrix, 0);
    GLES20.glUniformMatrix4fv(iVPMatrix, 1, false, m_fVPMatrix, 0);

    GLES20.glDrawElements(GLES20.GL_TRIANGLES, 36, GLES20.GL_UNSIGNED_SHORT, indexBuffer);
  }

Just have a look at the highlighted code above and you will be able to figure out what is change compared to previous post. Yes, we are binding the texture as cube map. that’s the magic word here to get it running.

Texturing Cube : Applying (Same) Texture on All Faces of Cube

I became over-enthusiastic and tried to leap too far before covering my ground, soon i found myself facing ground.

I tried to go for cube map directly before applying texture with texture2D on cube. then i realised my mistake and here we are applying same texture to cube faces.

Screen shot:

there are changes in cube definition compared to previous post. Now, all the face are drawn Counter Clock Wise and back face culling. following is the cube definition.

  1: float[] cube = {
  2:     2,2,2, -2,2,2, -2,-2,2, 2,-2,2, //0-1-2-3 front
  3:     2,2,2, 2,-2,2,  2,-2,-2, 2,2,-2,//0-3-4-5 right
  4:     2,-2,-2, -2,-2,-2, -2,2,-2, 2,2,-2,//4-7-6-5 back
  5:     -2,2,2, -2,2,-2, -2,-2,-2, -2,-2,2,//1-6-7-2 left
  6:     2,2,2, 2,2,-2, -2,2,-2, -2,2,2, //top
  7:     2,-2,2, -2,-2,2, -2,-2,-2, 2,-2,-2,//bottom
  8:   };

here we are defining all the faces of cube, and we use indexes to define the triangles for drawing. and texture co-ordinates for each face.

  1: short[] indeces = {
  2:       0,1,2, 0,2,3,
  3:       4,5,6, 4,6,7,
  4:       8,9,10, 8,10,11,
  5:       12,13,14, 12,14,15,
  6:       16,17,18, 16,18,19,
  7:       20,21,22, 20,22,23,
  8:
  9:       };
 10:
 11:   float[] tex = {
 12:       1,0, 0,0, 0,1, 1,1,
 13:       0,0, 0,1, 1,1, 1,0,
 14:       1,1, 0,1, 0,0, 1,0,
 15:       0,0, 1,0, 1,1, 0,1,
 16:       0,1, 0,0, 1,0, 1,1,
 17:       0,0, 1,0, 1,1, 0,1,
 18:
 19:       };

In constructor, added code to load texture co-ordinates into a buffer.

  1: public ViewPortRenderer(ES2SurfaceView view)
  2:   {
  3:     curView = view;
  4:     cubeBuffer = ByteBuffer.allocateDirect(cube.length * 4).order(ByteOrder.nativeOrder()).asFloatBuffer();
  5:     cubeBuffer.put(cube).position(0);
  6:
  7:     colorBuffer = ByteBuffer.allocateDirect(colors.length * 4).order(ByteOrder.nativeOrder()).asFloatBuffer();
  8:     colorBuffer.put(colors).position(0);
  9:
 10:     indexBuffer = ByteBuffer.allocateDirect(indeces.length * 4).order(ByteOrder.nativeOrder()).asShortBuffer();
 11:     indexBuffer.put(indeces).position(0);
 12:
 13:     texBuffer = ByteBuffer.allocateDirect(tex.length * 4).order(ByteOrder.nativeOrder()).asFloatBuffer();
 14:     texBuffer.put(tex).position(0);
 15:   }

Let’s have a look at changes in shaders.

Vertex Shader:

  1: String strVShader = "attribute vec4 a_position;" +
  2:         "uniform mat4 u_VPMatrix;" +
  3:         "attribute vec2 a_texCoords;" +
  4:         "varying vec2 v_texCoords;" +
  5:         "void main()" +
  6:         "{" +
  7:           "v_texCoords = a_texCoords;" +
  8:           "gl_Position = u_VPMatrix * a_position;" +
  9:         "}";

instead of color we are taking texture co-ordinates.

Fragment Shader:

  1: String strFShader = "precision mediump float;" +
  2:         "varying vec4 v_color;" +
  3:         "uniform sampler2D u_texId;" +
  4:         "varying vec2 v_texCoords;" +
  5:         "void main()" +
  6:         "{" +
  7:           "gl_FragColor = texture2D(u_texId, v_texCoords);" +
  8:         "}";

here we took texture in a sampler2D, since we are loading texture as GL_TEXTURE_2D.

texture2D function gives the fragment color at texture position in loaded texture.

changes in onSurfaceCreated

  1: GLES20.glFrontFace(GLES20.GL_CCW);
  2: GLES20.glEnable(GLES20.GL_CULL_FACE);
  3: GLES20.glCullFace(GLES20.GL_BACK);

here we telling opengl that all the triangles are drawn in counter clock wise and enable cull face.

Changes in onDrawFrame

  1: texBuffer.position(0);
  2: GLES20.glVertexAttribPointer(iTexCoords, 2, GLES20.GL_FLOAT, false, 0, texBuffer);
  3: GLES20.glEnableVertexAttribArray(iTexCoords);
  4:
  5: GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
  6: GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, iTexId);
  7: GLES20.glUniform1i(iTexLoc, 0);

Just here we are activating texture0 and binding the loaded texture to it. passing the texture index to shader with glUniform1i.

Setting Projection (Perspective Projection)

Till now we did not set view port. today, we will setup Perspective projection.

There are few changes in ES 2.0 compared to ES 1.1 like there is no Matrix Stock and co-ordinate transformation. i.e., we no longer have function like glMatrixMode, glRotate, glTranslate.

Hey, No worries. android is providing a class Matrix(android.opengl.Matrix) to do these missing functionalities like setting projection, rotating, translating, scaling etc., in ES 2.0.

Screen Shot of we are about to do:

New APIs that we are going to see are

  1. Matrix.setIdentityM
  2. Matrix.rotateM
  3. Matrix.multiplyMM
  4. Matrix.setLookAtM
  5. Matrix.frustumM

Lets look into code,

//cube co-ordinated
float[] cube = {
  -2, -2, -2,2, -2, -2,
         2,  2, -2,-2, 2, -2,
        -2, -2,  2,2, -2,  2,
         2,  2,  2,-2,  2,  2
  };
  //colors for each vertices
  float[] colors = {1,0,0, 0,1,0, 0,0,1, 1,1,0,
                    1,0,1, 0,1,1, 1,1,1, 0,0,0
  };
  //indeces for drawing the vertices in specified order
  short[] indeces = {
      0, 4, 5,0, 5, 1,
            1, 5, 6,1, 6, 2,
            2, 6, 7,2, 7, 3,
            3, 7, 4, 3, 4, 0,
            4, 7, 6,4, 6, 5,
            3, 0, 1,3, 1, 2};

  FloatBuffer cubeBuffer = null;
  FloatBuffer colorBuffer = null;
  ShortBuffer indexBuffer = null;

we have declared vertice, colors and indeces for drawing cube.

Vertex Shader:

String strVShader = "attribute vec4 a_position;" +
        "attribute vec4 a_color;" +
        "uniform mat4 u_VPMatrix;" +
        "varying vec4 v_color;" +
        "void main()" +
        "{" +
          "v_color = a_color;" +
          "gl_Position = u_VPMatrix * a_position;" +
        "}";

we have added new mat4 variable which holds the view port projection matrix. with viewport projection matrix we can determine the position of vertex.

Fragment Shader:

String strFShader = "precision mediump float;" +
        "varying vec4 v_color;" +
        "void main()" +
        "{" +
          "gl_FragColor = v_color;" +
        "}";

Fragment shader has nothing greate, it just sets the color.

public void onSurfaceCreated(GL10 arg0, EGLConfig arg1) {
    GLES20.glClearColor(0, 0, 0, 1);
    GLES20.glEnable(GLES20.GL_DEPTH_TEST);
    GLES20.glDepthFunc(GLES20.GL_LEQUAL);

    Matrix.setLookAtM(m_fViewMatrix, 0, 0, 0, -5, 0, 0, 0, 0, 1, 0);
.......
........
}

Just we have enabled Depth test. but here is the important part i.e., Matrix.setLookAtM, you may remember in OpenGL we have glLookAt function. functionality of setLookAtM is same as glLookAt function. setLookAtM gives us matrix with view position.

public void onSurfaceChanged(GL10 arg0, int width, int height) {
    GLES20.glViewport(0, 0, width, height);
    Matrix.frustumM(m_fProjMatrix, 0, -2, 2, -2, 2, 1, 10);
  }

in onSurfaceChanged function apart from setting viewport size, this time we also specifying frustum of the viewport.

Now Comes the drawing part

  1: public void onDrawFrame(GL10 arg0) {
  2:     GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
  3:     GLES20.glUseProgram(iProgId);
  4:     cubeBuffer.position(0);
  5:     GLES20.glVertexAttribPointer(iPosition, 3, GLES20.GL_FLOAT, false, 0, cubeBuffer);
  6:     GLES20.glEnableVertexAttribArray(iPosition);
  7:
  8:     colorBuffer.position(0);
  9:     GLES20.glVertexAttribPointer(iColor, 3, GLES20.GL_FLOAT, false, 0, colorBuffer);
 10:     GLES20.glEnableVertexAttribArray(iColor);
 11:
 12:     Matrix.setIdentityM(m_fIdentity, 0);
 13:     Matrix.rotateM(m_fIdentity, 0, -xAngle, 0, 1, 0);
 14:     Matrix.rotateM(m_fIdentity, 0, -yAngle, 1, 0, 0);
 15:     Matrix.multiplyMM(m_fVPMatrix, 0, m_fViewMatrix, 0, m_fIdentity, 0);
 16:     Matrix.multiplyMM(m_fVPMatrix, 0, m_fProjMatrix, 0, m_fVPMatrix, 0);
 17:
 18:     GLES20.glUniformMatrix4fv(iVPMatrix, 1, false, m_fVPMatrix, 0);
 19:
 20:     GLES20.glDrawElements(GLES20.GL_TRIANGLES, 36, GLES20.GL_UNSIGNED_SHORT, indexBuffer);
 21:
 22:   }

same as we do in normal OpenGL drawing, like load identity matrix.

since i’m rotating cube with touch event, so the angles in x,y axis.

use rotateM function to rotate the identity matrix, here it is a in-place rotation of identity matrix.

we multiply the view matrix (m_fViewMatrix) with our rotated identity matrix and the result will be stored in m_fVPMatrix.

then we multiply the above result with projection matrix. this result we will pass to vertex shader where we use this to determine the position of vertex in the viewport with projection matrix.

Rotating Cube

for rotating cube with touch, we have to add onTouchEvent in view class.

  1: public boolean onTouchEvent(MotionEvent event)
  2:   {
  3:     if (event.getAction() == MotionEvent.ACTION_DOWN)
  4:     {
  5:       touchedX = event.getX();
  6:       touchedY = event.getY();
  7:     } else if (event.getAction() == MotionEvent.ACTION_MOVE)
  8:     {
  9:       renderer.xAngle += (touchedX - event.getX())/2f;
 10:       renderer.yAngle += (touchedY - event.getY())/2f;
 11:
 12:       touchedX = event.getX();
 13:       touchedY = event.getY();
 14:     }
 15:     return true;
 16:
 17:   }

what we are doing here, store X and Y co-ordinates when touched the screen. find the difference whenever there is a movement. use this difference as angle for rotating cube.

Particle System with Point Sprites–Part II

After struggle for a day (though it’s a simple logic), i got the desired particle effect…

Effect i was looking for is “Particles continuously flow from the centre”, this effect i could achieve without Shaders, but i wanted to do the same with Shaders.

code changes from the previous post are as below…

Changes in Draw function of ParticleManger class are highlighted below.

public void draw(int iPosition, int iMove, int iTimes, int iColor, int iLife, int iAge)
  {
    GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);

    vertexBuffer.position(0);
    GLES20.glVertexAttribPointer(iPosition, 3, GLES20.GL_FLOAT, false, PARTICLE_SIZE * 4, vertexBuffer);
    GLES20.glEnableVertexAttribArray(iPosition);

    vertexBuffer.position(3);
    GLES20.glVertexAttribPointer(iColor, 3, GLES20.GL_FLOAT, false, PARTICLE_SIZE * 4, vertexBuffer);
    GLES20.glEnableVertexAttribArray(iColor);

    vertexBuffer.position(6);
    GLES20.glVertexAttribPointer(iMove, 3, GLES20.GL_FLOAT, false, PARTICLE_SIZE * 4, vertexBuffer);
    GLES20.glEnableVertexAttribArray(iMove);

    vertexBuffer.position(9);
    GLES20.glVertexAttribPointer(iLife, 1, GLES20.GL_FLOAT, false, PARTICLE_SIZE * 4, vertexBuffer);
    GLES20.glEnableVertexAttribArray(iLife);

    vertexBuffer.position(10);
    GLES20.glVertexAttribPointer(iAge, 1, GLES20.GL_FLOAT, false, PARTICLE_SIZE * 4, vertexBuffer);
    GLES20.glEnableVertexAttribArray(iAge);

    GLES20.glUniform1f(iTimes, nTimeCounter);

    GLES20.glDrawArrays(GLES20.GL_POINTS, 0, NUM_PARTICLES);
  }

I’m passing life and age attribute values for each particle to Vertex Shader.

Changes in Vertex Shader are highlighted below (need to optimize interms of operation steps though).

String strVShader =
            "precision mediump float;" +
            "attribute vec4 a_Position;" +
            "attribute vec4 a_move;" +
            "uniform float a_time;" +
            "attribute vec4 a_color;" +
            "varying vec4 v_color;" +
            "attribute float a_life;" +
            "attribute float a_age;" +
            "varying float alpha;" +
            "float time;" +
            "void main()" +
            "{" +
                "alpha = a_life - (a_time * 10.0 * a_age);" +
                "time = a_time;" +
                "if (alpha < 0.0)" +
                "{" +
                    "float td = a_life/a_age;" +
                    "td /= 10.0;" +
                    "float df = a_time/td;" +
                    "int div = int(df);" +
                    "df = float(div);" +
                    "td *= df;" +
                    "time = a_time - td;" +
                    "alpha = a_life - (time * 10.0 * a_age);" +
                "}" +
                "gl_PointSize = 5.0;" +
                "v_color = a_color;" +
                "gl_Position = a_Position;" +
                "gl_Position += (time * a_move * 0.5);" +
                "gl_Position.w = 1.0;" +
            "}";

here calculation logic is as follows:

  1. if particle run out of life (i.e., alpha calculated is less than zero), then
  2. Calculate the time (td), when particle has possibly died.
  3. find the modulo of current time and the calculated time in above step. thats is stored in time variable.
  4. calculate the alpha value based on the new calculated time.
  5. use calculated time to set the vertex position.

Fragment Shader do not have any changes.

Code for this post can be found at Google Code

Particle System with Point Sprites

It’s been almost 10 days I stuck with particle system. Still I have few doubts/problems/queries. But still I’ll post my progress

I have asked my question regarding particle system updation on stackoverflow, waiting for answers J

Creating particle system with point sprites is very exciting for me.
Here is the screen shot:

Let’s dig in.
Code for this post can be found at Google Code


Particle Structure
First let’s look at properties of a Particle.
My particle structure as [x,y,z, r,g,b, dx,dy,dz, life,age]
Position Color     displacement
ParticleManager class
Setup function, for initiating the particle system with the position, random colors and displacement.
Draw function, is for drawing the particles, we here pass the particle related information to shaders
Update function, is called by Update Thread
Rendering
Vertex Shader
String strVShader =

            “attribute vec4 a_Position;” +
            “attribute vec4 a_move;” +
            “uniform float a_time;” +
            “attribute vec3 a_color;” +
            “varying vec3 v_color;” +
            “void main()” +
            “{“ +
                “v_color = a_color;” +
                “gl_PointSize = 10.0;” +
                “gl_Position = a_Position;”
                “gl_Position += (a_time * a_move * 0.5);” +
                “gl_Position.w = 1.0;” +
            “}”;

a_position represents position of each particle, a_move represents displacement/movement
a_color represents color of a particle,
In this vertex shader, we are setting the position of each particle based on the time and the displacement values.

Fragment Shader
String strFShader =

            “precision mediump float;” +
            “uniform sampler2D u_texture;” +
            “varying vec3 v_color;” +
            “void main()” +
            “{“ +                
                “vec4 tex = texture2D(u_texture, gl_PointCoord);” +
                “gl_FragColor = vec4(v_color,0.5) * tex;” +
            “}”;

Fragment shader is not changed much, only I’m setting alpha to 0.5,
We have to take it from life and age value of particle, will do it later.

Drawing
Drawing part is very similar to previous posts. Not many changes here, you can have look in the code.

Todo:

  1. Add gravity and wind
  2. Change alpha value with particle life