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 2.0扩展[关闭]
在 Android 上的 OpenGL ES 2.0 中使用 VBO/IBO