Sunday, May 29, 2011

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


Saturday, May 28, 2011

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


Update::

More details in next post

 


 


 


 


 

Wednesday, May 18, 2011

Drawing and Texturing Triangle

Today I learned way to draw triangle and texture the same.

Screen Shot


As usual code is available in Google Code.

It is very easy to draw a triangle with Shaders.

First, let's look at the Vertex Shader

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

Here, I have declared 2 new variables:

a_texCoords, to get the texture co-ordinates while rendering

v_texCoords, is a varying variable to feed as a input variable for Fragment Shader for texture co-ordinates.

So I have just stored input, a_texCoords, to vertex shader's output, v_texCoords, variable.

Fragment Shader

String strFShader =
        "precision mediump float;" +
        "varying vec2 v_texCoords;" +
        "uniform sampler2D u_texId;" +
        "void main()" +
        "{" +
          "gl_FragColor = texture2D(u_texId, v_texCoords);" +
        "}";

Here, if you observe we have same, v_texCoords, variable is declared, which is a input from Vertex Shader.

And as in the previous post, we are taking the Fragment Color from the texture with texture2D function.

Now, let's go to drawing function.

OnDrawFrame function

First let's have a look at our vertex array declaration.

float[] fVertices =

{0,0.5f,0, 0,0,

-0.5f,-0.5f,0, 1,0,

0.5f,-0.5f,0, 1,1};

Values colored green are vertices for drawing triangle. And values in red are texture co-ordinates. We need not have separate arrays for vertices and texture co-ordinates.

Let's look at how to use this array for drawing triangle and texturing as well.

public void onDrawFrame(GL10 gl) {
    GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
    GLES20.glUseProgram(iProgId);
   
    vertexBuffer.position(0);
    GLES20.glVertexAttribPointer(iPosition, 3, GLES20.GL_FLOAT, false, 5 * 4, vertexBuffer);
    GLES20.glEnableVertexAttribArray(iPosition);
   
    vertexBuffer.position(3);
    GLES20.glVertexAttribPointer(iTexCoords, 2, GLES20.GL_FLOAT, false, 5* 4, vertexBuffer);
    GLES20.glEnableVertexAttribArray(iTexCoords);
   
    GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
    GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, iTexId);
    GLES20.glUniform1i(iTexLoc, 0);
   
    GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, 3);
  }

There are 2 distinct changes when compared to previous post,
We are setting current position to zero for vertices and 3 for texture coordinates, this way we telling Opengl, that vertices starts at zero with stride of 5 * 4 (number of byte to skip for next set of vertices) and texture co-ordinates start from position 3 with same stride. Finally glDrawArrays to draw triangle.

Tuesday, May 17, 2011

Applying Texture to Point Sprite

After successfully drawing Point on screen, I want to apply texture to point sprite.

Texture image is

As usual code can be downloaded from Google Code.

Changes made compared to previous post are.

  1. Vertex Shader : No Changes
  2. Fragment Shader :
    1. Added a uniform variable for texture id.
    2. Instead of defining Fragment color, will read from texture
  3. Rendering :
    1. Activate and Bind Texture
    2. Point uniform variable declared Fragment Shader

Remaining everything is same.
Let's look into changes made in detail

Fragment Shader

String strFShader =
                            "precision mediump float;" +
                            "uniform sampler2D u_baseMap;" +
                                "void main()" +
                                "{" +
                                        "vec4 color;" +
                                        "color = texture2D(u_baseMap, gl_PointCoord);" +
                                        "gl_FragColor = color;" +
                                "}";

u_baseMap variable for holding texture id, specified in OnDrawFrame

texture2D , a built-in function to fetch texture map.

OnDrawFrame

 public void onDrawFrame(GL10 gl) {
                GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
                GLES20.glUseProgram(iProgId);
                GLES20.glVertexAttribPointer(iPosition, 3, GLES20.GL_FLOAT, false, 0, vertBuffer);
               
                GLES20.glEnableVertexAttribArray(iPosition);
               
                GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
                GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texId);
               
                GLES20.glUniform1i(iBaseMap, 0);
               
                GLES20.glDrawArrays(GLES20.GL_POINTS, 0, 1);
        }

