美文网首页
OpenGL ES 扫盲

OpenGL ES 扫盲

作者: 毛先森 | 来源:发表于2020-09-02 15:42 被阅读0次

前言

OpenSL 是处理音频的底层开源库, OpenGL 则是处理图像相关的库,而对于移动端 Cpu/内存资源比较吃紧的特性,OpenGL 将部分不那么重要的 Api 进行精简,推出了 OpenGL ES ,相当于一个 mini 版本

从 2014 年以来,OpenGL ES 经历多个版本迭代,现在已经迭代到 3.x 版本

image.png

如何在 Android 上使用

首先它对应了专门的控件 GLSurfaceView,我们用 Opengl ES 可以直接绘制的图形有点、线、三角形,其他图像怎么办呢? -- 用三角形拼接

  • 构建 OpenGL ES 环境
    // AndroidManifest 文件中声明

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

  • 使用 GLSurfaceView
  • 构建渲染程序

ByteBuffer 是什么

缓冲区是一段定长的存储空间,用于加速频繁的 I/O 操作,其实就是临时存储数据,直接与信道(Channel)打交道,写入数据到信道或者从信道读取。

  • ByteBuffer 的使用方法
  1. allocate(int capacity):从堆空间中分配一个容量大小为capacity的byte数组作为缓冲区的byte数据存储器
  2. allocateDirect(int capacity):通过操作系统来创建内存块用作缓冲区,而不在JVM堆栈中
  3. wrap(byte[] array):这个缓冲区的数据会存放在byte数组中
  4. wrap(byte[] array,int offset, int length):在上一个方法的基础上可以指定偏移量和长度

实践:使用 OpenGL 绘制 2D 三角形

OpenGL ES 是直接作用于硬件,所以有较高的性能优势,但是需要特定的语言来实现,比如在绘制三角形中,我们需要自己手写 shader (着色器),Opsl ES 2.0 以后需要自己实现了,在 1.x 版本都是系统的工作,提高了开发难度

  • 定义三角形
  • 定义顶点着色器、片源着色器
  • 绘制

/**
 * 用于 OpenGL 绘制的三角形
 * @author : kai.mao
 * @date :  2020/9/2
 */
public class GLTriangle {

    private final int mProgram;

    // 为顶点创建一个浮点型缓冲区
    private FloatBuffer vertexBuffer;
    private static final int VERTEX_COUNT = 3;
    //用于存取attribute修饰的变量的位置编号
    private int mPositionHandle;
    //用于存取uniform修饰的变量的位置编号
    private int mColorHandle;

    private final int vertexCount = triangleCoords.length / VERTEX_COUNT;
    // 4 bytes per vertex
    private final int vertexStride = VERTEX_COUNT * 4;



    static float triangleCoords[] = {
            // top 屏幕顶端中心点
            0.0f, 1.0f, 1.0f,
            // bottom left 屏幕底部左下角
            -1.0f, -1.0f, 0.0f,
            // bottom right 屏幕底部右下角
            1.0f, -1.0f, 0.0f
            //以上坐标z都为0 创建一个平面的三角形
    };


    // Set color with red, green, blue and alpha (opacity) values
    private float color[] = { 0.63671875f, 0.76953125f, 0.22265625f, 1.0f };

    public GLTriangle(){
        ByteBuffer byteBuffer = ByteBuffer.allocateDirect(triangleCoords.length * 4);
        byteBuffer.order(ByteOrder.nativeOrder());
        vertexBuffer = byteBuffer.asFloatBuffer();
        vertexBuffer.put(triangleCoords);
        vertexBuffer.position(0);

        // 加载顶点着色器
        int vertexShader = TriangleRender.loadShader(GLES20.GL_VERTEX_SHADER,vertexShaderCode);
        // 加载片元着色器
        int frameShader = TriangleRender.loadShader(GLES20.GL_FRAGMENT_SHADER,fragmentShaderCode);
        // 创建 gl 程序
        mProgram = GLES20.glCreateProgram();
        GLES20.glAttachShader(mProgram, vertexShader);
        GLES20.glAttachShader(mProgram, frameShader);
        // 链接程序
        GLES20.glLinkProgram(mProgram);
    }

    /**
     * 顶点着色器代码
     * attribute变量(属性变量)只能用于顶点着色器中
     * uniforms变量(一致变量)用来将数据值从应用程其序传递到顶点着色器或者片元着色器。 。
     * varying变量(易变变量)是从顶点着色器传递到片元着色器的数据变量。
     * gl_Position (必须)为内建变量,表示变换后点的空间位置。
     */
    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;" + // 颜色值传给 gl_FragColor内建变量,完成片元的着色
                    "}";



    public void draw(){
        // 使用 GL 程序
        GLES20.glUseProgram(mProgram);
        mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");
        GLES20.glVertexAttribPointer(mPositionHandle, VERTEX_COUNT,
                GLES20.GL_FLOAT, false,
                vertexStride, vertexBuffer);
        GLES20.glEnableVertexAttribArray(mPositionHandle);
        mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor");
        GLES20.glUniform4fv(mColorHandle, 1, color, 0);
        GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount);
        GLES20.glDisableVertexAttribArray(mPositionHandle);
    }

}


/**
 * @author : kai.mao
 * @date :  2020/9/2
 */
public class TriangleRender implements GLSurfaceView.Renderer {

    GLTriangle glTriangle;


    public TriangleRender() {
    }

    @Override
    public void onSurfaceCreated(GL10 gl10, EGLConfig eglConfig) {
        glTriangle = new GLTriangle();
    }

    @Override
    public void onSurfaceChanged(GL10 gl10, int width, int height) {
        GLES20.glViewport(0, 0, width, height);

    }

    @Override
    public void onDrawFrame(GL10 gl10) {
        //清空颜色
        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);

        glTriangle.draw();
    }

    /**
     * 加载并编译着色器代码
     * 渲染器类型type={GLES20.GL_VERTEX_SHADER, GLES20.GL_FRAGMENT_SHADER}
     * 渲染器代码 GLSL
     */
    public static int loadShader(int type, String shaderCode){

        int shader = GLES20.glCreateShader(type);
        //加载shader代码 glShaderSource 和 glCompileShader
        GLES20.glShaderSource(shader, shaderCode);
        GLES20.glCompileShader(shader);
        return shader;
    }

}

PS

  • 三角形的顶点顺序以逆时针排列,叫做 “卷曲顺序”,原因统一使用卷曲顺序有利于 OpenGL 的优化,可以减少绘制那些无法被看到的图形
  • OpenGL 的坐标系和 Android 坐标系不一样,Android 中的坐标系顶点位于屏幕左上角,而 OpenGL 世界坐标系位于屏幕中心位置
image.png
  • OpenGL 程序需要使用 Java + GLSL 两种语言编写

相关链接

相关文章

网友评论

      本文标题:OpenGL ES 扫盲

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