Wednesday, June 1, 2011

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:         //let's multiply projection and view matrices
   4:         Matrix.multiplyMM(m_fVPMatrix, 0, m_fProjMatrix, 0, m_fViewMatrix, 0);
   5:         
   6:         GLES20.glUseProgram(iProgId);
   7:         cubeBuffer.position(0);
   8:         GLES20.glVertexAttribPointer(iPosition, 3, GLES20.GL_FLOAT, false, 0, cubeBuffer);
   9:         GLES20.glEnableVertexAttribArray(iPosition);
  10:         
  11:         colorBuffer.position(0);
  12:         GLES20.glVertexAttribPointer(iColor, 3, GLES20.GL_FLOAT, false, 0, colorBuffer);
  13:         GLES20.glEnableVertexAttribArray(iColor);
  14:         
  15:         Matrix.setIdentityM(m_fModelM, 0);
  16:         Matrix.rotateM(m_fModelM, 0, xAngle, 1, 0, 0);
  17:         Matrix.rotateM(m_fModelM, 0, -yAngle, 0, 1, 0);
  18:         //multiply model matrix with view-projection matrix
  19:         Matrix.multiplyMM(m_fMVPMatrix, 0, m_fVPMatrix, 0, m_fModelM, 0);
  20:         
  21:         GLES20.glUniformMatrix4fv(iVPMatrix, 1, false, m_fMVPMatrix, 0);

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 first multiply view matrix and projection matrix.


we multiply the view-projection matrix (m_fVPMatrix) with our rotated model matrix and the result will be stored in model-view-projection m_fMVPMatrix.


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.