Yup, Only 3 lines of code, and first 2 lines of code is very familiar

Last line, GLES20.glUniform1i(iBaseMap, 0), is to load the uniform variable. which tells the shader to use the texture indexed 0.


Sunday, May 15, 2011

First Experience with OpenGL ES2 on Android


I know there are lot of people want to explore and learn OpenGL and Android, so do I.
I want to learn GLSL OpenGL Shaders. so I chose Android as a platform to explore and to learn OpenGL.
It is very simple example and very minimum code to start with ES2 on Android.
It will simply draw point sprite at center of the screen
I'm glad that I could find lot of examples on net to startup with ES2.

Code for this article can be found at Google Code
Basic Steps I have followed:
  1. Create Android project.
  2. Create and Setup GLSurfaceView object.
  3. Create and compile shaders
  4. Draw frame.
I'll skip the first step and going to 2nd step.
Modify OnCreate function in Activity that we have created in First Step.
 public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        if (detectOpenGLES20()){
          Log.d("GLES20", "Supported");
        } else {
          Log.d("GLES20", "Not Supported");
        }
        view = new GLSurfaceView(this);
        view.setEGLContextClientVersion(2);
        view.setRenderer(new PointRenderer(this));
        setContentView(view);
    }
    private boolean detectOpenGLES20() {
        ActivityManager am =
            (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
        ConfigurationInfo info = am.getDeviceConfigurationInfo();
        return (info.reqGlEsVersion >= 0x20000);
    }

We are making the window to full screen and removing the title in first 2 lines. and in the next step we are trying to detect whether device supports OpenGL ES 2.0. just here i’m showing log message, this can be used to change the renderer or exit the program if running on non-supported device.
Next, create GLSurfaceView and set the EGL context to 2 (since we are using GL ES 2.0). Set the renderer object, which performs rendering of our drawing.
Finally, set our GLSurfaceView object as content view for this activity.
setEGLContextClientVersion call is VERY important as it will set the GL Context version to 2.
Now, let’s have a look at the Renderer class, PointRenderer.
when Renderer class is added, we are given with 3 override function to add our code
public void onDrawFrame(GL10 gl)
public void onSurfaceChanged(GL10 gl, int width, int height)
public void onSurfaceCreated(GL10 gl, EGLConfig config)
onDrawFrame is the function where we put our rendering code. onSurfaceChanged is to handle what to do when surface is changed and onSurfaceCreated is to define what should happen when surface is created initially. we will look into these functions in detail later.

Writing First Shader

Shader is small C like program (not much difference i found compared to C program). Two types Shaders are available in Open GL ES 2.0, namely Vertex Shader and Fragment Shader.
Simple Vertex shader is below
String strVShader =
                 "attribute vec4 a_position;\n"+
      "void main()\n" +
      "{\n" +
          "gl_PointSize = 45.0;\n" +
          "gl_Position = a_position;\n"+                 
      "}";
in this vertex shader, just setting the point size and the position of the vertex, where to place this point on display. gl_PointSize and gl_Position are built-in variables
Simple Fragment Shader:
String strFShader = "precision mediump float;" +
        "void main() " +
        "{" +
            "gl_FragColor = vec4(1,0,0,1);" +
        "}";
As you can see here, we are just setting the Fragment Color.
That’s all it is. out fragment and vertex shaders are done.
now we have to load them and put into use.

Compiling And Loading shaders

if you have downloaded code, then you can check Utils.java, which has functions to load shaders and create programs.

Loading Shader

Steps involved in loading a shader.
  1. Create a shader with glCreateShader function with shader type (Vertex Shader or Fragment Shader).
  2. Specify the shader code with glShaderSource.
  3. Compile the shader with glCompileShader.
  4. Find out the status of compilation with glGetShaderiv
Following is the code for above steps
//strSource, shader source
//iType, type of shader we are trying to load, 
//Vertex Shader or Fragment shader
public static int LoadShader(String strSource, int iType)
  {
    int[] compiled = new int[1];
    int iShader = GLES20.glCreateShader(iType);
    GLES20.glShaderSource(iShader, strSource);
    GLES20.glCompileShader(iShader);
    GLES20.glGetShaderiv(iShader, GLES20.GL_COMPILE_STATUS, compiled, 0);
    if (compiled[0] == 0)
    {     
      Log.d("Load Shader Failed", "Compilation\n"+GLES20.glGetShaderInfoLog(iShader));
      return 0;
    }
    return iShader;
  }
Creating Program
Steps involved as follows:
  1. Create a program to use shaders with glCreateProgram.
  2. Load shaders.
  3. Attach shader to program with glAttachShader.
  4. And then link the program with glLinkProgram
  5. To know the status of linking use glGetProgramiv
below is the code for above steps
public static int LoadProgram(String strVSource, String strFSource)
  {
    int iVShader;
    int iFShader;
    int iProgId;
    int[] link = new int[1];
   
    iVShader = LoadShader(strVSource, GLES20.GL_VERTEX_SHADER);
    if (iVShader == 0)
    {
      Log.d("Load Program", "Vertex Shader Failed");
      return 0;
    }
    iFShader = LoadShader(strFSource, GLES20.GL_FRAGMENT_SHADER);
    if(iFShader == 0)
    {
      Log.d("Load Program", "Fragment Shader Failed");
      return 0;
    }
   
    iProgId = GLES20.glCreateProgram();
    //attache shaders to program
    GLES20.glAttachShader(iProgId, iVShader);
    GLES20.glAttachShader(iProgId, iFShader);
    //link program
    GLES20.glLinkProgram(iProgId);
    //get the link status
    GLES20.glGetProgramiv(iProgId, GLES20.GL_LINK_STATUS, link, 0);
    if (link[0] <= 0)
    {
      Log.d("Load Program", "Linking Failed");
      return 0;
    }
//delete the shaders, since we have already loaded 
    GLES20.glDeleteShader(iVShader);
    GLES20.glDeleteShader(iFShader);
    return iProgId;
  }

Let's dig into Renderer code:

first lets check onSurfaceCreated function
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
    GLES20.glClearColor(0, 0, 0, 1);
    String strVShader =   
                        "attribute vec4 a_position;\n"+
              "void main()\n" +
              "{\n" +
                  "gl_PointSize = 45.0;\n" +
                  "gl_Position = a_position;\n"+                 
              "}";
    String strFShader = "precision mediump float;" +
        "void main() " +
        "{" +
            "gl_FragColor = vec4(1,0,0,1);" +
        "}";
    iProgId = Utils.LoadProgram(strVShader, strFShader);
    iPosition = GLES20.glGetAttribLocation(iProgId, "a_position");
  }
