OpenGL ES 顶点缓冲区和布局(3)

发布于:2024-10-09 ⋅ 阅读:(34) ⋅ 点赞:(0)

OpenGL ES 顶点缓冲区和布局(3)

简述

顶点缓冲区的本质就是一段GPU上的显存,我们通过绑定顶点缓冲区的方式来将数据从CPU传到GPU。
我们之前在绘制三角形的例子中,我们往顶点缓冲区只传入了坐标,但是其实顶点是可以包含很多数据的,比如纹理索引,颜色等,因为它的本质其实就是一个Buffer,所以我们可以使用它做非常多的事情。
我们前面说了,顶点缓冲区只是一个一段内存,但是GPU并不知道这段内存里的这些数是什么意思,每个数据的含义是什么。所以我们需要告诉GPU顶点缓冲区布局,定义每个数据是什么意思。

配置布局接口

glVertexAttribPointer

  • 第一个参数是index,表示这个数据要传递给shader哪一个属性,这个我们在shader对篇章会详细介绍。
  • 第二个参数是size,表示有几个数
  • 第三个参数是type,表示数据类型
  • 第四个参数是normalized,表示参数是否需要归一化
  • 第五个参数是stride,每个顶点的步长,基本可以理解为一个顶点的长度
  • 第六个参数是offset,表示当前属性在当前顶点的偏移

绑定BufferData

glBufferData

  • 第一个参数,数据类型,顶点缓冲区一般用GL_ARRAY_BUFFER,后续我们还会介绍索引缓冲区
  • 第二个参数,size,我们需要分配缓冲区的大小。
  • 第三个参数,data,我们定义的数据。
  • 第四个参数,缓冲区用于使用的场景,GL_STREAM_DRAW/GL_STATIC_DRAW/GL_DYNAMIC_DRAW。
    GL_STREAM_DRAW:缓冲区数据代码设置,仅渲染一次,使用很少。
    GL_STATIC_DRAW:缓冲区数据是静态的,不会修改的。
    GL_DYNAMIC_DRAW:缓冲区数据会动态变化,常常需要更新数据。

实现渐变颜色三角形

我们这里通过顶点缓冲区传递数据,实现一个渐变三角形。这个demo会有助于我们对顶点缓冲区数据传递,同时还能对顶点着色器属性有一些了解。
我们基于之前那个三角形的案例来修改。

修改顶点缓冲区Buffer

我们之前顶点缓冲区只有9个数,三个顶点,每个顶点三个坐标,修改后每个顶点有7个数,前三个点依旧是坐标,和之前相同,而后面新增的四个顶点是坐标,即RGBA。
我们之前提过,着色器会对每个像素都调用一次,而顶点着色器则会根据顶点坐标渐变,对每个像素都进行调用。
这里实现的三个角的颜色分别是红黄蓝,然后向中间渐变。

private float[] vertexArray = new float[] {
        -0.5f, -0.5f, 0.0f, 1f, 0f, 0f, 1f,
        0.5f, -0.5f, 0.0f, 0f, 1f, 0f, 1f,
        0.0f, 0.5f, 0.0f, 0f, 0f, 1f, 1f
};

修改shader

添加了属性attribute vec4 inputColor,用于接收顶点缓冲区新增的四维用于表示颜色的数组,顶点缓冲区传递给vertexColor,通过varying传递给片段着色器。
片段着色器直接使用varying vec4 vertexColor作为gl_FragColor返回。

private final String vertexShaderCode =
        "attribute vec4 vPosition;" +
                "attribute vec4 inputColor;" +
                "varying vec4 vertexColor;" +
                "void main() {" +
                "  vertexColor = inputColor;" +
                "  gl_Position = vPosition;" +
                "}";

private final String fragmentShaderCode =
        "precision mediump float;" +
                "varying vec4 vertexColor;" +
                "uniform vec4 vColor;" +
                "void main() {" +
                "  gl_FragColor = vertexColor;" +
                "}";

