Android官网培训课:绘制形状

用OpenGL定义了形状之后,下一步是绘制他们。以OpenGL ES 2.0绘制形状要写比你想象要多一些的代码,因为所提供的API提供了许多对图形渲染pipeline的控制。
本节课讲解如何用OpenGL ES 2.0 API绘制上节课所定义的形状。

初始化形状


在做任何绘制之前,需要初始化并加载所要绘制的形状。除非形状的结构(最初的坐标)在程序执行过程中会变化,否则你应该用你的渲染器的onSurfaceCreated()方法来初始化形状,这样做是为了内存效率和处理效率考虑。

public void onSurfaceCreated(GL10 unused, EGLConfig config) {
    ...

    // 初始化一个三角形
    mTriangle = new Triangle();
    // 初始化一个正方形
    mSquare = new Square();
}

绘制形状


用OpenGL ES 2.0绘制一个定义好的形状需要大量的代码,因为你必须提供许多图形渲染通道的设置细节。特别的,你应该定义如下:

  • 顶点着色器Vertex Shader - OpenGL ES 图形代码,用于渲染一个形状的顶点
  • 片段着色器Fragment Shader - OpenGL ES 代码,用于以颜色或者纹理来渲染一个图形的face
  • 项目Program - 一个OpenGL ES 对象,包含了你所要使用的shader
你需要至少一个Vertex Shader来绘制一个图形,至少一个fragment shader来染色这个图形。这些shader必须首先被编译,然后添加到OpenGL ES Program中,program用于绘制形状。

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;" +
    "}";

shader包含OpenGL着色语言(GLSL)代码,代码必须在其被使用之前被编译。为了编译这些代码,在你的渲染器类中创建一个工具方法:

public static int loadShader(int type, String shaderCode){

    // 创建一个vertex shader 类型 (GLES20.GL_VERTEX_SHADER)
    // 或者是一个 a fragment shader 类型 (GLES20.GL_FRAGMENT_SHADER)
    int shader = GLES20.glCreateShader(type);

    // 将源代码加入到shader,然后编译
    GLES20.glShaderSource(shader, shaderCode);
    GLES20.glCompileShader(shader);

    return shader;
}

为了绘制形状,需要先编译shader代码,然后加入到一个OpenGL ES Program对象,然后链接这个program对象。将这些操作放在要绘制对象的构造函数中,这样保证只会被执行一次。

注意: 编译OpenGL ES shader然后链接OpenGL ES program操作非常耗费CPU周期,处理时间长,因此应当避免执行此操作超过一次。如果你不知道在运行时shader的内容,你应该构建你的代码这样shader只会被创建一次并且会被缓存以备将来的使用。

public Triangle() {
    ...

    int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode);
    int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode);

    mProgram = GLES20.glCreateProgram();             // 创建一个空的OpenGL ES Program
    GLES20.glAttachShader(mProgram, vertexShader);   // 把vertex shader添加到program
    GLES20.glAttachShader(mProgram, fragmentShader); // 把fragment shader到program
    GLES20.glLinkProgram(mProgram);                  // 创建OpenGL ES program可执行文件
}

到这一步,就可以执行实际的绘制形状的调用了。用OpenGL ES 绘制形状需要你指定几个参数以通知渲染通道你想要绘制什么,如何绘制。由于这些参数因形状不同而不同,所以将其包含在具体形状类的绘制逻辑代码中比较合适。

创建一个draw() 方法作为绘制图形的总调方法。在这个方法中,首先将位置值和颜色值设置到形状的vertex shader和fragment shader,然后开始绘制形状。

public void draw() {
    // 把program添加到OpenGL ES环境
    GLES20.glUseProgram(mProgram);

    // 获取vertex shader的vPosition的句柄
    mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");

    // Enable a handle to the triangle vertices
    GLES20.glEnableVertexAttribArray(mPositionHandle);

    // 准备三角形坐标数据
    GLES20.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX,
                                 GLES20.GL_FLOAT, false,
                                 vertexStride, vertexBuffer);

    // 获取fragment shader的vColor句柄
    mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor");

    // 设置颜色
    GLES20.glUniform4fv(mColorHandle, 1, color, 0);

    // 绘制三角形
    GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount);

    // Disable vertex array
    GLES20.glDisableVertexAttribArray(mPositionHandle);
}

当你将所有这些代码都准备好,绘制形状只需要在你的渲染器的onDrawFrame()方法中调用draw()。当运行app时,它会看起来是这个样子:

图 1. 无投影和摄像机视角的三角形

这个代码示例中有几个问题。首先,它不会给使用者以深刻的印象。其次,这个三角形有些扁平歪曲,而且当你改变屏幕方向时其形状也随之改变。形状看起来歪曲的原因是形状的顶点并未根据GLSurfaceView显示区域的比例而纠正。下节课中,用投影和摄像机视角来解决此问题。

最后,这个三角形是静止不动的,有点单调乏味。在后面课程中,你可以用OpenGL ES图形通道旋转形状,并且做一些其他有趣的事情。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值