【问题标题】:OpenGL ES 2 on Android: how to use VBOsAndroid 上的 OpenGL ES 2:如何使用 VBO
【发布时间】:2023-03-20 08:34:01
【问题描述】:

这个问题类似于我在这里问的问题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 guidethis 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)

【问题讨论】:

    标签: android opengl-es-2.0 vbo


    【解决方案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 为目标后,它就可以工作了。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-09
    • 1970-01-01
    相关资源
    最近更新 更多