I am looking for help with understanding VBOs. I have done a ton of research and have found tutorials on the subject, but they are still vague to me. I have a few questions:
Where should a VBO be created, and how should I create one?
I am currently using the code right below to initialize my vertex and index buffers:
vertices = new float[]
{
p[0].x, p[0].y, 0.0f,
p[1].x, p[1].y, 0.0f,
p[2].x, p[2].y, 0.0f,
p[3].x, p[3].y, 0.0f,
};
// The order of vertex rendering for a quad
indices = new short[] {0, 1, 2, 0, 2, 3};
ByteBuffer bb = ByteBuffer.allocateDirect(vertices.length * 4);
bb.order(ByteOrder.nativeOrder());
vertexBuffer = bb.asFloatBuffer();
vertexBuffer.put(vertices);
vertexBuffer.position(0);
ByteBuffer dlb = ByteBuffer.allocateDirect(indices.length * 2);
dlb.order(ByteOrder.nativeOrder());
drawListBuffer = dlb.asShortBuffer();
drawListBuffer.put(indices);
drawListBuffer.position(0);
If I am correct, this is not creating a VBO. So, if I wanted to make a VBO, would the code to create a VBO go right after the code listed above? If so, how would it be created?
Also, how is a VBO rendered and drawn to screen?
Is it rendered and drawn the same way as just using vertex and index arrays? If not, what is the process? Currently, I render and draw my objects as shown in the code below:
GLES20.glUseProgram(GraphicTools.sp_SolidColor);
mPositionHandle =
GLES20.glGetAttribLocation(GraphicTools.sp_SolidColor, "vPosition");
GLES20.glEnableVertexAttribArray(mPositionHandle);
GLES20.glVertexAttribPointer(mPositionHandle, 3,
GLES20.GL_FLOAT, false,
0, vertexBuffer);
mtrxHandle = GLES20.glGetUniformLocation(GraphicTools.sp_SolidColor,
"uMVPMatrix");
GLES20.glUniformMatrix4fv(mtrxHandle, 1, false, m, 0);
GLES20.glDrawElements(GLES20.GL_TRIANGLES, indices.length,
GLES20.GL_UNSIGNED_SHORT, drawListBuffer);
GLES20.glDisableVertexAttribArray(mPositionHandle);
If you have any questions, let me know. Thanks in advance.
A Vertex Buffer Object is a buffer where vertex array data can be stored. The data are uploaded one time to the graphics memory (GPU) and can be used repeatedly to draw a mesh.
First you have to create 2 buffer objects, one for the vertices and one for the indices:
int buffers[] = new int[2];
GLES20.glGenBuffers(2, buffers, 0);
int vbo = buffers[0];
int ibo = buffers[1];
Then you have to bind the buffer and to transfer the data
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vbo);
GLES20.glBufferData(
GLES20.GL_ARRAY_BUFFER,
vertexBuffer.capacity() * 4, // 4 = bytes per float
vertexBuffer,
GLES20.GL_STATIC_DRAW);
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, ibo);
GLES20.glBufferData(
GLES20.GL_ELEMENT_ARRAY_BUFFER,
drawListBuffer.capacity() * 2, // 2 = bytes per short
drawListBuffer,
GLES20.GL_STATIC_DRAW);
GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, 0);
If you want to draw the mesh, then you have to define the array of generic vertex attribute data and you have to bind the index buffer, but you don't have to transfer any data to the GPU:
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vbo);
GLES20.glVertexAttribPointer(
mPositionHandle, 3,
GLES20.GL_FLOAT, false,
0, 0); // <----- 0, because "vbo" is bound
GLES20.glEnableVertexAttribArray(mPositionHandle);
GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, ibo);
GLES20.glDrawElements(
GLES20.GL_TRIANGLES, indices.length,
GLES20.GL_UNSIGNED_SHORT, 0); // <----- 0, because "ibo" is bound
GLES20.glDisableVertexAttribArray(mPositionHandle);
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, 0);
See also An Introduction to Vertex Buffer Objects (VBOs)
Related
I am trying to render a model by importing the .obj File. Im successfully importing and preparing the data to be rendered in FloatBuffers/IntegerBuffers but cant manage to render it. Can someone help me?
This is the class that renders and initializes the VBOs
private int vertexBufferID;
private int indexBufferID;
private int numberIndices;
public void init() {
try{
InputStream objInputStream = new FileInputStream("./res/obj/Terrain.obj");
Obj obj = ObjReader.read(objInputStream);
obj = ObjUtils.convertToRenderable(obj);
IntBuffer indices = ObjData.getFaceVertexIndices(obj, 3);
FloatBuffer vertices = ObjData.getVertices(obj);
// FloatBuffer texCoords = ObjData.getTexCoords(obj, 2);
// FloatBuffer normals = ObjData.getNormals(obj);
vertexBufferID = GL15.glGenBuffers();
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, vertexBufferID);
GL15.glBufferData(GL15.GL_ARRAY_BUFFER, vertices, GL15.GL_STATIC_DRAW);
GL20.glVertexAttribPointer(0, 3, GL11.GL_FLOAT, false, 0, 0);
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
GL30.glBindVertexArray(0);
indexBufferID = GL15.glGenBuffers();
GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, indexBufferID);
GL15.glBufferData(GL15.GL_ELEMENT_ARRAY_BUFFER, indices, GL15.GL_STATIC_DRAW);
GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, 0);
}catch(IOException e)
{
e.printStackTrace();
}
}
public void render() {
GL11.glEnableClientState(GL11.GL_VERTEX_ARRAY);
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, vertexBufferID);
GL11.glVertexPointer(3, GL11.GL_FLOAT, 0, 0);
GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, indexBufferID);
GL11.glDrawElements(GL11.GL_TRIANGLES, numberIndices, GL11.GL_UNSIGNED_INT, 0);
}
I get no error code the object is just not rendered. I am able to render the Object using Display Lists.
I've now solved the problem and am now able to import .obj-Files and render them using vbo/vao. Im using "Obj - a simple Wavefront OBJ file loader and writer" to prepare the data to be rendered.
The code to load up and prepare the .obj file
InputStream objInputStream = new FileInputStream(pathTObjFile);
Obj obj = ObjReader.read(objInputStream);
obj = ObjUtils.convertToRenderable(obj);
IntBuffer indices = ObjData.getFaceVertexIndices(obj, 3);
FloatBuffer vertices = ObjData.getVertices(obj);
Now the data is stored in 2 VBOs and the VBO for the vertices is stored in a VAO:
vaoId = GL30.glGenVertexArrays();
The Vertex Array Object ID (vaoID) is an Integer that is called if we are calling the VAO.
GL30.glBindVertexArray(vaoId);
To be able to do anything with the VAO we have to bind or "select" it.
vboId = GL15.glGenBuffers();
The VBOs also have an ID to be called with. We are creating the Vertex Buffer Object ID here (vboId)
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, vboId);
GL15.glBufferData(GL15.GL_ARRAY_BUFFER, vertices, GL15.GL_STATIC_DRAW);
Now we select the VBO and store the vertices in it.
GL20.glVertexAttribPointer(0, 3, GL11.GL_FLOAT, false, 0, 0);
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
GL30.glBindVertexArray(0);
First we put the VBO in the first attribute list of the VAO (a VAO has 16 lists), then we unbind the VAO and the VBO.
vboiId = GL15.glGenBuffers();
GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, vboiId);
GL15.glBufferData(GL15.GL_ELEMENT_ARRAY_BUFFER, indices, GL15.GL_STATIC_DRAW);
GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, 0);
indicesCount = indices.capacity();
At the end of the Initialization we have create the vbo for the indices so the Vertices can be connected to each others to create faces in the right way. Therefor we bind the vboiId and store the index data inside. Then we unbind the VBO again and save the number of Indices stored in the VBO because we have to tell it OpenGL to be able to render it later.
Rendering the Object:
GL30.glBindVertexArray(vaoId);
GL20.glEnableVertexAttribArray(0);
GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, vboiId);
To Render the Object we have to bind the VAO and the VBO inside. We also have to select the VBO for the Indices. That is done by the code snippet above.
GL11.glDrawElements(GL11.GL_TRIANGLES, indicesCount, GL11.GL_UNSIGNED_INT, 0);
Now we draw the Object and
GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, 0);
GL20.glDisableVertexAttribArray(0);
GL30.glBindVertexArray(0);
Deselect the VBOs and VAOs again.
Here is the whole code combined
private int vboiId;
private int vaoId;
private int vboId;
private int indicesCount;
public void init() {
try{
InputStream objInputStream =
new FileInputStream("./res/obj/Terrain.obj");
Obj obj = ObjReader.read(objInputStream);
obj = ObjUtils.convertToRenderable(obj);
IntBuffer indices = ObjData.getFaceVertexIndices(obj, 3);
FloatBuffer vertices = ObjData.getVertices(obj);
FloatBuffer texCoords = ObjData.getTexCoords(obj, 2);
FloatBuffer normals = ObjData.getNormals(obj);
indicesCount = indices.capacity();
vaoId = GL30.glGenVertexArrays();
GL30.glBindVertexArray(vaoId);
vboId = GL15.glGenBuffers();
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, vboId);
GL15.glBufferData(GL15.GL_ARRAY_BUFFER, vertices, GL15.GL_STATIC_DRAW);
GL20.glVertexAttribPointer(0, 3, GL11.GL_FLOAT, false, 0, 0);
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
GL30.glBindVertexArray(0);
vboiId = GL15.glGenBuffers();
GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, vboiId);
GL15.glBufferData(GL15.GL_ELEMENT_ARRAY_BUFFER, indices, GL15.GL_STATIC_DRAW);
GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, 0);
}catch(IOException e)
{
e.printStackTrace();
}
}
public void render() {
GL11.glClear(GL11.GL_COLOR_BUFFER_BIT);
GL30.glBindVertexArray(vaoId);
GL20.glEnableVertexAttribArray(0);
GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, vboiId);
GL11.glDrawElements(GL11.GL_TRIANGLES, indicesCount, GL11.GL_UNSIGNED_INT, 0);
GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, 0);
GL20.glDisableVertexAttribArray(0);
GL30.glBindVertexArray(0);
}
For further and more detailed information i strongly recommend u to read The quad with Draw Array, The Quad with Draw Elements and if you want to go more in detail look up the OpenGL 3.2 and above tutorials on the Main Page of the LWJGL Wiki.
For information on how to use the Obj loader I used look up The sample projects using this loader
My code basically consists of a mixture of the code on this websites.
Edit: If you try to render multiple Objects you have to Clear the Color before rendering the first object (First line of the Render method)
I am trying to use VAOs to render and am having trouble with incorrect rendering.
I do have a little experience in the subject but not a huge amount.
This is my binding Code:
//RawModel is just a type to store vao id and number of indices.
public RawModel loadToVao(float[] positions, int[] indices){
//create VAO and bind it
int vaoID = GL30.glGenVertexArrays();
GL30.glBindVertexArray(vaoID);
//create index buffer
int indexVBO = GL15.glGenBuffers();
GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, indexVBO);
IntBuffer indexBuffer = BufferUtils.createIntBuffer(indices.length);
indexBuffer.put(indices);
indexBuffer.flip();
GL15.glBufferData(GL15.GL_ELEMENT_ARRAY_BUFFER, indexBuffer , GL15.GL_STATIC_DRAW);
//create vertex buffer
int positionVBO = GL15.glGenBuffers();
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, positionVBO);
FloatBuffer vertBuffer = BufferUtils.createFloatBuffer(positions.length);
vertBuffer.put(positions);
vertBuffer.flip();
GL15.glBufferData(GL15.GL_ARRAY_BUFFER, vertBuffer , GL15.GL_STATIC_DRAW);
GL20.glVertexAttribPointer(0, 3, GL11.GL_FLOAT, false, 0,0);
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
GL30.glBindVertexArray(0);
return new RawModel(vaoID, indices.length);
}
This is my render code:
GL30.glBindVertexArray(obj.getVaoID());
GL20.glEnableVertexAttribArray(0);
GL11.glDrawArrays(GL11.GL_POINTS, 0, sphere.getRawModel().getSize());
GL11.glDrawArrays(GL11.GL_TRIANGLES, 0, sphere.getRawModel().getSize());
GL20.glDisableVertexAttribArray(0);
GL30.glBindVertexArray(0);
I render both points and Triangles just to see what is going wrong. eventually will only be triangles.
This code always seems to give me a point at the objects centre which should not be there. It also has incorrect grouping of elements so triangles render completely wrong.
If anyone could help it would be great.
Thanks
I am trying to render large VBO array objects, containing ~700000 values and I have ~1500000 values in my element_array buffer. But what I am getting is a blank screen. On the other hand if I just use only the VAO, my code works correctly. My code is as follows:
//Data buffers
FloatBuffer vertexBuffer = GLBuffers.newDirectFloatBuffer(this.coordCount);
vertexBuffer.put(Vertices);
vertexBuffer.rewind();
IntBuffer indexBuffer = GLBuffers.newDirectIntBuffer(this.indexCount);
indexBuffer.put(index);
indexBuffer.rewind();
//setting up the VBO
int nVBO = 2;
int[] VBO = new int[nVBO];
gl.glGenBuffers(nVBO, VBO,0);
gl.glBindBuffer(GL.GL_ARRAY_BUFFER, VBO[0]);
gl.glBindBuffer(GL.GL_ELEMENT_ARRAY_BUFFER,VBO[1]);
gl.glBufferData(GL.GL_ARRAY_BUFFER, this.coordCount*Float.SIZE, vertexBuffer, GL.GL_STATIC_DRAW);
gl.glBufferData(GL.GL_ELEMENT_ARRAY_BUFFER, this.indexCount*Integer.SIZE, indexBuffer, GL.GL_STATIC_DRAW);
gl.glBindBuffer(GL.GL_ARRAY_BUFFER, VBO[0]);
gl.glBindBuffer(GL.GL_ELEMENT_ARRAY_BUFFER, VBO[1]);
gl.glEnableClientState(GLPointerFunc.GL_VERTEX_ARRAY);
gl.glVertexPointer(3, GL.GL_FLOAT, 0, 0);
//gl.glVertexPointer(3, GL.GL_FLOAT, 0, vertexBuffer);
//gl.glDrawArrays(GL.GL_TRIANGLE_STRIP, 0, 4);
gl.glDrawElements(GL.GL_TRIANGLES, this.indexCount, GL.GL_UNSIGNED_INT, 0);
//gl.glDrawElements(GL.GL_TRIANGLES, this.indexCount, GL.GL_UNSIGNED_INT, indexBuffer);
gl.glDisableClientState(GLPointerFunc.GL_VERTEX_ARRAY);
gl.glBindBuffer(GL.GL_ARRAY_BUFFER, 0);
gl.glBindBuffer(GL.GL_ELEMENT_ARRAY_BUFFER, 0);
Any clues/suggestions on how I can fix this?
Is this code block called each time when the loop runs? If it is, you should divide your vbo phases into different blocks.This sample code may help you how to divide. (In the link that I post, you should just focus initVBO and renderVbo functions) I guess your initializing your vbo's sequentially, which it may make your program unresponsive.
I've been following the tutorials http://www.learnopengles.com and learning more how to work with OpenGL ES but I'm having a hard time trying to get a sphere to show up.
I've went ahead and made a geodesic sphere in Blender and imported the vertices and the draw order but I whenever I call the sphere the app crashes.
I'll include a link to my full render file but I'll also point out what I think some of the key things where I think problems might be:
Here is where the buffers are created. I'm not sure if the app has issues with the way I'm buffering the sphere or the sphere's draw order points.
// Initialize the buffers.
mCubePositions = ByteBuffer.allocateDirect(cubePositionData.length * mBytesPerFloat)
.order(ByteOrder.nativeOrder()).asFloatBuffer();
mCubePositions.put(cubePositionData).position(0);
mSpherePositions = ByteBuffer.allocateDirect(spherePositionData.length * 4).order(ByteOrder.nativeOrder()).asFloatBuffer();
mSpherePositions.put(spherePositionData).position(0);
ByteBuffer dlb = ByteBuffer.allocateDirect(sphereDrawOrder.length * 2).order(ByteOrder.nativeOrder());
drawListBuffer = dlb.asShortBuffer();
drawListBuffer.put(sphereDrawOrder);
drawListBuffer.position(0);
mCubeColors = ByteBuffer.allocateDirect(cubeColorData.length * mBytesPerFloat)
.order(ByteOrder.nativeOrder()).asFloatBuffer();
mCubeColors.put(cubeColorData).position(0);
mCubeNormals = ByteBuffer.allocateDirect(cubeNormalData.length * mBytesPerFloat)
.order(ByteOrder.nativeOrder()).asFloatBuffer();
mCubeNormals.put(cubeNormalData).position(0);
And here is the class that draws the sphere. I have no colors or normals for the sphere so I just removed those parts. Is that what's causing it to freak out?
private void drawSphere()
{
// Pass in the position information
mSpherePositions.position(0);
GLES20.glVertexAttribPointer(mPositionHandle, mPositionDataSize, GLES20.GL_FLOAT, false,
0, mSpherePositions);
GLES20.glEnableVertexAttribArray(mPositionHandle);
// Pass in the color information
// Pass in the normal information
// This multiplies the view matrix by the model matrix, and stores the result in the MVP matrix
// (which currently contains model * view).
Matrix.multiplyMM(mMVPMatrix, 0, mViewMatrix, 0, mModelMatrix, 0);
// Pass in the modelview matrix.
GLES20.glUniformMatrix4fv(mMVMatrixHandle, 1, false, mMVPMatrix, 0);
// This multiplies the modelview matrix by the projection matrix, and stores the result in the MVP matrix
// (which now contains model * view * projection).
Matrix.multiplyMM(mMVPMatrix, 0, mProjectionMatrix, 0, mMVPMatrix, 0);
// Pass in the combined matrix.
GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mMVPMatrix, 0);
// Pass in the light position in eye space.
GLES20.glUniform3f(mLightPosHandle, mLightPosInEyeSpace[0], mLightPosInEyeSpace[1], mLightPosInEyeSpace[2]);
// Draw the sphere.
GLES20.glDrawElements(GLES20.GL_TRIANGLES, sphereDrawOrder.length,
GLES20.GL_UNSIGNED_SHORT, drawListBuffer);
}
Whenever it crashes it tells me there's something wrong with the code after //Draw the sphere. It doesn't like the glDrawElements for some reason.
Here is the full render script for reference: http://pastebin.com/Y1WU27hz
If you have any insight to this I thank you.
Is it possible to use per vertex data interleaved in OpenGL ES on Android?
I'm unable to get the correct offset pointers for the normal and color members.
In C++ I would do something like this:
struct ColoredVertexData3D{
Vertex3D vertex;
Vector3D normal;
ColorRGBA color;
};
const ColoredVertexData3D vertexData[] =
{
{
{0.0f, 0.5f, 0.8f}, // Vertex |
{0.0f, 0.4f, 0.6f}, // Normal | Vertex 0
{1.0f, 0.0f, 0.0f, 1.0f} // Color |
},
{
{0.8f, 0.0f, 0.5f}, // Vertex |
{0.6f, 0.0f, 0.4f}, // Normal | Vertex 1
{1.0f, 0.5f, 0.0f, 1.0f} // Color |
},
// ... more vertexes.
};
const int stride = sizeof(ColoredVertexData3D);
glVertexPointer(3, GL_FLOAT, stride, &vertexData[0].vertex);
glColorPointer(4, GL_FLOAT, stride, &vertexData[0].color);
glNormalPointer(GL_FLOAT, stride, &vertexData[0].normal);
Is the same thing possible on Android in Java? This is what I currently got:
ByteBuffer vertexData = ...;
int stride = 40;
gl.glVertexPointer(3, GL10.GL_FLOAT, stride, vertexData);
// This obviously doesn't work. ------------v
gl.glColorPointer(4, GL10.GL_FLOAT, stride, &vertexData[0].color);
gl.glNormalPointer(GL10.GL_FLOAT, stride, &vertexData[0].normal);
The basic idea is to call duplicate() on the ByteBuffer, which creates a new ByteBuffer that shares the underlying storage but allows you to start at a different position.
Here's what worked for me:
FloatBuffer verticesNormals;
// ... code to initialize verticesNormals ...
// verticesNormals contains 6 floats for each vertex. The first three
// define the position and the next three define the normal.
gl.glVertexPointer(3, gl.GL_FLOAT, 24, verticesNormals);
// Create a buffer that points 3 floats past the beginning.
FloatBuffer normalData = mVerticesNormals.duplicate();
normalData.position(3);
gl.glNormalPointer(gl.GL_FLOAT, 24, normalData);