美文网首页音视频从入门到放弃
OpenGLES 之 绘制三角形 (一)

OpenGLES 之 绘制三角形 (一)

作者: PuHJ | 来源:发表于2019-07-20 16:36 被阅读0次

一、OpenGL ES

OpenGL是一个供运行在Linux、Unix、Mac和Windows的桌面系统使用的跨平台标准的API,OpenGL ES是以手持和嵌入式设备为目标的高级3D图形应用程序编程接口。即OpenGLES是OpenGL的子集,去除了复杂的不常用的功能,专门为PDA等为目标的图形API。

OpenGLES经常用在对图片、视频、相机、游戏等方面。它是使用了GPU的计算的,从而解放CPU的使用,加速渲染能力。

二、前言

作为一门语言的入门,都是从HelloWord学起的。当然OpenGL也不例外,绘制一个三角形应该就是OpenGLES的“HelloWorld”了,但是这个HelloWorld有的复杂,并不像java等语言输入一句话就行了。绘制一个三角形虽然是“HelloWorld”,但是麻雀虽小五脏俱全,工作量和操作步骤却没有变小。

本文选择的渲染载体为:GLSurfaceView

简单的介绍下GLSurfaceView,它是继承自SurfaceView,一个双缓冲机制并可在子线程更新的具有两个View体系结构的View,在SurfaceView的基础上增加了对EGL的管理。减少了我们对EGL的操作,简化了步骤。简单说EGL是OpenGLES和底层硬件的过渡层,他抽象了硬件的细节,完成了OpenGLES的跨平台性。

编写前,先介绍一个概念:图形渲染管线,它是从输入数据开始到绘制到界面的过程。大体步骤,可以参考如下官方给出的步骤。蓝色部分是用户可定制开发的。


管线操作
  • 顶点着色器:它把一个顶点作为输入。把3D坐标转为标准化设备坐标,同时顶点着色器允许我们对顶点属性进行一些基本处理,如矩阵变换。
  • 图元装配:它将所有的顶点作为输入,将其装配成一个具体的形状。
  • 几何着色器:几何着色器把图元形式的一系列顶点的集合作为输入,通过产生新顶点构造出新的图元来生成其他形状。
  • 光栅化: 图元映射为最终屏幕上相应的像素
  • 片段着色器: 计算一个像素的最终颜色
  • 测试和混合: 根据Z轴选择是否抛弃该点(被遮住),以及对于透明的颜色进行混合。

三、绘制三角形

绘制三角形,分为如下几步:

  • 存储顶点数据到ByteBuffer中
  • 编写顶点和片段着色器,并加载相应的着色器(shader)
  • 创建着色器程序,并对顶点和片段着色器进行链接
  • 视口变换操作(glViewport)
  • 传递顶点和颜色数据
  • 绘制物体

1)、环境准备

在AndroidManifest中配置OpenGLES版本

<uses-feature android:glEsVersion="0x00020000" android:required="true" />

并用GLSurfaceView搭建好一个EGL环境,所有的操作在GLSurfaceView.Render的回调中进行。回调开始的时候代表着EGL环境搭建完毕,否则会导致操作失败。

2)、存储顶点数据到ByteBuffer中

    // 三维的顶点坐标,有方向的
    private static final float triangleCoords[] = {
            -0.5f, 1f, 0.0f,  // bottom right
            -1f, -1f, 0.0f, // bottom left
            0.5f, 1f, 0.0f, // top
    };


    private static final short indices[] = {
            0,1,2
    };

    // 颜色
    private static final  float colors[] = {0.8f, 0.4f, 0.1f, 0f};

        // 1、存储顶点坐标
        vertexBuffer = initBuffer(triangleCoords,4);
        ByteBuffer mbb = ByteBuffer.allocateDirect(triangleCoords.length * 4);
        // 数组排列用nativeOrder
        mbb.order(ByteOrder.nativeOrder());
        FloatBuffer floatBuffer = mbb.asFloatBuffer();
        floatBuffer.put(vertexBuffer);
        floatBuffer.flip();
        
        mbb = ByteBuffer.allocateDirect(indices.length * 2);
        // 数组排列用nativeOrder
        mbb.order(ByteOrder.nativeOrder());
        indiceBuffer = mbb.asShortBuffer();
        indiceBuffer.put(indices);
        indiceBuffer.flip();

将顶点坐标存放在ByteBuffer中,供下文传递数据到Shader中。

3)、编写顶点和片段着色器代码,并加载相应的着色器

