绘制图形
定义图形之后,你可能想去绘制他们.使用OpenGL ES 2.0绘制图形需要的代码会比你想象的多一点,因为API提供了大量对图形渲染管线的控制.
这一课讲解如何使用OpenGL ES 2.0的API绘制前一课定义的形状.
初始化图形
在绘制之前,你必须初始化和加载你要绘制的图形,除非在运行过程中,图形结构发生改变,为了高效利用内存和处理效率,你应该在renderer的OnSurfaceCreated()中初始化他们.
public class MyGLRenderer implements GLSurfaceView.Renderer {
...
private Triangle mTriangle;
private Square mSquare;
public void onSurfaceCreated(GL10 unused, EGLConfig config) {
...
// initialize a triangle
mTriangle = new Triangle();
// initialize a square
mSquare = new Square();
}
...
}
绘制图形
使用OpenGL ES 2.0绘制定义好的图形需要大量代码,你要有心里准备,之所以需要大量代码,是因为你必须为渲染管线提供详细的信息,通常,你必须按照如下来定义:
Vertex Shader - OpenGL ES 渲染图形顶点的顶点着色器.
Fragment Shader - OpenGL ES 使用文理或者颜色渲染图形表面的片段着色器.
Program - An OpenGL ES 包含一个或者多个用来绘制图形的顶点着色器的OpenGL ES 对象.
你至少需要一个绘制图形的顶点着色器和一个用来给图形表面着色的片段着色器.这些着色器必须要编译后放入OpenGL ES 程序中,然后就可以用来绘制图形.下面是在三角形中定义的能用来绘制图形的着色器一个示例:
public class Triangle {
private final String vertexShaderCode =
"attribute vec4 vPosition;" +
"void main() {" +
" gl_Position = vPosition;" +
"}";
private final String fragmentShaderCode =
"precision mediump float;" +
"uniform vec4 vColor;" +
"void main() {" +
" gl_FragColor = vColor;" +
"}";
...
}
着色器包含OpenGL着色语言(GLSL)代码,必须在使用前预编译.为了编译这段代码,在你的renderer类中创建一个公共的方法:
public static int loadShader(int type, String shaderCode){
// create a vertex shader type (GLES20.GL_VERTEX_SHADER)
// or a fragment shader type (GLES20.GL_FRAGMENT_SHADER)
int shader = GLES20.glCreateShader(type);
// add the source code to the shader and compile it
GLES20.glShaderSource(shader, shaderCode);
GLES20.glCompileShader(shader);
return shader;
}
为了绘制图形,必须变异着色器代码并把他们加到OpenGL ES程序对象中,然后建立链接.在绘制图形的构造方法中执行,只需要依次操作就可以了.
注意:
编译OpenGL ES 着色器并与程序链接对CPU周期和处理时间而言,是非常耗时的,所以尽量避免操作次数超过一次.如果你不知道着色器在运行时的内容,你就应该像那样只创建依次然后缓存起来以便后续使用.
public class Triangle() {
...
private final int mProgram;
public Triangle() {
...
int vertexShader = MyGLRenderer.loadShader(GLES20.GL_VERTEX_SHADER,
vertexShaderCode);
int fragmentShader = MyGLRenderer.loadShader(GLES20.GL_FRAGMENT_SHADER,
fragmentShaderCode);
// create empty OpenGL ES Program
mProgram = GLES20.glCreateProgram();
// add the vertex shader to program
GLES20.glAttachShader(mProgram, vertexShader);
// add the fragment shader to program
GLES20.glAttachShader(mProgram, fragmentShader);
// creates OpenGL ES program executables
GLES20.glLinkProgram(mProgram);
}
}
现在,你已经准备好了即将使用真正的调用来绘制你的图形.使用OpenGL ES 绘制图形要求你提供几个参数来告诉渲染管线,你想要绘制什么以及如何绘制他们.由于绘制选项可能因图形不同而不同,因此最好让图形包含自己的绘制逻辑.
为绘制图形创建一个draw()方法,这段代码给顶点着色器设置了绘制起点,给片段着色器设置了颜色,然后执行绘制方法:
public void draw() {
// Add program to OpenGL ES environment
GLES20.glUseProgram(mProgram);
// get handle to vertex shader's vPosition member
mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");
// Enable a handle to the triangle vertices
GLES20.glEnableVertexAttribArray(mPositionHandle);
// Prepare the triangle coordinate data
GLES20.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX,
GLES20.GL_FLOAT, false,
vertexStride, vertexBuffer);
// get handle to fragment shader's vColor member
mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor");
// Set color for drawing the triangle
GLES20.glUniform4fv(mColorHandle, 1, color, 0);
// Draw the triangle
GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount);
// Disable vertex array
GLES20.glDisableVertexAttribArray(mPositionHandle);
}
只要所有的代码是正确的,绘制这个图形只需要在renderer.onDrawFrame()中调用这个图形对象的 draw()方法
Once you have all this code in place, drawing this object just requires a call to the draw()
method from within your renderer’s onDrawFrame()
method:
public void onDrawFrame(GL10 unused) {
...
mTriangle.draw();
}
然后运行程序,他应该看起来像这样:
代码示例中有几个问题.它不能让你的朋友感到惊讶.第二,三角形有一点变形而且当你改变设备屏幕方向时,形状也会发生变化.图形发生变形的原因是图形的顶点在屏幕显示区域中的GLSurfaceView的比例没有被修正.你可以在下一下课中使用投影和相机视角修复这个问题.
最后,这个三角形是静止的,没什么意思.在下一课(添加运动)中,你能够用OpenGL ES图形管线使它旋转起来,做更多有趣的事!