Android 上的 OpenGL ES 2:如何使用 VBO

Posted

技术标签:

【中文标题】Android 上的 OpenGL ES 2:如何使用 VBO【英文标题】:OpenGL ES 2 on Android: how to use VBOs 【发布时间】:2014-07-31 08:07:03 【问题描述】:

这个问题类似于我在这里问的问题android OpenGL ES 2: Introduction to VBOs 但是从那时起我尝试了多种方法,但我仍然没有成功,所以我认为发布另一个问题,我提供额外的细节将是一个更好的方法。

我是 Android 上 OpenGL ES 2 的新手(我从未使用过其他 OpenGL,我只需要为我正在为 Android 开发的应用程序绘制一些东西),我非常想了解如何使用 VBO。我尝试修改此OpenGL ES 2 for Android tutorial 以在绘制三角形时使用 VBO。我尝试使用this step by step guide 和this tutorial,但我仍然不了解所有内容,我对所有这些东西都很陌生。我的应用程序目前在启动时崩溃。这是我所拥有的:

public class Triangle 

private final String vertexShaderCode =
        // This matrix member variable provides a hook to manipulate
        // the coordinates of the objects that use this vertex shader
        "uniform mat4 uMVPMatrix;" +
        "attribute vec4 vPosition;" +
        "void main() " +
        // the matrix must be included as a modifier of gl_Position
        // Note that the uMVPMatrix factor *must be first* in order
        // for the matrix multiplication product to be correct.
        "  gl_Position = uMVPMatrix * vPosition;" +
        "";

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

private final FloatBuffer vertexBuffer;
private final int mProgram;
private int mPositionHandle;
private int mColorHandle;
private int mMVPMatrixHandle;
private final int buffer[] = new int[1];

// number of coordinates per vertex in this array
static final int COORDS_PER_VERTEX = 3;
static float triangleCoords[] = 
        // in counterclockwise order:
        0.0f,  0.622008459f, 0.0f,   // top
       -0.5f, -0.311004243f, 0.0f,   // bottom left
        0.5f, -0.311004243f, 0.0f    // bottom right
;
private final int vertexCount = triangleCoords.length / COORDS_PER_VERTEX;
private final int vertexStride = COORDS_PER_VERTEX * 4; // 4 bytes per vertex

float color[] =  0.63671875f, 0.76953125f, 0.22265625f, 0.0f ;

/**
 * Sets up the drawing object data for use in an OpenGL ES context.
 */
public Triangle() 
    // initialize vertex byte buffer for shape coordinates
    ByteBuffer bb = ByteBuffer.allocateDirect(
            // (number of coordinate values * 4 bytes per float)
            triangleCoords.length * 4);
    // use the device hardware's native byte order
    bb.order(ByteOrder.nativeOrder());

    // create a floating point buffer from the ByteBuffer
    vertexBuffer = bb.asFloatBuffer();
    // add the coordinates to the FloatBuffer
    vertexBuffer.put(triangleCoords);
    // set the buffer to read the first coordinate
    vertexBuffer.position(0);

    // First, generate as many buffers as we need.
    // This will give us the OpenGL handles for these buffers.

    GLES20.glGenBuffers(1, buffer, 0);

    // prepare shaders and OpenGL program
    int vertexShader = MyGLRenderer.loadShader(
            GLES20.GL_VERTEX_SHADER, vertexShaderCode);
    int fragmentShader = MyGLRenderer.loadShader(
            GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode);

    mProgram = GLES20.glCreateProgram();             // create empty OpenGL Program
    GLES20.glAttachShader(mProgram, vertexShader);   // add the vertex shader to program
    GLES20.glAttachShader(mProgram, fragmentShader); // add the fragment shader to program
    GLES20.glLinkProgram(mProgram);                  // create OpenGL program executables



/**
 * Encapsulates the OpenGL ES instructions for drawing this shape.
 *
 * @param mvpMatrix - The Model View Project matrix in which to draw
 * this shape.
 */
public void draw(float[] mvpMatrix) 


    // get handle to fragment shader's vColor member
    mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor");

    // Set color for drawing the triangle
    GLES20.glUniform4fv(mColorHandle, 1, color, 0);

    // get handle to shape's transformation matrix
    mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
    MyGLRenderer.checkGlError("glGetUniformLocation");

    // get handle to vertex shader's vPosition member
    mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");



    //these I don't fully understand
    GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, buffer[0]);
    GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER,vertexBuffer.capacity() * 4,vertexBuffer,GLES20.GL_STATIC_DRAW);

    // Add program to OpenGL environment
    GLES20.glUseProgram(mProgram);


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

    // Prepare the triangle coordinate data
    GLES20.glVertexAttribPointer(
            mPositionHandle, COORDS_PER_VERTEX,
            GLES20.GL_FLOAT, false,
            vertexStride, 0);




    // Apply the projection and view transformation
    GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mvpMatrix, 0);
    MyGLRenderer.checkGlError("glUniformMatrix4fv");

    // Draw the triangle
    GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount);

