渲染流水线
OpenGL ES 1.0和1.1使用的渲染方式是Fixed Function Pipeline,从2.0版本开始,转而使用Programmable Shader Pipeline。要想使用OpenGL ES 2.0,必须使用一种语法很像C语言的语言GLSL来编写Shader程序。
封装OpenGL API
直接通过OpenGL API来使用GLSL太繁琐了,因此我进行了简单的抽象和封装。
GLWrapper
import javax.media.opengl.GL2ES2; public abstract class GLWrapper { protected GL2ES2 getGL() { return Glob.getInstance().getGL(); } }GLWrapper类只有一个方法getGL(),返回 javax.media.opengl.GL2ES2接口。关于GL2ES2接口的更多信息,请看 这个网页。Glob对象稍后介绍。
GLObject
public abstract class GLObject extends GLWrapper { protected int objectId; }GLObject继承GLWrapper,用来抽象OpenGL管理的对象(应该在GPU内存里)。
shadercompiler和ShaderObject
import static javax.media.opengl.GL2ES2.GL_FRAGMENT_SHADER; import static javax.media.opengl.GL2ES2.GL_VERTEX_SHADER; public class shadercompiler extends GLWrapper { public ShaderObject compiLevertexShader(String shadercode) { return compileShader(GL_VERTEX_SHADER,shadercode); } public ShaderObject compileFragmentShader(String shadercode) { return compileShader(GL_FRAGMENT_SHADER,shadercode); } private ShaderObject compileShader(int shaderType,String shadercode) { ShaderObject obj = new ShaderObject(); obj.objectId = getGL().glCreateShader(shaderType); getGL().glShaderSource(obj.objectId,1, new String[] {shadercode},new int[] {shadercode.length()},0); getGL().glCompileShader(obj.objectId); return obj; } }
public class ShaderObject extends GLObject { }
前面说GLSL的语法和C语言很像,其实不光是语法,编译和链接过程也很像。shadercompiler抽象Shader编译器,ShaderObject抽象编译后的Shader对象。Vertex Shader和Fragment Shader的详细信息可以从OpenGL wiki上查到。
ShaderLinker和ShaderProgram
public class ShaderLinker extends GLWrapper { public ShaderProgram linkProgram(ShaderObject... shaders) { ShaderProgram program = new ShaderProgram(); program.objectId = getGL().glCreateProgram(); for (ShaderObject shader : shaders) { getGL().glAttachShader(program.objectId,shader.objectId); } getGL().glLinkProgram(program.objectId); return program; } }
public class ShaderProgram extends GLObject { public void use() { getGL().gluseProgram(objectId); } }类似C语言的链接过程,ShaderLinker把编译好的ShaderObject链接成一个ShaderProgram,之后就可以使用这个ShaderProgram了。
Glob
import javax.media.opengl.GL2ES2; public class Glob { private static final Glob INSTANCE = new Glob(); public static Glob getInstance() { return INSTANCE; } private GL2ES2 gl; private final shadercompiler shadercompiler; private final ShaderLinker shaderLinker; private Glob() { shadercompiler = new shadercompiler(); shaderLinker = new ShaderLinker(); } public ShaderProgram compileAndLink(String vertexshadercode,String fragmentshadercode) { ShaderObject vertexShader = shadercompiler.compiLevertexShader(vertexshadercode); ShaderObject fragmentShader = shadercompiler.compileFragmentShader(fragmentshadercode); ShaderProgram program = shaderLinker.linkProgram(vertexShader,fragmentShader); getGL().glDeleteShader(vertexShader.objectId); getGL().glDeleteShader(fragmentShader.objectId); return program; } ... }
类图
编译和链接过程示意图
在OpenGL Console里试验
打开OpenGL Console,运行下面的脚本:
import javax.media.opengl.GL def vertexShader = """ void main() { gl_Position = vec4(0.0,0.0,1.0); gl_PointSize = 10.0; } """ def fragmentShader = """ void main() { gl_FragColor = vec4(1.0,1.0); } """ def shaderProgram = glob.compileAndLink(vertexShader,fragmentShader) shaderProgram.use() gl.glClearColor(0.8f,0.6f,0.4f,1.0f) gl.glClear(gl.GL_COLOR_BUFFER_BIT)画布应该被清除成像下面这样的淡黄色:
代码解释
上面的脚本只是定义了两个字符串,一个是Vertex Shader代码,一个是Fragment Shader代码,然后调用Glob对象的compileAndLink()方法编译和链接Shader,最后清屏。本篇文章里的Shader没有起任何作用,只是证明我们的代码可以工作。在下一篇文章中,我会用Shader在画布上画一个点。