绘制形状(Drawing Shapes)

    在定义了将要绘制的形状之后,你可能希望使用 OpenGL 绘制出它们。使用 OpenGL ES 2.0 绘制图形可能会比你想象当中更复杂一些,因为 API 中提供了大量对于图形渲染流程的控制。

    这节课将解释如何使用 OpenGL ES 2.0 接口画出在上一节课中定义的形状。

    在你开始绘画之前,你需要初始化并加载你期望绘制的图形。除非你所使用的形状结构(原始坐标)在执行过程中发生了变化,不然的话你应该在渲染器的 onSurfaceCreated() 方法中初始化它们,这样做是出于内存和执行效率的考量。

    画一个形状(Draw a Shape)

    使用 OpenGL ES 2.0 画一个定义好的形状需要较多代码,因为你需要提供很多图形渲染流程的细节。具体而言,你必须定义如下几项:

    • 顶点着色器(Vertex Shader):用来渲染形状顶点的 OpenGL ES 代码。
    • 片段着色器(Fragment Shader):使用颜色或纹理渲染形状表面的 OpenGL ES 代码。
    • 程式(Program):一个 OpenGL ES 对象,包含了你希望用来绘制一个或更多图形所要用到的着色器。
    1. private final String vertexShaderCode =
    2. "attribute vec4 vPosition;" +
    3. "void main() {" +
    4. " gl_Position = vPosition;" +
    5. "}";
    6. private final String fragmentShaderCode =
    7. "precision mediump float;" +
    8. "uniform vec4 vColor;" +
    9. "void main() {" +
    10. "}";
    11. ...

    着色器包含了 OpenGL Shading Language(GLSL)代码,它必须先被编译然后才能在 OpenGL 环境中使用。要编译这些代码,需要在你的渲染器类中创建一个辅助方法:

    为了绘制你的图形,你必须编译着色器代码,将它们添加至一个 OpenGL ES Program 对象中,然后执行链接。在你的绘制对象的构造函数里做这些事情,这样上述步骤就只用执行一次。

    Note:编译 OpenGL ES 着色器及链接操作对于CPU周期和处理时间而言,消耗是巨大的,所以你应该避免重复执行这些事情。如果在执行期间不知道着色器的内容,那么你应该在构建你的应用时,确保它们只被创建了一次,并且缓存以备后续使用。

    1. public class Triangle() {
    2. ...
    3. private final int mProgram;
    4. public Triangle() {
    5. ...
    6. int vertexShader = MyGLRenderer.loadShader(GLES20.GL_VERTEX_SHADER,
    7. vertexShaderCode);
    8. int fragmentShader = MyGLRenderer.loadShader(GLES20.GL_FRAGMENT_SHADER,
    9. fragmentShaderCode);
    10. mProgram = GLES20.glCreateProgram();
    11. // add the vertex shader to program
    12. GLES20.glAttachShader(mProgram, vertexShader);
    13. // add the fragment shader to program
    14. GLES20.glAttachShader(mProgram, fragmentShader);
    15. // creates OpenGL ES program executables
    16. GLES20.glLinkProgram(mProgram);
    17. }
    18. }

    至此,你已经完全准备好添加实际的调用语句来绘制你的图形了。使用OpenGL ES绘制图形需要你定义一些变量来告诉渲染流程你需要绘制的内容以及如何绘制。既然绘制属性会根据形状的不同而发生变化,把绘制逻辑包含在形状类里面将是一个不错的主意。

    一旦你完成了上述所有代码,仅需要在你渲染器的onDrawFrame()方法中调用draw()方法就可以画出我们想要画的对象了:

    1. public void onDrawFrame(GL10 unused) {
    2. ...
    3. }

    当你运行这个应用时,它看上去会像是这样:

    在这个代码样例中,还存在一些问题。首先,它无法给用户带来什么深刻的印象。其次,这个三角形看上去有一些扁,另外当你改变屏幕方向时,它的形状也会随之改变。发生形变的原因是因为对象的顶点没有根据显示的屏幕区域的长宽比进行修正。你可以在下一节课中使用投影(Projection)或者相机视角(Camera View)来解决这个问题。