顶点缓冲区创建/数据填充

创建绑定顶点缓冲区,后使用glBufferData填充数据,这里和之前创建三角形一样,基本没有修改。

public void onSurfaceCreated(GL10 gl, EGLConfig config) {
    // 清除颜色
    GLES30.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
    // 创建顶点缓冲区
    int[] idBuffer = new int[1];
    GLES30.glGenBuffers(1, idBuffer, 0);
    vertexBufferId = idBuffer[0];

    // 顶点缓冲区数据填充
    FloatBuffer vertexBuffer = ByteBuffer.allocateDirect(vertexArray.length * 4).order(ByteOrder.nativeOrder()).asFloatBuffer();
    vertexBuffer.put(vertexArray);
    vertexBuffer.position(0);

    GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, vertexBufferId);
    // 填充数据
    GLES30.glBufferData(
            GLES30.GL_ARRAY_BUFFER,
            vertexArray.length * 4,
            vertexBuffer,
            GLES30.GL_STATIC_DRAW
    );
    GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, 0);

    // shader
    shaderProgramId = initShaderProgram(vertexShaderCode, fragmentShaderCode);
}

配置顶点缓冲区布局

通过这里的glVertexAttribPointer使用,配合上面的api介绍,我们大概就能理解这个布局接口是怎么使用的。

public void onDrawFrame(GL10 gl) {
    // 清除屏幕
    GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT);
    // 使能着色器程序
    GLES30.glUseProgram(shaderProgramId);

    GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, vertexBufferId);
    int positionLocation = GLES30.glGetAttribLocation(shaderProgramId, "vPosition");

    GLES30.glEnableVertexAttribArray(positionLocation);
    // 主要改动部分在这里
    // 这里的步长为7个顶点 * sizeof(float), 这里index是传给vPosition,type还是GL_FLOAT,size为3个顶点
    GLES30.glVertexAttribPointer(positionLocation, 3, GLES30.GL_FLOAT, false, 7 * 4, 0);

    // 步长和上面一样, 这里index是传给inputColor,type还是GL_FLOAT,size为4个顶点
    int colorInputLocation = GLES30.glGetAttribLocation(shaderProgramId, "inputColor");
    GLES30.glEnableVertexAttribArray(colorInputLocation);
    GLES30.glVertexAttribPointer(colorInputLocation, 4, GLES30.GL_FLOAT, false, 7 * 4, 3 * 4);

    // 调用DrawCall绘制三角形
    GLES30.glDrawArrays(GLES30.GL_TRIANGLES, 0, 3);

    // 清除配置
    GLES30.glDisableVertexAttribArray(positionLocation);
    GLES30.glDisableVertexAttribArray(colorInputLocation);
    GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, 0);
    GLES30.glUseProgram(0);
}

效果

在这里插入图片描述

顶点数组

还有一个概念是顶点数组,我们简称VAO,相关的接口glGenVertexArrays,这个接口在GLES20上没有,但是在GLES30上有。
顶点数组的本质的作用是用于记录顶点缓冲区和顶点布局的关系,如果有多个缓冲区,顶点数组可以记录这些,可以减少调用绑定的次数。
相信大家会有一个问题,为什么GLES20上没有这个api,而且我们之前绘制三角形的demo中也没有使用这个顶点数组,为什么还可以正常绘制呢。
按照技术上的作用,这个顶点数组是必要的,前面这个问题的答案其实是因为OpenGL默认配置下就会有一个顶点数组。
这个目前了解一下即可,后续我们绘制多个对象的时候会再仔细介绍顶点数组。

小结

本节介绍了顶点缓冲区和布局,且通过实现一个渐变色的三角形demo来展示了怎么使用顶点缓冲区和布局。
顶点缓冲区的数据可以是位置,颜色,纹理等等,本质这里就是一个内存缓冲区,这里展示了颜色的用法。
配置顶点布局的接口使用比较简单,按照文档的使用即可。