load the shaders when the surface is created and in the last line, as you can see we are loading the attribute variable defined in the vertex shader. with this position variable we are going to specify the vertex position to draw.
onSurfaceChanged
  public void onSurfaceChanged(GL10 gl, int width, int height) {
    GLES20.glViewport(0, 0, width, height);
  }
Only setting the view port size.i’m not setting any projection view (this we will do it in later developments).
now comes the important function onDrawFrame.
public void onDrawFrame(GL10 gl) {

    GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
    GLES20.glUseProgram(iProgId);
    GLES20.glVertexAttribPointer(iPosition, 3, GLES20.GL_FLOAT, false, 0, vertexBuf);
    GLES20.glEnableVertexAttribArray(iPosition);
    GLES20.glDrawArrays(GLES20.GL_POINTS, 0, 1);
  }
normally, we can use gl object passed in the function, since we are using OpenGL ES 2.0, we don’t use the default GL10 object.
First line of code is very familiar.
in order to use the shaders we wrote earlier, we have to load the program that we have linked the shaders to. then specify the attributes, in our case vertex, to use this attribute in the shader we have to enable the attribute.
then just use DrawArray function to draw the point on the screen.
Remaining code of Renderer, where we specify the vertex data and load it.
int iProgId;
int iPosition;
float[] vertices = {
  0f,0f,0f 
};
FloatBuffer vertexBuf;

//constructor
public PointRenderer()
{
  vertexBuf = ByteBuffer.allocateDirect(vertices.length * 4).order(ByteOrder.nativeOrder()).asFloatBuffer();
  vertexBuf.put(vertices).position(0);
}
This does not require much of an explanation.