Right now it seems to me that my interleaved VBO is strictly ' read-only ' but I want to update it every frame (preferrably from GLSL).
I have a planet that moves around in an orbit, the code below is for rendering points of the orbit.
Problem outline:
I want each point on that orbit to have its own "lifetime", logic:
when the planet passes each consecutive point? update lifetime to 1.0 and reduce with time!
This will be used to create a fading orbitpath of each moving object. Right now Im just looking for ways to manipulate the vbo...
How can I read AND write within GLSL to and from a VBO ? Can anyone post example please?
Update: I modified the code above to work with transform feedback (suggested by user Andon M. Coleman) but I think I might be doing something wrong (I get glError):
Setup:
// Initialize and upload to graphics card
glGenVertexArrays(1, &_vaoID);
glGenBuffers(1, &_vBufferID);
glGenBuffers(1, &_iBufferID);
glGenBuffers(1, &_tboID);
// First VAO setup
glBindVertexArray(_vaoID);
glBindBuffer(GL_ARRAY_BUFFER, _vBufferID);
glBufferData(GL_ARRAY_BUFFER, _vsize * sizeof(Vertex), _varray, GL_DYNAMIC_DRAW);
// TRANSFORM FEEDBACK
glBindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, _tboID); // Specify buffer
// Allocate space without specifying data
glBufferData(GL_TRANSFORM_FEEDBACK_BUFFER,
_vsize*sizeof(Vertex), NULL, GL_DYNAMIC_COPY);
// Tell OGL which object to store the results of transform feedback
glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, _vBufferID); //correct?
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE,
sizeof(Vertex), reinterpret_cast<const GLvoid*>(offsetof(Vertex, location)));
glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE,
sizeof(Vertex), reinterpret_cast<const GLvoid*>(offsetof(Vertex, velocity)));
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _iBufferID);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, _isize * sizeof(int), _iarray, GL_STREAM_DRAW);
render method():
//disable fragment, so that we do a first run with feedback
glEnable(GL_RASTERIZER_DISCARD);
glBindVertexArray(_vaoID);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _iBufferID);
glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, _tboID);
glBeginTransformFeedback(_mode);
glDrawElements(_mode, _isize, GL_UNSIGNED_INT, 0);
glEndTransformFeedback();
glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, 0);
glBindVertexArray(0);
glDisable(GL_RASTERIZER_DISCARD);
// then i attempt to do the actual draw
glBindVertexArray(_vaoID);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _iBufferID);
glDrawElements(_mode, _isize, GL_UNSIGNED_INT, 0);
glBindVertexArray(0);
And - right before linking:
const GLchar* feedbackVaryings[] = { "point_position" };
glTransformFeedbackVaryings(_ephemerisProgram->getProgramID(), 1, feedbackVaryings, GL_INTERLEAVED_ATTRIBS);
you can not change the content of your VBO from the rendering pipline of Opengl, but you can use tricks to update them depending on the time, also if you are using Opengl 4.4 you can use ComputeShaders but it's a little bit complicated to explain it in here, hust google for it, good luck.
How can I read AND write within GLSL to and from a VBO?
You can't. VBOs are strictly readonly from the normal rendering shaders. Modification is not possible at all (because that would open an infathomable deep barrel of worms) but using transformation feedback the results of the shader stages can be written into a buffer.
Or you use compute shaders.
Problem outline: I want each point on that orbit to have its own "lifetime", logic: When the planet passes each consecutive point? update lifetime to 1.0 and reduce with time!
Sound like a task for a compute shader. But honestly I don't think there's much to gain from processing this on a GPU.
Related
Ok after some timeout I am continuing my studies about OpenGL3.2+, now I am confused about how to optimize something like this:
// Verts
glBindBuffer(GL_ARRAY_BUFFER, VertBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * size, verts, GL_DYNAMIC_DRAW);
glEnableVertexAttribArray(VERTEX_COORD_ATTRIB);
glVertexAttribPointer(VERTEX_COORD_ATTRIB,3,GL_FLOAT, GL_FALSE, sizeof(float) * floatsPerVertex, 0);
// Textures
glBindBuffer(GL_ARRAY_BUFFER, TexBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * texsize, tex, GL_DYNAMIC_DRAW);
glEnableVertexAttribArray(TEXTURE_COORD_ATTRIB);
glVertexAttribPointer(TEXTURE_COORD_ATTRIB, 2, GL_FLOAT, GL_FALSE, sizeof(float) * TexCoords2D, 0);
//add light color info
glBindBuffer(GL_ARRAY_BUFFER, ColorBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * colorsize, lightcolor, GL_DYNAMIC_DRAW);
glEnableVertexAttribArray(COLOR_ATTRIB);
glVertexAttribPointer(COLOR_ATTRIB, 4, GL_FLOAT, GL_FALSE, sizeof(float) * ColorInfo, 0);
// Draw
glDrawArrays(GL_TRIANGLES, 0, size);
// Clean up
glDisableVertexAttribArray(VERTEX_COORD_ATTRIB);
glDisableVertexAttribArray(TEXTURE_COORD_ATTRIB);
glDisableVertexAttribArray(COLOR_ATTRIB);
Lets assume this is done for a few meshes, currently each mesh is always pushed like this, bound, buffered and drawn. Needles to say that this is surely no effective approach.
Now when reading (many) tutorials, the one thing I always see is that it is suggested to use VAO to improve that, now my difficulty to understand is- each single tutorial seems to refer in that relation also to indexed drawing. While this method seems to be perfectly fine when using an example with something extremely simple like 2 quads, I am wondering now how one is supposed to create an index for a real complex mesh? Or is it just assumed that this is available (due to .obj file or something).
Also I am confused about if a VAO always needs an index or could it be used without? And if so, would it even make sense without, since I read that an optimization makes use of knowing the index?
You see there is a lot of confusion here yet and I realize this might be a silly question again :)
However, what I want to finally achieve is, instead of pushing each mesh like this, to buffer each mesh once in the memory of the graphics card and redraw it then from the buffer.
I don't know yet a VAO is even the right approach, but every tutorial I read seems to have VAO's as the next step.
First of all you should separate your GRAM writting with glBufferData() from your drawing calls with glDrawArrays(). This significantly drops your performance, because you are basicly copying your data from RAM to GRAM on every drawing call.
For this purpose you can use VAOs:
// setting up buffers
glBindVertexArray(VertexArrayIndex);
glBindBuffer(GL_ARRAY_BUFFER, VertBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * size, verts, GL_DYNAMIC_DRAW);
glEnableVertexAttribArray(VERTEX_COORD_ATTRIB);
glVertexAttribPointer(VERTEX_COORD_ATTRIB,3,GL_FLOAT, GL_FALSE, sizeof(float) * floatsPerVertex, 0);
// Textures
glBindBuffer(GL_ARRAY_BUFFER, TexBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * texsize, tex, GL_DYNAMIC_DRAW);
glEnableVertexAttribArray(TEXTURE_COORD_ATTRIB);
glVertexAttribPointer(TEXTURE_COORD_ATTRIB, 2, GL_FLOAT, GL_FALSE, sizeof(float) * TexCoords2D, 0);
//add light color info
glBindBuffer(GL_ARRAY_BUFFER, ColorBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * colorsize, lightcolor, GL_DYNAMIC_DRAW);
glEnableVertexAttribArray(COLOR_ATTRIB);
glVertexAttribPointer(COLOR_ATTRIB, 4, GL_FLOAT, GL_FALSE, sizeof(float) * ColorInfo, 0);
glBindVertexArray(0);
// your drawing call
glBindVertexArray(VertexArrayIndex);
glDrawArrays(GL_TRIANGLES, 0, size);
glBindVertexArray(0);
If you want to draw really big meshes (their size is bigger than GRAM available on you graphic card) you should take interest in splitting your data into small chunks. Having all that data in one big array may cause some nasty memory allocation and rendering issues (believe me - I've been there ;)).
I suggest your next step should be to turn your vertex atrributes into a struct rather than separate arrays. That way, you're only using one array object, and GPUs much prefer this memory layout.
Beyond that:
Whether you go indexed or not depends heavily on your data; it's not a requirement for fast performance, but it can help; the other option is to use triangle strips, this also reduces the amount of vertex data. If you have multiple meshes in a single array object, you can simply change your vertex attrib pointers to start at a different location in the array object in order to draw a different mesh. Then you're not switching between array objects so much.
Most of these decisions should be driven by your constraints and by performance measurements!
I have generally learned OpenGL Interoperability with CUDA, but my problem is like this:
I have a lot of arrays, some for vertex, some for norm and some for alpha value alone, and some pointers to these arrays on device memory (something like dev_ver, dev_norm) which are used in kernel. I have already mapped the resource and now I want to use these data in shaders to make some effects. My rendering code is like this:
glUseProgram (programID);
glBindBuffer (GL_ARRAY_BUFFER, vertexBuffer_0);
glBufferData(GL_ARRAY_BUFFER, size, _data_on_cpu_0, GL_DYNAMIC_DRAW);
glVertexAttribPointer (0, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);
glBindBuffer (GL_ARRAY_BUFFER, vertexBuffer_1);
glBufferData(GL_ARRAY_BUFFER, size, _data_on_cpu_1, GL_DYNAMIC_DRAW);
glVertexAttribPointer (1, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);
glBindBuffer (GL_ARRAY_BUFFER, vertexBuffer_2);
glBufferData(GL_ARRAY_BUFFER, size, _data_on_cpu_2, GL_DYNAMIC_DRAW);
glVertexAttribPointer (2, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);
glEnableVertexAttribArray (0);
glEnableVertexAttribArray (1);
glEnableVertexAttribArray (2);
glDrawArrays (GL_TRIANGLES, 0, _max_);
glDisableVertexAttribArray (0);
glDisableVertexAttribArray (1);
glDisableVertexAttribArray (2);
However, now I have no _data_on_cpu_, is it still possible to do the same thing ? The sample in cuda 6.0 is something like this:
glBindBuffer(GL_ARRAY_BUFFER, posVbo);
glVertexPointer(4, GL_FLOAT, 0, 0);
glEnableClientState(GL_VERTEX_ARRAY);
glBindBufferARB(GL_ARRAY_BUFFER_ARB, normalVbo);
glNormalPointer(GL_FLOAT, sizeof(float)*4, 0);
glEnableClientState(GL_NORMAL_ARRAY);
glColor3f(1.0, 0.0, 0.0);
glDrawArrays(GL_TRIANGLES, 0, totalVerts);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_NORMAL_ARRAY);
I don't exactly understand how this could work and what to do in my case.
By the way, the method I have used is to cudaMemcpy the dev_ to host and do the render like usual, but this is obviously not efficient, because when I do rendering I again send the data back to GPU by OpenGL (if I'm right).
It's not really clear what your asking for, you mention CUDA yet none of the code you have posted is CUDA specific. I'm guessing vertexbuffer_2 contains additional per vertex information you want to access in the shader?
OpenGL calls are as efficient as you will get it, they aren't actually copying any data back from device to host. They are simply sending the addresses to the device, telling it where to get the data from and how much data to use to render.
You only need to fill the vertex and normal information at the start of your program, there isn't much reason to be changing this information during execution. You can then change data stored in texture buffers to pass additional per entity data to shaders to change model position, rotation, colour etc.
When you write your shader you must include in it;
attribute in vec3 v_data; (or similar)
When you init your shader you must then;
GLuint vs_v_data = glGetAttribLocation(p_shaderProgram, "v_data");
Then instead of your;
glBindBuffer (GL_ARRAY_BUFFER, vertexBuffer_2);
glBufferData(GL_ARRAY_BUFFER, size, _data_on_cpu_2, GL_DYNAMIC_DRAW);
glVertexAttribPointer (2, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);
You use;
glEnableVertexAttribArray (vs_v_data);
glBindBuffer (GL_ARRAY_BUFFER, vertexBuffer_2);
glBufferData(GL_ARRAY_BUFFER, size, _data_on_cpu_2, GL_DYNAMIC_DRAW);
glVertexAttribPointer (vs_v_data, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);
This should let you access a float3 inside your vshaders called v_data that has whatevers stored in vertexBuffer_2, presumably secondary vertex information to lerp between for animation.
A simple shader for this that simply repositions vertices based on an input tick
#version 120
attribute in float tick;
attribute in vec3 v_data;
void main()
{
gl_Vertex.xyz = mix(gl_Vertex.xyz, v_data, tick);
}
If you want per entity data instead of/in addition to per vertex data, you should be doing that via texture buffers.
If your trying to access vertex buffer obj data inside kernels you need to use a bunch of functions;
cudaGraphicsGLRegisterBuffer() This will give you a resource pointer to the buffer, execute this once after you initially setup the vbo.
cudaGraphicsMapResources() This will map the buffer (you can use it in CUDA but not gl)
cudaGraphicsResourceGetMappedPointer() This will give you a device pointer to the buffer, pass this to the the kernel.
cudaGraphicsUnmapResources() This will unmap the buffer (you can use it in gl, but not CUDA)
It seems like glBufferSubData is overwriting or somehow mangling data between my glDrawArrays calls. I'm working in Windows 7 64bit, with that latest drivers for my Nvidia GeForce GT520M CUDA 1GB.
I have 2 models, each with an animation. The models have 1 mesh, and that mesh is stored in the same VAO. They also have 1 animation each, and the bone transformations to be used for rendering the mesh is stored in the same VBO.
My workflow looks like this:
calculate bone transformation matrices for a model
load bone transformation matrices into opengl using glBufferSubData, then bind the buffer
render the models mesh using glDrawArrays
For one model, this works (at least, mostly - sometimes I get weird gaps in between the vertices).
However, for more than one model, it looks like bone transformation matrix data is getting mixed up between the rendering calls to the meshes.
Single Model Animated Windows
Two Models Animated Windows
I load my bone transformation data like so:
void Animation::bind()
{
glBindBuffer(GL_UNIFORM_BUFFER, bufferId_);
glBufferSubData(GL_UNIFORM_BUFFER, 0, currentTransforms_.size() * sizeof(glm::mat4), ¤tTransforms_[0]);
bindPoint_ = openGlDevice_->bindBuffer( bufferId_ );
}
And I render my mesh like so:
void Mesh::render()
{
glBindVertexArray(vaoId_);
glDrawArrays(GL_TRIANGLES, 0, vertices_.size());
glBindVertexArray(0);
}
If I add a call to glFinish() after my call to render(), it works just fine! This seems to indicate to me that, for some reason, the transformation matrix data for one animation is 'bleeding' over to the next animation.
How could this happen? I am under the impression that if I called glBufferSubData while that buffer was in use (i.e. for a glDrawArrays for example), then it would block. Is this not the case?
It might be worth mentioning that this same code works just fine in Linux.
Note: Related to a previous post, which I deleted.
Mesh Loading Code:
void Mesh::load()
{
LOG_DEBUG( "loading mesh '" + name_ +"' into video memory." );
// create our vao
glGenVertexArrays(1, &vaoId_);
glBindVertexArray(vaoId_);
// create our vbos
glGenBuffers(5, &vboIds_[0]);
glBindBuffer(GL_ARRAY_BUFFER, vboIds_[0]);
glBufferData(GL_ARRAY_BUFFER, vertices_.size() * sizeof(glm::vec3), &vertices_[0], GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
glBindBuffer(GL_ARRAY_BUFFER, vboIds_[1]);
glBufferData(GL_ARRAY_BUFFER, textureCoordinates_.size() * sizeof(glm::vec2), &textureCoordinates_[0], GL_STATIC_DRAW);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, 0);
glBindBuffer(GL_ARRAY_BUFFER, vboIds_[2]);
glBufferData(GL_ARRAY_BUFFER, normals_.size() * sizeof(glm::vec3), &normals_[0], GL_STATIC_DRAW);
glEnableVertexAttribArray(2);
glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 0, 0);
glBindBuffer(GL_ARRAY_BUFFER, vboIds_[3]);
glBufferData(GL_ARRAY_BUFFER, colors_.size() * sizeof(glm::vec4), &colors_[0], GL_STATIC_DRAW);
glEnableVertexAttribArray(3);
glVertexAttribPointer(3, 4, GL_FLOAT, GL_FALSE, 0, 0);
if (bones_.size() == 0)
{
bones_.resize( vertices_.size() );
for (auto& b : bones_)
{
b.weights = glm::vec4(0.25f);
}
}
glBindBuffer(GL_ARRAY_BUFFER, vboIds_[4]);
glBufferData(GL_ARRAY_BUFFER, bones_.size() * sizeof(VertexBoneData), &bones_[0], GL_STATIC_DRAW);
glEnableVertexAttribArray(4);
glVertexAttribIPointer(4, 4, GL_INT, sizeof(VertexBoneData), (const GLvoid*)0);
glEnableVertexAttribArray(5);
glVertexAttribPointer(5, 4, GL_FLOAT, GL_FALSE, sizeof(VertexBoneData), (const GLvoid*)(sizeof(glm::ivec4)));
glBindVertexArray(0);
}
Animation UBO Setup:
void Animation::setupAnimationUbo()
{
bufferId_ = openGlDevice_->createBufferObject(GL_UNIFORM_BUFFER, Constants::MAX_NUMBER_OF_BONES_PER_MESH * sizeof(glm::mat4), ¤tTransforms_[0]);
}
where Constants::MAX_NUMBER_OF_BONES_PER_MESH is set to 100.
In OpenGlDevice:
GLuint OpenGlDevice::createBufferObject(GLenum target, glmd::uint32 totalSize, const void* dataPointer)
{
GLuint bufferId = 0;
glGenBuffers(1, &bufferId);
glBindBuffer(target, bufferId);
glBufferData(target, totalSize, dataPointer, GL_DYNAMIC_DRAW);
glBindBuffer(target, 0);
bufferIds_.push_back(bufferId);
return bufferId;
}
Those usage flags are mostly correct for this scenario, though you might consider trying GL_STREAM_DRAW.
Your driver appears to be failing to implicitly synchronize for some reason, so you might want to try a technique that eliminates the need for synchronization in the first place. I would suggest Buffer Orphaning: call glBufferData (...) with NULL for the data pointer prior to sending data. This will allow commands that are currently using the UBO to continue using the original data store without forcing synchronization, since you will allocate a new data store before sending new data. When the earlier mentioned commands finish the original data store will be orphaned and the GL implementation will free it.
In newer OpenGL implementations you can use glInvalidateBuffer[Sub]Data (...) to hint the driver into doing what was discussed above. Likewise, you can use glMapBufferRange (...) with appropriate flags to control all of this behavior more explicitly. Unmapping will implicitly flush and synchronize access to a buffer object unless told otherwise, this might get your driver to do its job if you do not want to mess around with synchronization-free buffer update logic.
Most of what I mentioned is discussed in more detail here.
It seems like glBufferSubData is overwriting or somehow mangling data between my glDrawArrays calls. I'm working in Windows 7 64bit, with that latest drivers for my Nvidia GeForce GT520M CUDA 1GB.
I have 2 models, each with an animation. The models have 1 mesh, and that mesh is stored in the same VAO. They also have 1 animation each, and the bone transformations to be used for rendering the mesh is stored in the same VBO.
My workflow looks like this:
calculate bone transformation matrices for a model
load bone transformation matrices into opengl using glBufferSubData, then bind the buffer
render the models mesh using glDrawArrays
For one model, this works (at least, mostly - sometimes I get weird gaps in between the vertices).
However, for more than one model, it looks like bone transformation matrix data is getting mixed up between the rendering calls to the meshes.
Single Model Animated Windows
Two Models Animated Windows
I load my bone transformation data like so:
void Animation::bind()
{
glBindBuffer(GL_UNIFORM_BUFFER, bufferId_);
glBufferSubData(GL_UNIFORM_BUFFER, 0, currentTransforms_.size() * sizeof(glm::mat4), ¤tTransforms_[0]);
bindPoint_ = openGlDevice_->bindBuffer( bufferId_ );
}
And I render my mesh like so:
void Mesh::render()
{
glBindVertexArray(vaoId_);
glDrawArrays(GL_TRIANGLES, 0, vertices_.size());
glBindVertexArray(0);
}
If I add a call to glFinish() after my call to render(), it works just fine! This seems to indicate to me that, for some reason, the transformation matrix data for one animation is 'bleeding' over to the next animation.
How could this happen? I am under the impression that if I called glBufferSubData while that buffer was in use (i.e. for a glDrawArrays for example), then it would block. Is this not the case?
It might be worth mentioning that this same code works just fine in Linux.
Note: Related to a previous post, which I deleted.
Mesh Loading Code:
void Mesh::load()
{
LOG_DEBUG( "loading mesh '" + name_ +"' into video memory." );
// create our vao
glGenVertexArrays(1, &vaoId_);
glBindVertexArray(vaoId_);
// create our vbos
glGenBuffers(5, &vboIds_[0]);
glBindBuffer(GL_ARRAY_BUFFER, vboIds_[0]);
glBufferData(GL_ARRAY_BUFFER, vertices_.size() * sizeof(glm::vec3), &vertices_[0], GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
glBindBuffer(GL_ARRAY_BUFFER, vboIds_[1]);
glBufferData(GL_ARRAY_BUFFER, textureCoordinates_.size() * sizeof(glm::vec2), &textureCoordinates_[0], GL_STATIC_DRAW);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, 0);
glBindBuffer(GL_ARRAY_BUFFER, vboIds_[2]);
glBufferData(GL_ARRAY_BUFFER, normals_.size() * sizeof(glm::vec3), &normals_[0], GL_STATIC_DRAW);
glEnableVertexAttribArray(2);
glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 0, 0);
glBindBuffer(GL_ARRAY_BUFFER, vboIds_[3]);
glBufferData(GL_ARRAY_BUFFER, colors_.size() * sizeof(glm::vec4), &colors_[0], GL_STATIC_DRAW);
glEnableVertexAttribArray(3);
glVertexAttribPointer(3, 4, GL_FLOAT, GL_FALSE, 0, 0);
if (bones_.size() == 0)
{
bones_.resize( vertices_.size() );
for (auto& b : bones_)
{
b.weights = glm::vec4(0.25f);
}
}
glBindBuffer(GL_ARRAY_BUFFER, vboIds_[4]);
glBufferData(GL_ARRAY_BUFFER, bones_.size() * sizeof(VertexBoneData), &bones_[0], GL_STATIC_DRAW);
glEnableVertexAttribArray(4);
glVertexAttribIPointer(4, 4, GL_INT, sizeof(VertexBoneData), (const GLvoid*)0);
glEnableVertexAttribArray(5);
glVertexAttribPointer(5, 4, GL_FLOAT, GL_FALSE, sizeof(VertexBoneData), (const GLvoid*)(sizeof(glm::ivec4)));
glBindVertexArray(0);
}
Animation UBO Setup:
void Animation::setupAnimationUbo()
{
bufferId_ = openGlDevice_->createBufferObject(GL_UNIFORM_BUFFER, Constants::MAX_NUMBER_OF_BONES_PER_MESH * sizeof(glm::mat4), ¤tTransforms_[0]);
}
where Constants::MAX_NUMBER_OF_BONES_PER_MESH is set to 100.
In OpenGlDevice:
GLuint OpenGlDevice::createBufferObject(GLenum target, glmd::uint32 totalSize, const void* dataPointer)
{
GLuint bufferId = 0;
glGenBuffers(1, &bufferId);
glBindBuffer(target, bufferId);
glBufferData(target, totalSize, dataPointer, GL_DYNAMIC_DRAW);
glBindBuffer(target, 0);
bufferIds_.push_back(bufferId);
return bufferId;
}
Those usage flags are mostly correct for this scenario, though you might consider trying GL_STREAM_DRAW.
Your driver appears to be failing to implicitly synchronize for some reason, so you might want to try a technique that eliminates the need for synchronization in the first place. I would suggest Buffer Orphaning: call glBufferData (...) with NULL for the data pointer prior to sending data. This will allow commands that are currently using the UBO to continue using the original data store without forcing synchronization, since you will allocate a new data store before sending new data. When the earlier mentioned commands finish the original data store will be orphaned and the GL implementation will free it.
In newer OpenGL implementations you can use glInvalidateBuffer[Sub]Data (...) to hint the driver into doing what was discussed above. Likewise, you can use glMapBufferRange (...) with appropriate flags to control all of this behavior more explicitly. Unmapping will implicitly flush and synchronize access to a buffer object unless told otherwise, this might get your driver to do its job if you do not want to mess around with synchronization-free buffer update logic.
Most of what I mentioned is discussed in more detail here.
I am transferring over my vertex arrays functions to VBOs to increase the speed of my application.
Here was my original working vertex array rendering function:
void BSP::render()
{
glFrontFace(GL_CCW);
// Set up rendering states
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glVertexPointer(3, GL_FLOAT, sizeof(Vertex), &vertices[0].x);
glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), &vertices[0].u);
// Draw
glDrawElements(GL_TRIANGLES, numIndices, GL_UNSIGNED_SHORT, indices);
// End of rendering - disable states
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
}
Worked great!
Now I am moving them into VBOs and my program actually caused my graphics card to stop responding. The setup on my vertices and indices are exactly the same.
New setup:
vboId is setup in the bsp.h like so: GLuint vboId[2];
I get no error when I just run the createVBO() function!
void BSP::createVBO()
{
// Generate buffers
glGenBuffers(2, vboId);
// Bind the first buffer (vertices)
glBindBuffer(GL_ARRAY_BUFFER, vboId[0]);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
// Now save indices data in buffer
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vboId[1]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
}
And the rendering code for the VBOS. I am pretty sure it's in here. Just want to render whats in the VBO like I did in the vertex array.
Render:
void BSP::renderVBO()
{
glBindBuffer(GL_ARRAY_BUFFER, vboId[0]); // for vertex coordinates
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vboId[1]); // for indices
// do same as vertex array except pointer
glEnableClientState(GL_VERTEX_ARRAY); // activate vertex coords array
glVertexPointer(3, GL_FLOAT, 0, 0); // last param is offset, not ptr
// draw the bsp area
glDrawElements(GL_TRIANGLES, numVertices, GL_UNSIGNED_BYTE, BUFFER_OFFSET(0));
glDisableClientState(GL_VERTEX_ARRAY); // deactivate vertex array
// bind with 0, so, switch back to normal pointer operation
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}
Not sure what the error is but I am pretty sure I have my rendering function wrong. Wish there was a more unified tutorial on this as there are a bunch online but they are often contradicting eachother.
In addition what Miro said (the GL_UNSIGNED_BYTE should be GL_UNSIGNED_SHORT), I don't think you want to use numVertices but numIndices, like in your non-VBO call.
glDrawElements(GL_TRIANGLES, numIndices, GL_UNSIGNED_SHORT, 0);
Otherwise your code looks quite valid and if this doesn't fix your problem, maybe the error is somewhere else.
And by the way the BUFFER_OFFSET(i) thing is usuaully just a define for ((char*)0+(i)), so you can also just pass in the byte offset directly, especially when it's 0.
EDIT: Just spotted another one. If you use the exact data structures you use for the non-VBO version (which I assumed above), then you of course need to use sizeof(Vertex) as stride parameter in glVertexPointer.
If you are passing same data to glDrawElements when you aren't using VBO and same data to VBO buffer. Then parameters little differs, without FBO you've used GL_UNSIGNED_SHORT and with FBO you've used GL_UNSIGNED_BYTE. So i think VBO call should look like that:
glDrawElements(GL_TRIANGLES, numVertices, GL_UNSIGNED_SHORT, 0);
Also look at this tutorial, there are VBO buffers explained very well.
How do you declare vertices and indices?
The size parameter to glBufferData should be the size of the buffer in bytes and if you pass sizeof(vertices) it will return the total size of the declared array (not just what is allocated).
Try something like sizeof(Vertex)*numVertices and sizeof(indices[0])*numIndices instead.