渲染流水线
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
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里试验
打开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在画布上画一个点。