zoukankan      html  css  js  c++  java
  • 使用Android OpenGL ES 2.0绘图之三:绘制形状

    传送门 ☞ 轮子的专栏 ☞ 转载请注明 ☞ http://blog.csdn.net/leverage_1229

             在定义好待绘制的形状之后,就要开始绘制它们了。使用OpenGL ES 2.0绘制形状可能比你想象的要复杂一些,因为它的API中提供了大量控制渲染管道的行为。

            这一节将介绍如何使用OpenGL ES 2.0的API绘制上一节你已经定义好的那些形状。

    1初始化形状

            在做任何绘制之前,你必须初始化形状并加载它。除非形状的结构(原始坐标)在程序执行过程中发生了改变,都应该在你的Renderer类的onSurfaceCreated()方法中进行初始化内存和处理效率。
    public void onSurfaceCreated(GL10 unused, EGLConfig config) {
        ...
    
        // 初始化一个三角形
        mTriangle = new Triangle();
        // 初始化一个正方形
        mSquare = new Square();
    }

    2绘制一个形状

            使用OpenGL ES 2.0绘制一个定义好的形状需要大量的代码,因为必须提供给图形渲染管道很多细节信息。具体定义如下:
    2.1VertexShader:用于呈现形状顶点的OpenGL ES图形代码。
    2.2FragmentShader:用于呈现形状外观(颜色或纹理)的OpenGL ES代码。
    2.3Program:一个OpenGL ES对象,包含了你想要用来绘制一个或多个形状的shader。
            你至少需要一个vertex shader来绘制一个形状和一个fragment shader来为形状着色。这些shader必须被编译,然后将它们添加到一个OpenGL ES program中,接着使用program绘制形状。这里有一个例子,演示了如何定义基本的shader来绘制一个形状:
    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 Shading Language(GLSL)代码,必须在OpenGL ES环境下先编译再使用。想要编译这些代码,需要在你的Renderer类中创建一个工具类方法:

    public static int loadShader(int type, String shaderCode){
    
        // 创建一个vertex shader类型(GLES20.GL_VERTEX_SHADER)
        // 或一个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。可以在你的renderer对象的构造器中做这些事情,因为只需要执行一次。
            注:编译OpenGL ES shader和链接program是很耗CPU的,所以你应该尽量避免多次执行它们。如果在运行时还不知道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
    }

            此时,应该可以添加真正的绘制调用了。你需要提供一些参数给渲染管道,告诉它想要绘制什么以及如何绘制。因为绘制操作是因形状而异的,让形状类包含特定的绘制逻辑是一个不错的设计。
            创建一个draw()方法负责绘制形状。下面的代码将展示向形状的vertex shader和fragment shader设置位置和颜色值,然后执行绘制操作。

    public void draw() {
        // 添加program到OpenGL ES环境中
        GLES20.glUseProgram(mProgram);
    
        // 获取指向vertex shader的成员vPosition的handle
        mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");
    
        // 启用一个指向三角形的顶点数组的handle
        GLES20.glEnableVertexAttribArray(mPositionHandle);
    
        // 准备三角形的坐标数据
        GLES20.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX,
                                     GLES20.GL_FLOAT, false,
                                     vertexStride, vertexBuffer);
    
        // 获取指向fragment shader的成员vColor的handle 
        mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor");
    
        // 设置三角形的颜色
        GLES20.glUniform4fv(mColorHandle, 1, color, 0);
    
        // 绘制三角形
        GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount);
    
        // 禁用指向三角形的顶点数组
        GLES20.glDisableVertexAttribArray(mPositionHandle);
    }

            一旦你完成了这些代码,绘制这个对象只需要在Renderer类的onDrawFrame()方法中调用draw()方法。当你运行应用程序时,它看起来应该是这样的:

     

            此示例中的代码还存在很多问题。首先,它不会给人留下深刻印象。其次,当你改变设备屏幕的方向时,三角形会被压扁和改变形状。原因是它的各个顶点无法根据屏幕的宽高比例进行修正。可以使用投影和相机视图来解决这个问题。不过那是下一节的内容了。
            最后,三角形是静止不动的,这样看起来有点无聊。在后续内容中,我们还将介绍如何使用OpenGL ES图形管道让形状旋转起来,以及更多有趣的用法。

  • 相关阅读:
    Centos安装mysql 5.6
    Cache和Buffer都是缓存,主要的区别是什么?
    服务器遭受攻击后的处理过程
    划船学python——语法(二)循环语句
    划船学python——语法(一)条件语句
    划船学python——初识数据类型
    FTP与SFTP区别
    说一说Nginx Proxy配置
    Virtual Box 中的虚拟系统无法调整分辨率(无法自适应窗口大小)
    [error]subprocess.CalledProcessError: Command '['which', 'g++']' returned non-zero exit status 1.
  • 原文地址:https://www.cnblogs.com/innosight/p/3271163.html
Copyright © 2011-2022 走看看