OpenGL ES 1.0和1.1使用的渲染方式是Fixed Function Pipeline,从2.0版本开始,转而使用Programmable Shader Pipeline。要想使用OpenGL ES 2.0,必须使用一种语法很像C语言的语言GLSL来编写Shader程序。
直接通过OpenGL API来使用GLSL太繁琐了,因此我进行了简单的抽象和封装。
import javax.media.opengl.GL2ES2; public abstract class GLWrapper { protected GL2ES2 getGL() { return Glob.getInstance().getGL(); } }GLWrapper类只有一个方法getGL(),返回 javax.media.opengl.GL2ES2接口。关于GL2ES2接口的更多信息,请看 这个网页。Glob对象稍后介绍。
public abstract class GLObject extends GLWrapper { protected int objectId; }GLObject继承GLWrapper,用来抽象OpenGL管理的对象(应该在GPU内存里)。
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上查到。
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单例类定义了一个方法来编译和链接Shader程序:
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,运行下面的脚本:
import javax.media.opengl.GL def vertexShader = """ void main() { gl_Position = vec4(0.0, 0.0, 0.0, 1.0); gl_PointSize = 10.0; } """ def fragmentShader = """ void main() { gl_FragColor = vec4(1.0, 0.0, 0.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在画布上画一个点。