未能在 OpenGL/Android 中使用 VBO
Posted
技术标签:
【中文标题】未能在 OpenGL/Android 中使用 VBO【英文标题】:Failing to use VBOs in OpenGl/Android 【发布时间】:2016-07-21 23:19:06 【问题描述】:问候我的程序员伙伴,
我已经搜索了 WEB,在网上查看了示例,但仍然无法弄清楚。很抱歉,如果之前有人问过这个问题,经过一周的调试,我很累。我希望你能帮助我。
基本上问题是我尝试绘制一些四边形(带三角形)但没有绘制任何内容。以前我在没有 VBO 的情况下按照 android 官方网站上的“三角形示例”中描述的方式进行绘制。一切正常,但我认为在 Renderer.OnDrawFrame() 中更新顶点/索引缓冲区效率不高:)
这是我的代码:
public class FloorPlanRenderer implements GLSurfaceView.Renderer
public volatile float mAngle;
// mMVPMatrix is an abbreviation for "Model View Projection Matrix"
private final float[] mMVPMatrix = new float[16];
private final float[] mProjectionMatrix = new float[16];
private final float[] mViewMatrix = new float[16];
private final float[] mRotationMatrix = new float[16];
private GLSurfaceView mGlView;
private GlEngine mGlEngine;
private boolean dataSet = false;
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config)
// Set the background frame color
GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
// Initialize the accumulated rotation matrix
Matrix.setIdentityM(mRotationMatrix, 0);
// Position the eye in front of the origin.
final float eyeX = 0.0f;
final float eyeY = 0.0f;
final float eyeZ = -3.0f;
// We are looking toward the distance
final float lookX = 0.0f;
final float lookY = 0.0f;
final float lookZ = 0.0f; //-5.0f;
// Set our up vector. This is where our head would be pointing were we holding the camera.
final float upX = 0.0f;
final float upY = 1.0f;
final float upZ = 0.0f;
// Set the view matrix. This matrix can be said to represent the camera position.
Matrix.setLookAtM(mViewMatrix, 0, eyeX, eyeY, eyeZ, lookX, lookY, lookZ, upX, upY, upZ);
mGlEngine = new GlEngine(10);
mGlEngine.registerQuad(new Wall(-0.5f, 0.4f, -0.2f, 0.4f));
mGlEngine.registerQuad(new Wall(0.5f, 0.4f, 0.2f, 0.4f));
mGlEngine.registerQuad(new Wall(0.0f, 0.0f, 0.0f, 0.3f, 0.02f));
@Override
public void onSurfaceChanged(GL10 unused, int width, int height)
GLES20.glViewport(0, 0, width, height);
// Create a new perspective projection matrix. The height will stay the same
// while the width will vary as per aspect ratio.
final float ratio = (float) width / height;
final float left = -ratio;
final float right = ratio;
final float bottom = -1.0f;
final float top = 1.0f;
final float near = 3.0f;
final float far = 7.0f;
// this projection matrix is applied to object coordinates
// in the onDrawFrame() method
Matrix.frustumM(mProjectionMatrix, 0, left, right, bottom, top, near, far);
@Override
public void onDrawFrame(GL10 gl)
float[] scratch = new float[16];
// Calculate the projection and view transformation
Matrix.multiplyMM(mMVPMatrix, 0, mProjectionMatrix, 0, mViewMatrix, 0);
Matrix.setRotateM(mRotationMatrix, 0, mAngle, 0, 0, 1.0f);
// Combine the rotation matrix with the projection and camera view
// Note that the mMVPMatrix factor *must be first* in order
// for the matrix multiplication product to be correct.
Matrix.multiplyMM(scratch, 0, mMVPMatrix, 0, mRotationMatrix, 0);
mGlEngine.render(scratch);
GlEngine 类:
public class GlEngine
public static final int COORDS_PER_VERTEX = 3;
public static final int ORDER_INDICES_PER_QUAD = 6;
public static final int VERTICES_PER_QUAD = 4;
public static final int SIZE_OF_FLOAT = Float.SIZE/Byte.SIZE;
public static final int SIZE_OF_SHORT = Short.SIZE/Byte.SIZE;
private int mQuadsNum = 0;
private int mLastCoordsIndex = 0;
private int mLastOrderIndex = 0;
private final FloatBuffer vertexBuffer;
private final ShortBuffer indexBuffer;
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;" +
"";
// Use to access and set the view transformation
private int mMVPMatrixHandle;
private final String fragmentShaderCode =
"precision mediump float;" +
"uniform vec4 vColor;" +
"void main() " +
" gl_FragColor = vColor;" +
"";
private final int mProgram;
private int mPositionHandle;
private int mColorHandle;
private final int vertexStride = COORDS_PER_VERTEX * 4; // 4 bytes per vertex
float color[] = 0.63671875f, 0.76953125f, 0.22265625f, 0.0f ;
private boolean mDataInitNeeded = true;
public GlEngine(int quadsNum)
ByteBuffer bb = ByteBuffer.allocateDirect(quadsNum * VERTICES_PER_QUAD *
COORDS_PER_VERTEX * SIZE_OF_FLOAT);
bb.order(ByteOrder.nativeOrder()); // device hardware's native byte order
vertexBuffer = bb.asFloatBuffer();
ByteBuffer bb2 = ByteBuffer.allocateDirect(quadsNum *
ORDER_INDICES_PER_QUAD * SIZE_OF_SHORT);
bb2.order(ByteOrder.nativeOrder());
indexBuffer = bb2.asShortBuffer();
int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER,
vertexShaderCode);
int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER,
fragmentShaderCode);
mProgram = GLES20.glCreateProgram();
GLES20.glAttachShader(mProgram, vertexShader);
GLES20.glAttachShader(mProgram, fragmentShader);
GLES20.glLinkProgram(mProgram);
public static int loadShader(int type, String shaderCode)
// create a vertex shader type (GLES20.GL_VERTEX_SHADER)
// or a fragment shader type (GLES20.GL_FRAGMENT_SHADER)
int shader = GLES20.glCreateShader(type);
// add the source code to the shader and compile it
GLES20.glShaderSource(shader, shaderCode);
GLES20.glCompileShader(shader);
return shader;
public void registerQuad(Wall quad)
quad.putCoords(vertexBuffer);
quad.putIndices(indexBuffer);
mQuadsNum++;
// This code is dealing with VBO side of things
private final int[] mVerticesBufferId = new int[BUFFERS_COUNT];
private final int[] mIndicesBufferId = new int[BUFFERS_COUNT];
private static final int BUFFERS_COUNT = 1;
public void copyToGpu(FloatBuffer vertices)
GLES20.glGenBuffers(BUFFERS_COUNT, mVerticesBufferId, 0);
// Copy vertices data into GPU memory
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mVerticesBufferId[0]);
GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, vertices.capacity() * SIZE_OF_FLOAT, vertices, GLES20.GL_STATIC_DRAW);
// Cleanup buffer
vertices.limit(0);
vertices = null;
public void copyToGpu(ShortBuffer indices)
GLES20.glGenBuffers(BUFFERS_COUNT, mIndicesBufferId, 0);
// Copy vertices data into GPU memory
GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, mIndicesBufferId[0]);
GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, indices.capacity() * SIZE_OF_SHORT, indices, GLES20.GL_STATIC_DRAW);
// Cleanup buffer
indices.limit(0);
indices = null;
public void render(float[] mvpMatrix)
setData();
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mVerticesBufferId[0]);
GLES20.glUseProgram(mProgram);
mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");
GLES20.glEnableVertexAttribArray(mPositionHandle);
GLES20.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, vertexStride, 0);
mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor");
GLES20.glUniform4fv(mColorHandle, 1, color, 0);
// get handle to shape's transformation matrix
mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
// Pass the projection and view transformation to the shader
GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mvpMatrix, 0);
GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, mIndicesBufferId[0]);
// Draw quads
GLES20.glDrawElements(
GLES20.GL_TRIANGLES, mQuadsNum * ORDER_INDICES_PER_QUAD,
GLES20.GL_UNSIGNED_SHORT, 0);
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, 0);
// This method is called on gl thread GlSurfaceView.queueEvent(...)
public void setData()
if (mDataInitNeeded)
// Reset positions of buffers for consuming in GL
vertexBuffer.position(0);
indexBuffer.position(0);
copyToGpu(vertexBuffer);
copyToGpu(indexBuffer);
mDataInitNeeded = false;
public void deallocateGlBuffers()
if (mVerticesBufferId[0] > 0)
GLES20.glDeleteBuffers(mVerticesBufferId.length, mVerticesBufferId, 0);
mVerticesBufferId[0] = 0;
if (mIndicesBufferId[0] > 0)
GLES20.glDeleteBuffers(mIndicesBufferId.length, mIndicesBufferId, 0);
mIndicesBufferId[0] = 0;
表示矩形的 Wall 类:
public class Wall
// number of coordinates per vertex in this array
private static final int COORDS_PER_VERTEX = 3;
private static final int VERTICES_NUM = 4; // it's a rect after all
private static final float DEFAULT_WIDTH = 0.05f;
private static final float DEFAULT_COORDS_SOURCE = 0.5f;
private final float mCoords[] = new float[COORDS_PER_VERTEX * VERTICES_NUM];
private final short mDrawOrder[] = 0, 1, 2, // first triangle
1, 2, 3 ; // second triangle
private int mVertexBufferPosition;
private int mIndexBufferPosition;
private final PointF mA = new PointF(0, 0);
private final PointF mB = new PointF(0, 0);
private float mWidth;
public Wall()
init(-DEFAULT_COORDS_SOURCE, DEFAULT_COORDS_SOURCE, DEFAULT_COORDS_SOURCE,
-DEFAULT_COORDS_SOURCE, DEFAULT_WIDTH);
public Wall(float x1, float y1, float x2, float y2)
init(x1, y1, x2, y2, DEFAULT_WIDTH);
public Wall(float x1, float y1, float x2, float y2, float width)
init(x1, y1, x2, y2, width);
private void init(float x1, float y1, float x2, float y2, float width)
mA.x = x1;
mA.y = y1;
mB.x = x2;
mB.y = y2;
mWidth = width;
calcCoords();
private void calcCoords()
float[] vector = mA.x - mB.x, mA.y - mB.y;
float magnitude = (float) Math.sqrt(vector[0]*vector[0] + vector[1]*vector[1]);
float[] identityVector = vector[0]/magnitude, vector[1]/magnitude;
float[] orthogonalIdentityVector = identityVector[1], -identityVector[0];
mCoords[0] = mA.x + mWidth * orthogonalIdentityVector[0];
mCoords[1] = mA.y + mWidth * orthogonalIdentityVector[1];
mCoords[3] = mA.x - mWidth * orthogonalIdentityVector[0];
mCoords[4] = mA.y - mWidth * orthogonalIdentityVector[1];
mCoords[6] = mB.x + mWidth * orthogonalIdentityVector[0];
mCoords[7] = mB.y + mWidth * orthogonalIdentityVector[1];
mCoords[9] = mB.x - mWidth * orthogonalIdentityVector[0];
mCoords[10] = mB.y - mWidth * orthogonalIdentityVector[1];
public void putCoords(FloatBuffer vertexBuffer)
mVertexBufferPosition = vertexBuffer.position();
for (int i = 0; i < mDrawOrder.length; i++)
mDrawOrder[i] += mVertexBufferPosition/GlEngine.COORDS_PER_VERTEX;
vertexBuffer.put(mCoords);
public void putIndices(ShortBuffer indexBuffer)
mIndexBufferPosition = indexBuffer.position();
indexBuffer.put(mDrawOrder);
public float getWidth()
return mWidth;
public void setWidth(float mWidth)
this.mWidth = mWidth;
public PointF getA()
return mA;
public void setA(float x, float y)
this.mA.x = x;
this.mA.y = y;
public PointF getB()
return mB;
public void setB(float x, float y)
this.mB.x = x;
this.mB.y = y;
在 Wall 类中,我保存了放置其顶点和索引的偏移量,因为该类将来会发生变化,并且它打算在主缓冲区中更新其顶点(缓冲区不会为每个 OnDrawFrame 重新编译)。
谢谢。我希望在您的帮助下,我将在通往 OpenGL ES 的道路上克服这个(另一个)障碍。
【问题讨论】:
我会尝试将“near”值设置得更小一些。现在,您的几何图形正好在近裁剪平面上。 能否请您指出代码中的确切行?final float near = 3.0f;
【参考方案1】:
真丢脸!我偶然将索引放入错误的数组中。而不是这个:
// Copy vertices data into GPU memory
GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, mIndicesBufferId[0]);
GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, indices.capacity() * SIZE_OF_SHORT, indices, GLES20.GL_STATIC_DRAW);
应该有:
// Copy vertices data into GPU memory
GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, mIndicesBufferId[0]);
GLES20.glBufferData(GLES20.GL_ELEMENT_ARRAY_BUFFER, indices.capacity() * SIZE_OF_SHORT, indices, GLES20.GL_STATIC_DRAW);
为什么感到羞耻?因为在日志中我看到了:
07-23 16:20:05.442 5170-5264/com.example.neutrino.maze W/Adreno-ES20: : GL_INVALID_OPERATION
就在第二次调用 glBufferData 之后,我在其中放置了 GL_ARRAY_BUFFER
而不是 GL_ELEMENT_ARRAY_BUFFER
。在许多情况下,这肯定是由复制粘贴引起的。
【讨论】:
以上是关于未能在 OpenGL/Android 中使用 VBO的主要内容,如果未能解决你的问题,请参考以下文章
在openGl android(Wear)中绘制基于图像的纹理
VB.NET编好程序后一运行就提示生成错误,重装VB好几遍了还是不行