7.5 增加着色器程序类(Adding Classes for Our Shader Programs)
这一部分将会针对纹理着色器及颜色着色器创建两个不同的类,我们将会使用纹理着色器程序绘制桌面,使用着色器程序绘制小球;同时我们将会建立一个基类以便复用一些相同的功能函数,因为现在中心分隔线已经是纹理的一部分了所以并不需要考虑绘制;如下图是即将要创建的类结构:
现在在类ShaderHelper中创建一个辅助函数,代码如下:
//AirHockeyTextured/src/com/airhockey/android/util/ShaderHelper.java
public static int buildProgram(String vertexShaderSource,
String fragmentShaderSource) {
int program;
// Compile the shaders.
int vertexShader = compileVertexShader(vertexShaderSource);
int fragmentShader = compileFragmentShader(fragmentShaderSource);
// Link them into a shader program.
program = linkProgram(vertexShader, fragmentShader);
if (LoggerConfig.ON) {
validateProgram(program);
}
return program;
}
这个函数将会编译由vertexShaderSource 及fragmentShaderSource着色器并链接成相应程序,假如Log是打开的话还会验证着色器程序。
在包com.airhockey.android.programs中新建类ShaderProgram,并增加如下代码:
AirHockeyTextured/src/com/airhockey/android/programs/ShaderProgram.java
// Uniform constants
protected static final String U_MATRIX = "u_Matrix";
protected static final String U_TEXTURE_UNIT = "u_TextureUnit";
// Attribute constants
protected static final String A_POSITION = "a_Position";
protected static final String A_COLOR = "a_Color";
protected static final String A_TEXTURE_COORDINATES = "a_TextureCoordinates";
// Shader program
protected final int program;
protected ShaderProgram(Context context, int vertexShaderResourceId,
int fragmentShaderResourceId) {
// Compile the shaders and link the program.
program = ShaderHelper.buildProgram(TextResourceReader.readTextFileFromResource(
context, vertexShaderResourceId),
TextResourceReader.readTextFileFromResource(context, fragmentShaderResourceId));
}
public void useProgram() {
// Set the current OpenGL shader program to this program.
glUseProgram(program);
}
首先定义了一些通用的常量,在构造函数中调用了刚刚创建的函数并编译产生相应的OpenGL着色器程序;最后定义了函数useProgram(),在需要的时候我们将调用函数告诉OpenGL使用相应的着色器进行渲染。
7.5.1 增加纹理相关类(Adding the Texture Shader Program)
这里将会定义一个类进行纹理的相关设置,创建类TextureShaderProgram 并让其继承于ShaderProgram,首先添加如下代码:
AirHockeyTextured/src/com/airhockey/android/programs/TextureShaderProgram.java
// Uniform locations
private final int uMatrixLocation;
private final int uTextureUnitLocation;
// Attribute locations
private final int aPositionLocation;
private final int aTextureCoordinatesLocation;
增加了4个int类型的变量存储uniform及attribute变量的位置引用。
7.5.1.1 初始化着色器(Initializing the Shader Program)
下一步是初始化着色器,相关代码如下:
//AirHockeyTextured/src/com/airhockey/android/programs/TextureShaderProgram.java
public TextureShaderProgram(Context context) {
super(context, R.raw.texture_vertex_shader, R.raw.texture_fragment_shader);
// Retrieve uniform locations for the shader program.
uMatrixLocation = glGetUniformLocation(program, U_MATRIX);
uTextureUnitLocation = glGetUniformLocation(program, U_TEXTURE_UNIT);
// Retrieve attribute locations for the shader program.
aPositionLocation = glGetAttribLocation(program, A_POSITION);
aTextureCoordinatesLocation = glGetAttribLocation(program, A_TEXTURE_COORDINATES);
}
该构造函数将会调用超类以完成着色器的创建,然后读取并保存相关位置引用数据。
7.5.1.2 设置纹理参数(Setting Uniforms and Returning Attribute Locations)
下一步是传入相应的矩阵及纹理,代码如下:
//AirHockeyTextured/src/com/airhockey/android/programs/TextureShaderProgram.java
public void setUniforms(float[] matrix, int textureId) {
// Pass the matrix into the shader program.
glUniformMatrix4fv(uMatrixLocation, 1, false, matrix, 0);
// Set the active texture unit to texture unit 0.
glActiveTexture(GL_TEXTURE0);
// Bind the texture to this unit.
glBindTexture(GL_TEXTURE_2D, textureId);
// Tell the texture uniform sampler to use this texture in the shader by
// telling it to read from texture unit 0.
glUniform1i(uTextureUnitLocation, 0);
}
首先是传入变换矩阵;后面的稍作解释下,在OpenGL中,当进行纹理绘制的时候,并不会直接把纹理传递给着色器,而是使用一个纹理单元持有纹理。之所以这样因为GPU只能同时绘制数量有限的纹理,它使用这些纹理单元代表当前正在被绘制的已激活纹理。
当然我们也可以把纹理图片交换出纹理单元,但是如果很频繁的交换则会引起性能问题;我们也可以使用多个纹理单元同时绘制多个纹理。
这里首先调用glActiveTexture()激活0号纹理,然后调用 glBindTexture()绑定该纹理单元;然后调用函数glUniform1i(uTextureUnitLocation, 0)把当前选定的纹理单元传递给着色器程序中的变量u_TextureUnit。
现在纹理相关类基本完成,由于你还需要把顶点数据绑定到着色器中相应的变量,因此增加如下代码:
//AirHockeyTextured/src/com/airhockey/android/programs/TextureShaderProgram.java
public int getPositionAttributeLocation() {
return aPositionLocation;
}
public int getTextureCoordinatesAttributeLocation() {
return aTextureCoordinatesLocation;
}
7.5.2 增加颜色着色器类(Adding the Color Shader Program)
在相同包下创建类ColorShaderProgram并继承于ShaderProgram,该类的模式与纹理类TextureShaderProgram一样,代码如下:
//AirHockeyTextured/src/com/airhockey/android/programs/ColorShaderProgram.java
// Uniform locations
private final int uMatrixLocation;
// Attribute locations
private final int aPositionLocation;
private final int aColorLocation;
public ColorShaderProgram(Context context) {
super(context, R.raw.simple_vertex_shader, R.raw.simple_fragment_shader);
// Retrieve uniform locations for the shader program.
uMatrixLocation = glGetUniformLocation(program, U_MATRIX);
// Retrieve attribute locations for the shader program.
aPositionLocation = glGetAttribLocation(program, A_POSITION);
aColorLocation = glGetAttribLocation(program, A_COLOR);
}
public void setUniforms(float[] matrix) {
// Pass the matrix into the shader program.
glUniformMatrix4fv(uMatrixLocation, 1, false, matrix, 0);
}
public int getPositionAttributeLocation() {
return aPositionLocation;
}
public int getColorAttributeLocation() {
return aColorLocation;
}
我们将会使用这个类绘制小球。
通过着色器程序与绘制对象数据的解耦,使得代码的复用性更好了;比如我们可以使用颜色着色器绘制任何包含颜色属性的物体而不仅仅是绘制小球(点击进入下一章)。