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:
- Create Android project.
- Create and Setup GLSurfaceView object.
- Create and compile shaders
- Draw frame.
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
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.public void onDrawFrame(GL10 gl)public void onSurfaceChanged(GL10 gl, int width, int height)public void onSurfaceCreated(GL10 gl, EGLConfig config)
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
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 variablesString strVShader ="attribute vec4 a_position;\n"+
"void main()\n" +
"{\n" +
"gl_PointSize = 45.0;\n" +
"gl_Position = a_position;\n"+
"}";
Simple Fragment Shader:
As you can see here, we are just setting the Fragment Color.String strFShader = "precision mediump float;" +
"void main() " +
"{" +
"gl_FragColor = vec4(1,0,0,1);" +
"}";
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.- Create a shader with glCreateShader function with shader type (Vertex Shader or Fragment Shader).
- Specify the shader code with glShaderSource.
- Compile the shader with glCompileShader.
- Find out the status of compilation with glGetShaderiv
Creating Program//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;
}
Steps involved as follows:
- Create a program to use shaders with glCreateProgram.
- Load shaders.
- Attach shader to program with glAttachShader.
- And then link the program with glLinkProgram
- To know the status of linking use glGetProgramiv
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 functionload 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.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");
}
onSurfaceChanged
Only setting the view port size.i’m not setting any projection view (this we will do it in later developments).public void onSurfaceChanged(GL10 gl, int width, int height) {GLES20.glViewport(0, 0, width, height);}
now comes the important function onDrawFrame.
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.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);}
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.
This does not require much of an explanation.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);}
Good
ReplyDeleteBrilliant Tutorial - thanks :-)
ReplyDelete