//        //is this still necesary? or do i have to use glDeleteBuffers?
//        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);



当我将 0 而不是 vertexBuffer 放入 glVertexAttribPointer() 时,我收到一条错误消息,提示我没有提供必要的参数:expected parameter: ptr: java.nio.Buffer; actual arguments: 0(int)

【问题讨论】:

【参考方案1】:

由于数据指针的使用,到 VBO 的转换可能有点奇怪。

通过快速检查,您的主要问题在于

GLES20.glVertexAttribPointer(
            mPositionHandle, COORDS_PER_VERTEX,
            GLES20.GL_FLOAT, false,
            vertexStride, vertexBuffer);

应该是这样的

 GLES20.glVertexAttribPointer(
            mPositionHandle, COORDS_PER_VERTEX,
            GLES20.GL_FLOAT, false,
            vertexStride, 0);

关于这个缓冲区:VBO 是 GPU 上的自定义缓冲区,通常使用和优化以将顶点数据直接存储到 GPU。这样做获得的性能是无需在每次绘制调用时将顶点数据复制到 GPU。

这些缓冲区仍然是自定义的,在生成它们时,您需要设置的只是它们的大小。我看到您在顶点数上使用因子 *4 假设浮点值的大小为 4 个字节,这不是最好的主意,因为这可能并不总是正确的。如果可能,请始终尝试使用某种形式的“sizeOf”。无论如何,您的缓冲区已正确创建并将数据发送到它。

数据发送到 VBO 后,您应该将它们保留在那里,直到您需要它们。这意味着您通常为每个唯一对象(例如正方形)创建一个 VBO,然后只保留其 ID。每当你想绘制它时,你只需简单地绑定缓冲区并像你一样绘制。换句话说,缓冲区永远不应该在 draw 方法中创建。您所做的还有内存泄漏,因为一旦不再需要缓冲区,您就负责通过调用 delete 来释放缓冲区。

关于glVertexAttribPointer 上的指针问题:有两种方法可以使用此方法。如果没有 VBO,最后一个参数是指向 CPU 上数据的指针。使用 VBO,您需要将其设置为 VBO 内的相对指针。这意味着当绑定 VBO 时,缓冲区的开头将是 NULL (0),您甚至可能需要对该值进行类型转换。对于缓冲区中的其他位置,您需要手动计算它们。

以及您专门发布的代码:

    // First, generate as many buffers as we need.
    // This will give us the OpenGL handles for these buffers.
    final int buffer[] = new int[1];
    GLES20.glGenBuffers(1, buffer, 0);

    //these I don't fully understand
    GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, buffer[0]);
    GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER,vertexBuffer.capacity() *    4,vertexBuffer,GLES20.GL_STATIC_DRAW);

这一切都进入了一些加载时间,并引用了buffer[],除了你应该添加GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);,这个调用所做的是unbind缓冲区。这不是您需要做的事情,但最好这样做,这样您就不会混淆绑定的缓冲区(如果有)。

在调用glVertexAttribPointer 之前,您需要绑定缓冲区。然后如上所述设置最后一个参数。

在你使用完这个缓冲区(完成绘图)后,你应该(同样没有必要)解除绑定。

【讨论】:

当我输入 0 而不是 vertexBuffer 时,我收到一条错误消息,提示我没有提供必要的参数:expected parameter: ptr: java.nio.Buffer; actual arguments: 0(int) @Matik 我编辑了代码,试图记住你所说的话,除了glVertexAttribPointer 中的 0,我仍然不明白如何使它工作 正如我所说,尝试对它进行类型转换之类的。找一个例子,因为我不习惯在 java 中创建它。在 C/C++ 中,这非常简单…… 我的问题是我使用了最低 API 级别 8,其中 glVertexAttribPointer 具有不同的参数。在我将最低 API 设置为 14 并以 20 为目标后,它就可以工作了。

以上是关于Android 上的 OpenGL ES 2:如何使用 VBO的主要内容,如果未能解决你的问题,请参考以下文章

如何确定Android OpenGL ES上的最大纹理内存

Android设备上的OpenGL ES 2.0扩展[关闭]

在 Android 上的 OpenGL ES 2.0 中使用 VBO/IBO

使用 Android 上的 OpenGL ES 2.0,在帧缓冲区上渲染时是不是可以保留多重采样?

如何在Android上使用OpenGL ES 2.0绘制点

如何在Android上将OpenGL ES 1.0代码转换为OpenGL Es 2.0?