分别编写顶点和片段GLSL代码,Shader是通过输入和输出进行通信的。顶点着色器中用四个元素的向量的属性值传递顶点值,传递到内置的变量gl_Position中。片段着色器是输入颜色的,这里传入的类型是uniform。precision mediump float是代表精度。

    // 顶点着色器code
    private static final String vertexShaderCode =
            "attribute vec4 vPosition;" +
            "void main() {" +
            "  gl_Position = vPosition;" +
            "}";

    // 片元着色器code
    private static final String fragmentShaderCode =
            "precision mediump float;" +
            "uniform vec4 vColor;" +
            "void main() {" +
            "    gl_FragColor = vColor;" +
            "}";
加载shader:

分别创建、加载以及编译着色器代码:

    private int loadShader(int type, String shaderCode) {
        //根据type创建顶点着色器或者片元着色器
        int shader = GLES20.glCreateShader(type);
        //将资源加入到着色器中,并编译
        GLES20.glShaderSource(shader, shaderCode);
        GLES20.glCompileShader(shader);
        return shader;
    }

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

4)、创建着色器程序,并对顶点和片段着色器进行链接

顶点和片段着色器只有在链接到着色器程序中,才会起作用。

        //创建一个空的OpenGLES程序
        mProgram = GLES20.glCreateProgram();
        //将顶点着色器加入到程序
        GLES20.glAttachShader(mProgram, vertexShader);
        //将片元着色器加入到程序中
        GLES20.glAttachShader(mProgram, fragmentShader);
        //连接到着色器程序
        GLES20.glLinkProgram(mProgram);

5)、视口变换操作(glViewport)

// 设置窗口的大小
GLES20.glViewport(0,0,width,height);

6)、传递顶点和颜色数据

现在需要将顶点、颜色、顶点索引数据传入到相应的地方,这样才会绘制出想要的形状。

首先需要获取属性为vPosition的句柄和uniform为vColor的句柄,并根据Attribute和Uniform两种方式传入对应的数据。

        GLES20.glUseProgram(mProgram);
        GLES20.glClearColor(0, 0, 0, 1);
        GLES20.glDisable(GLES20.GL_DEPTH_TEST); // 当我们需要绘制透明图片时,就需要关闭它
        // 填充数据
        //获取顶点着色器的vPosition成员句柄
        mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");
        //启用三角形顶点的句柄
        GLES20.glEnableVertexAttribArray(mPositionHandle);
        //准备三角形的坐标数据
        GLES20.glVertexAttribPointer(mPositionHandle, 3,
                GLES20.GL_FLOAT, false,
                12, vertexBuffer);

        mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor");
        Log.e(TAG, "onDrawFrame: mPositionHandle="+mPositionHandle+"  mColorHandle="+mColorHandle);
        //设置绘制三角形的颜色
        GLES20.glUniform4fv(mColorHandle, 1, colors, 0);

7)、绘制物体

通过glDrawElements方法绘制图形,glDrawElements需要使用到索引数组。绘制的API还可以是glDrawArrays,这里不需要明确的设置绘制的顺序,而是通过绘制的模式来决定的。

        //绘制三角形
        GLES20.glDrawElements(GLES20.GL_TRIANGLE_STRIP, 3,GLES20.GL_UNSIGNED_SHORT,indiceBuffer);

四、绘制模式详解

对于绘制的模式,上面的代码使用的是GLES20.GL_TRIANGLE_STRIP,除此之外还有些其它的模式:、

  • GLES20.GL_POINTS:绘制点精灵模式
  • GLES20.GL_LINES:对于n各定点,会绘制(v0、v1)、(v2、v3)、(v4、v5)、(vn-2、vn-1)条线段
  • GLES20.GL_LINE_LOOP:对于n各定点,会绘制(v0、v1)、(v1、v2)、(v2、v3)、(vn-2、vn-1)条线
  • GLES20.GL_LINE_STRIP:对于n各定点,会绘制(v0、v1)、(v1、v2)、(v2、v3)、(vn-2、vn-1)、(vn-1、v0)条线,这绘制的会多了个首位相连的线段。
  • GLES20.GL_TRIANGLES:同GL_LINES,它是不重复的使用顶点
  • GLES20.GL_TRIANGLE_STRIP :绘制一系列相互连接的三角形,对于5个顶点,它绘制的是(v0,v1,v2)、(v2,v1,v3)、(v2,v3,v4)
  • GLES20.GL_TRIANGLE_FAN: 绘制一系列相互连接的三角形,对于5个顶点,它绘制的是(v0,v1,v2)、(v0,v2,v3)、(v0,v3,v4)

五、参考

LearnOpenGL-CN
Jhuster的专栏

上例代码下载

相关文章

网友评论

    本文标题:OpenGLES 之 绘制三角形 (一)

    本文链接:https://www.haomeiwen.com/subject/ymqulctx.html