So, I've been working on this .OBJ/.MTL mesh parser for the past week and a half. Within this time I've been tracking down/fixing a lot of bugs, cleaning up code, documenting it, etc etc.
The problem is that, with every bug I fix, there still is this issue which crops up, and since a picture is worth a thousand words...
Using GL_LINE_LOOP
(NOTE: the pyramid on the right tipping outward from the sphere is the problem here)
Using GL_TRIANGLES
What's even more interesting is that this "bad" vertex data appears to move with the camera when floating around the scene...except that it scales and sticks outside of the mesh.
The odd thing here is that while I'm sure the issue has something to do with memory, I've been checking for issues which contradict whether or not the parsing algorithm works properly. After some unit tests, it appears to be working fine.
So, I thought it may be a Linux nVidia driver issue. I updated the driver to the next version, restarted, and still no dice.
After some heavy thinking, I've been trying to find errors in the following code.
//! every 3 vertices should represent a triangle, therefore we'll want to
//! use the indices to grab their corresponding vertices. Since the cross product
//! of two sides of every triangle (where one side = Vn - Vm, 'n' and 'm' being on the range of 1..3),
//! we first grab the three vertices, and then compute the normal using the their differences.
const uInt32 length = mesh->vertices.size();
//! declare a pointer to the vector so we can perform simple
//! memory copies to get the indices for each triangle within the
//! iteration.
GLuint* const pIndexBuf = &mesh->indices[ 0 ];
for ( uInt32 i = 0; i < length; i += 3 )
{
GLuint thisTriIndices[ 3 ];
memcpy( thisTriIndices, pIndexBuf + i, sizeof( GLuint ) * 3 );
vec3 vertexOne = vec3( mesh->vertices[ thisTriIndices[ 0 ] ] );
vec3 vertexTwo = vec3( mesh->vertices[ thisTriIndices[ 1 ] ] );
vec3 vertexThree = vec3( mesh->vertices[ thisTriIndices[ 2 ] ] );
vec3 sideOne = vertexTwo - vertexOne;
vec3 sideTwo = vertexThree - vertexOne;
vec3 surfaceNormal = glm::cross( sideOne, sideTwo );
mesh->normals.push_back( surfaceNormal );
}
The current one shown in the picture doesn't even have normal data, so the idea is to compute surface normals for it, hence the above code. While I've made some checks to see if the index data was being loaded properly within the loop, I haven't been able to find anything yet.
I think the way I'm laying out my memory might have problems too, but I can't quite put my finger on what the problem would be. In case I've missed something, I'll throw in my glVertexAttribPointer calls:
//! Gen some buf handles
glGenBuffers( NUM_BUFFERS_PER_MESH, mesh->buffers );
//! Load the respective buffer data for the mesh
__LoadVec4Buffer( mesh->buffers[ BUFFER_VERTEX ], mesh->vertices ); //! positons
__LoadVec4Buffer( mesh->buffers[ BUFFER_COLOR ], mesh->colors ); //! material colors
__LoadVec3Buffer( mesh->buffers[ BUFFER_NORMAL ], mesh->normals ); //! normals
__LoadIndexBuffer( mesh->buffers[ BUFFER_INDEX ], mesh->indices ); //! indices
//! assign the vertex array a value
glGenVertexArrays( 1, &mesh->vertexArray );
//! Specify the memory layout for each attribute
glBindVertexArray( mesh->vertexArray );
//! Position and color are both stored in BUFFER_VERTEX.
glBindBuffer( GL_ARRAY_BUFFER, mesh->buffers[ BUFFER_VERTEX ] );
glEnableVertexAttribArray( meshProgram->attributes[ "position" ] );
glVertexAttribPointer( meshProgram->attributes[ "position" ], //! index
4, //! num vals
GL_FLOAT, GL_FALSE, //! value type, normalized?
sizeof( vec4 ), //! number of bytes until next value in the buffer
( void* ) 0 ); //! offset of the memory in the buffer
glBindBuffer( GL_ARRAY_BUFFER, mesh->buffers[ BUFFER_COLOR ] );
glEnableVertexAttribArray( meshProgram->attributes[ "color" ] );
glVertexAttribPointer( meshProgram->attributes[ "color" ],
4,
GL_FLOAT, GL_FALSE,
sizeof( vec4 ),
( void* ) 0 );
//! Now we specify the layout for the normals
glBindBuffer( GL_ARRAY_BUFFER, mesh->buffers[ BUFFER_NORMAL ] );
glEnableVertexAttribArray( meshProgram->attributes[ "normal" ] );
glVertexAttribPointer( meshProgram->attributes[ "normal" ],
3,
GL_FLOAT, GL_FALSE,
sizeof( vec3 ),
( void* )0 );
//! Include the index buffer within the vertex array
glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, mesh->buffers[ BUFFER_INDEX ] );
glBindVertexArray( 0 );
Any kind of point in the right direction at the very least would be appreciated: I have no idea what the common causes are for these issues.
Edit: posted draw code on request
glBindVertexArray( mMeshes[ i ]->vertexArray );
UBO::LoadMatrix4( UBO::MATRIX_MODELVIEW, modelView.top() );
UBO::LoadMatrix4( UBO::MATRIX_PROJECTION, camera.projection() );
glDrawElements( GL_TRIANGLES, mMeshes[ i ]->indices.size(), GL_UNSIGNED_INT, ( void* )0 );
glBindVertexArray( 0 );
I found the final culprit, in conjunction with #radical7's suggestions, these fixed the issue for the most part.
// round mesh->indices.size() down if it's not already divisible by 3.
// the rounded value is stored in numTris
std::vector< vec4 > newVertices;
uInt32 indicesLen = Math_FloorForMultiple( mesh->indices.size(), 3 );
// declare a pointer to the vector so we can perform simple
// memory copies to get the indices for each triangle within the
// iteration.
newVertices.reserve( indicesLen );
const GLuint* const pIndexBuf = &mesh->indices[ 0 ];
for ( uInt32 i = 0; i < indicesLen; i += 3 )
{
const GLuint* const thisTriIndices = pIndexBuf + i;
vec4 vertexOne = mesh->vertices[ thisTriIndices[ 0 ] - 1 ];
vec4 vertexTwo = mesh->vertices[ thisTriIndices[ 1 ] - 1 ];
vec4 vertexThree = mesh->vertices[ thisTriIndices[ 2 ] - 1 ];
vec4 sideOne = vertexTwo - vertexOne;
vec4 sideTwo = vertexThree - vertexOne;
vec3 surfaceNormal = glm::cross( vec3( sideOne ), vec3( sideTwo ) );
mesh->normals.push_back( surfaceNormal );
mesh->normals.push_back( surfaceNormal + vec3( sideOne ) );
mesh->normals.push_back( surfaceNormal + vec3( sideTwo ) );
newVertices.push_back( vertexOne );
newVertices.push_back( vertexTwo );
newVertices.push_back( vertexThree );
}
mesh->vertices.clear();
mesh->vertices = newVertices;
Note that when the vertices are grabbed in the loop, via the call to mesh->vertices[ thisTriIndices[ x ] - 1 ], the - 1 is extremely important: OBJ mesh files store their face indices starting from 1...N indices, as opposed to 0....N-1 indices.
The indices themselves also shouldn't be used to draw the mesh, but rather as a means to obtain a new buffer of vertices from an already temporary buffer of vertices: you use the indices to access the elements within the temporary vertices, and then for each vertex obtained from the temp buffer, you add that vertex to a new buffer. This way, you'll get the number of vertices specified in the correct draw order. Thus, you want to draw them using vertex arrays only.
Related
I'm porting OpenGL 3.X code to OpenGL 2.1, where no VAO available. The codes below make my program crash inside graphics driver, immediately at glDrawElements call.
Part of the code is shared between 3.X and 2.1 mode, and it works properly in OpenGL 3.X above.
struct StrokeVertex
{
glm::vec2 position;
glm::vec2 tangent;
float center_dist;
};
if (use_gl3)
bind_vao();
bind_vbo();
bind_ibo();
send_data();
bind_program();
set_uniforms();
// setup client-side vertex array here
if (!use_gl3)
{
glEnableClientState( GL_VERTEX_ARRAY );
GLHELPER_CHECK;
glEnableVertexAttribArray( 0 );
GLHELPER_CHECK;
glVertexAttribPointer( get_attribute_location( "tangent" ) /* got 1 here */, 2, GL_FLOAT, GL_FALSE, sizeof( StrokeVertex ), (void*) offsetof( StrokeVertex, tangent ) );
GLHELPER_CHECK;
glEnableVertexAttribArray( 1 );
GLHELPER_CHECK;
glVertexAttribPointer( get_attribute_location( "center_dist" ) /* got 2 here */, 1, GL_FLOAT, GL_FALSE, sizeof( StrokeVertex ), (void*) offsetof( StrokeVertex, center_dist ) );
GLHELPER_CHECK;
// I also tried call this before setting the two attributes
glVertexPointer( 2, GL_FLOAT, sizeof( StrokeVertex ), (void*) offsetof( StrokeVertex, position ) );
GLHELPER_CHECK;
}
// immediately crash here
glDrawElements( GL_TRIANGLES, GLsizei( n_elem ), GL_UNSIGNED_INT, nullptr );
GLHELPER_CHECK;
if (!use_gl3)
{
glDisableClientState( GL_VERTEX_ARRAY );
}
else
{
unbind_vao();
}
And the part of vertex shader of OpenGL 3.X and 2.X. Most part are same expect attribute declaration:
in vec2 logic_pos;
in vec2 tangent;
in float center_dist;
OpenGL 2.X:
// builtin gl_Vertex is used, so we omit logic_pos
attribute vec2 tangent;
attribute float center_dist;
There seems to be a mismatch regarding which vertex attributes get enabled and to which data gets bound:
The following code tells OpenGL that it should enable vertex attribute 0, but the data gets bound to attribute 1 (at least according to the comment).
glEnableVertexAttribArray( 0 );
glVertexAttribPointer( get_attribute_location( "tangent" ) /* got 1 here */, 2, GL_FLOAT, GL_FALSE, sizeof( StrokeVertex ), (void*) offsetof( StrokeVertex, tangent ) );
In total, it looks as if your code enables attributes 0 and 1, but binds data to 1 and 2.
If you have attributes enabled but do not bind any data to them, this might lead to the crash you describe.
I'm struggling to understand the relationship between the offset variables in the two functions and how the offset value affects gl_VertexID and gl_InstanceID variables in the shader.
Through reading of the functions documentation, I think glMapBufferRange expects offset to be the number of bytes from the start of the buffer, whereas glDrawArraysInstanced expects first to be the the number of strides as specified by glVertexAttribPointer.
However that doesn't seem to be the case, as the below code doesn't work if offsetVerts has a value different from 0. For 0 it renders 3 squares on the screen, as I expected it.
The other possible error source would be the value of gl_VertexID. I'd expect it to be 0,1,2,3 for the 4 vertex shader calls per instance, regardless of the offset value.
Just to make sure I also tried using a first value that is multiple of 4 and vertices[int(mod(gl_VertexID,4))] for the position lookup, without success.
How can I alternate the code to make it work with offsets other than 0?
glGetError() calls are omitted here to shorten the code, it's 0 through the whole process. GL version is 3.3.
Init code:
GLuint buff_id, v_id;
GLint bytesPerVertex = 2*sizeof(GLfloat); //8
glGenBuffers( 1, &buff_id );
glBindBuffer( GL_ARRAY_BUFFER, buff_id );
glGenVertexArrays( 1, &v_id );
glBufferData( GL_ARRAY_BUFFER, 1024, NULL, GL_STREAM_DRAW );
glBindVertexArray( v_id );
glEnableVertexAttribArray( posLoc );
glVertexAttribPointer( posLoc, 2, GL_FLOAT, GL_FALSE, bytesPerVertex, (void *)0 );
glVertexAttribDivisor( posLoc, 1 );
glBindVertexArray( 0 );
glBindBuffer( GL_ARRAY_BUFFER, 0 );
float *data_ptr = nullptr;
int numVerts = 3;
int offsetVerts = 0;
render code:
glBindBuffer( GL_ARRAY_BUFFER, buff_id );
data_ptr = (float *)glMapBufferRange( GL_ARRAY_BUFFER,
bytesPerVertex * offsetVerts,
bytesPerVertex * numVerts,
GL_MAP_WRITE_BIT );
data_ptr[0] = 50;
data_ptr[1] = 50;
data_ptr[2] = 150;
data_ptr[3] = 50;
data_ptr[4] = 250;
data_ptr[5] = 50;
glUnmapBuffer( GL_ARRAY_BUFFER );
glBindBuffer( GL_ARRAY_BUFFER, 0 );
glBindVertexArray( v_id );
glDrawArraysInstanced( GL_TRIANGLE_STRIP, offsetVerts, 4, 3 );
glBindVertexArray( 0 );
vertex shader:
#version 330
uniform mat4 proj;
in vec2 pos;
void main() {
vec2 vertices[4]= vec2[4](
vec2(pos.x, pos.y),
vec2(pos.x + 10.0f, pos.y),
vec2(pos.x, pos.y + 10.0f ),
vec2(pos.x + 10.0f, pos.y + 10.0f )
);
gl_Position = proj * vec4(vertices[gl_VertexID], 1, 1);
}
fragment shader:
#version 330
out vec4 LFragment;
void main() {
LFragment = vec4( 1.0f, 1.0f, 1.0f, 1.0f );
}
The other possible error source would be the value of gl_VertexID. I'd expect it to be 0,1,2,3 for the 4 vertex shader calls per instance, regardless of the offset value.
There is no offset value in glDrawArrays*
The base function for this is
glDrawArrays(type, first, count), and this just will generate primitives from a consecutive sub-array of the specified vertex attribute arrays, from index frist to frist+count-1. Hence, gl_VertexID will be in the range first,first+count-1.
You are actually not using any vertex attribute array, you turned your attribute into an per-instance attribute. But the first parameter will not introduce an offset into these. You can either adjust your attribute pointer to include the offset, or you can use glDrawArraysInstancedBaseInstance to specify the offset you need.
Note that the gl_InstanceID will not reflect the base instance you set there, it will still count from 0 relative to the begin of the draw call. But the actuall instance values fetched from the array will use the offset.
If I got a cube model for example (cube got 6 faces). how can I draw each face with vbo? do I need to call glDrawElements 6 times? or is there another function to draw all at once? Usually I draw it like this:
for (int i = 0; i < facesNum; i++)
glDrawElements(GL_TRIANGLE_FAN, 4 + i*4, GL_UNSIGNED_INT, (GLvoid*)(i*4));
Is that the best way?
You can use Primitive Restart (OpenGL 3.1+) to restart a primitive such as a triangle fan while rendering, as if you started another glDraw* command.
Use glEnable(GL_PRIMITIVE_RESTART) to enable it, then glPrimitiveRestartIndex(restartIndex) to set an index (such as 0xFFFF) to use to signal a restart. Then whenever OpenGL encounters the restart index, it will stop the currently drawn primitive and start another one.
This lets you draw multiple triangle strips, fans, line loops, or strips with one index buffer and draw command. Just add the restart index between each primitive's index data.
Generally, what you would do is draw your object as GL_TRIANGLES instead of GL_TRIANGLE_FAN, which allows you to just draw all 12 triangles (6 faces * 2 triangles per face) with one call to glDrawElements.
To do this, you of course have to somewhat rearrange your Index Buffer, to include the information for the vertices of each triangle. This means you have to duplicate some indices, which should however not be a problem, as the point of the Index Buffer is precisely to be able to do this and not duplicate vertices.
Assuming your top face consists of the vertices index 0,1,2,3 in counterclockwise order,
you would change that part of the index buffer from 0,1,2,3 to 0,1,2,0,2,3 for example.
With this changed setup of the index buffer, all it should take would be a call to
glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_INT, NULL);
(36 as we are drawing 12 triangles for the cube with 3 vertices each)
You should only have to call DrawElements once with a single VBO. There are alternatives such as IBO's, but I am going to provide an example for a VBO since that is what you requested. Ideally, you should store all the vertices in an array or std::vector holding Vertex objects. Put all of your cube face vertices in this array or std::vector. You do not need an array/vector for each face. You will want to keep the data tightly compact and avoid using the virtual keyword as it creates a non exposed pointer (vtptr) which adds to your class memory footprint. The reason you want to keep the data compact is for when you send it to OpenGL, it will expect step sizes. If you ever are in doubt of the memory footprint of a class, it never hurts to do a quick output with the sizeof( ClassName ) function.
Your vertex class would look something like as follows;
class Vertex {
public:
~Vertex() {}
Vertex() {}
Vector3<float> vertexPosition;
Vector4<float> vertexColor;
Vector2<float> vertexTextureCoords;
Vector3<float> vertexNormal;
Vector3<float> vertexTangent;
Vector3<float> vertexBitangent;
Vector4<int> vertexBoneIndexes;
Vector4<float> vertexBoneWeights;
};
Where all the vector classes are NON virtual and the only data members are the vector components.
Here are the steps I follow:
Create a std::vector (or array) of vertex objects and populate the vertex data
Generate the VBO with the appropriate OpenGL Calls (Here is an example from my engine). I use a singleton which has wrapper functions in order to be Graphics API agnostic. The parameters are in the exact same order OpenGL would expect them for their VBO generation functions.
cbengine::CBRenderer * sharedRenderer = cbengine::CBRenderer::sharedCBRenderer();
sharedRenderer->generateVBOBuffers( 1, &entityToCreate->m_VBOBufferID );
sharedRenderer->bindVBOBuffer( entityToCreate->m_VBOBufferID );
sharedRenderer->bufferDataForVBO( ( sizeof( cbengine::Vertex ) * entityToCreate- >m_verts.size() ), &entityToCreate->m_verts.front() );
In your render function make the following OpenGL Calls. This is why I advised keeping the data tightly compact earlier. OpenGL needs to know what type of data to expect, the size of the data type, and the offset from the start of the data.
glEnableVertexAttribArray( m_vertexLocation );
glVertexAttribPointer( m_vertexLocation, 3, GL_FLOAT, false, sizeof( cbengine::Vertex ), (float*) offsetof( cbengine::Vertex, vertexPosition ) );
glEnableVertexAttribArray( m_colorLocation );
glVertexAttribPointer( m_colorLocation, 4, GL_FLOAT, false, sizeof( cbengine::Vertex ), (float*) offsetof( cbengine::Vertex, vertexColor ) );
glEnableVertexAttribArray( m_diffuseTextureCoordLocation );
glVertexAttribPointer( m_diffuseTextureCoordLocation, 2, GL_FLOAT, false, sizeof( cbengine::Vertex ), (float*) offsetof( cbengine::Vertex, vertexTextureCoords ) );
glEnableVertexAttribArray( m_normalCoordLocation );
glVertexAttribPointer( m_normalCoordLocation, 3, GL_FLOAT, false, sizeof( cbengine::Vertex ), (float*) offsetof( cbengine::Vertex, vertexNormal ) );
glEnableVertexAttribArray( m_tangentLocation );
glVertexAttribPointer( m_tangentLocation, 3, GL_FLOAT, false, sizeof( cbengine::Vertex ), (float*) offsetof( cbengine::Vertex, vertexTangent ) );
glEnableVertexAttribArray( m_bitangentLocation );
glVertexAttribPointer( m_bitangentLocation, 3, GL_FLOAT, false, sizeof( cbengine::Vertex ), (float*) offsetof( cbengine::Vertex, vertexBitangent ) );
glEnableVertexAttribArray( m_boneIndexesLocation ); // Apparently GL_INT causes issues
glVertexAttribPointer( m_boneIndexesLocation, 4, GL_FLOAT, false, sizeof( cbengine::Vertex ), (float*) offsetof( cbengine::Vertex, vertexBoneIndexes ) );
glEnableVertexAttribArray( m_boneWeightsLocation );
glVertexAttribPointer( m_boneWeightsLocation, 4, GL_FLOAT, false, sizeof( cbengine::Vertex ), (float*) offsetof( cbengine::Vertex, vertexBoneWeights ) );
Lastly, make a call to DrawArrays
sharedRenderer->drawArrays( drawPrim, 0, verts.size() );
This is a very quick example of how to accomplish what you want. Other things to keep in mind are winding order for culling, enabling/disabling textures, and custom vertex attrib data you may want to send to your shaders. You may not want to include the Bone Weights and Bone Indexes vectors if your engine does not support skeletal animation.
With all of my objects that are to be rendered, I use glDrawElements. However, my venture into Compute Shaders has left me a setup that uses glDrawArrays. As with many who are breaching the topic, I used this PDF as a basis. The problem is that when it is rendered, nothing appears.
#include "LogoTail.h"
LogoTail::LogoTail(int tag1) {
tag = tag1;
needLoad = false;
shader = LoadShaders("vertex-shader[LogoTail].txt","fragment-shader[LogoTail].txt");
shaderCompute = LoadShaders("compute-shader[LogoTail].txt");
for( int i = 0; i < NUM_PARTICLES; i++ )
{
points[ i ].x = 0.0f;
points[ i ].y = 0.0f;
points[ i ].z = 0.0f;
points[ i ].w = 1.0f;
}
glGenBuffers( 1, &posSSbo);
glBindBuffer( GL_SHADER_STORAGE_BUFFER, posSSbo );
glBufferData( GL_SHADER_STORAGE_BUFFER, sizeof(points), points, GL_STATIC_DRAW );
glUnmapBuffer(GL_SHADER_STORAGE_BUFFER);
for( int i = 0; i < NUM_PARTICLES; i++ )
{
times[ i ].x = 0.0f;
}
glGenBuffers( 1, &birthSSbo);
glBindBuffer( GL_SHADER_STORAGE_BUFFER, birthSSbo );
glBufferData( GL_SHADER_STORAGE_BUFFER, sizeof(times), times, GL_STATIC_DRAW );
glUnmapBuffer(GL_SHADER_STORAGE_BUFFER);
for( int i = 0; i < NUM_PARTICLES; i++ )
{
vels[ i ].vx = 0.0f;
vels[ i ].vy = 0.0f;
vels[ i ].vz = 0.0f;
vels[ i ].vw = 0.0f;
}
glGenBuffers( 1, &velSSbo );
glBindBuffer( GL_SHADER_STORAGE_BUFFER, velSSbo );
glBufferData( GL_SHADER_STORAGE_BUFFER, sizeof(vels), vels, GL_STATIC_DRAW );
glUnmapBuffer(GL_SHADER_STORAGE_BUFFER);
}
void LogoTail::Update(const double dt, float sunTime,glm::vec3 sunN) {
position=glm::translate(glm::mat4(), glm::vec3(4.5f,0,0));
}
void LogoTail::Draw(shading::Camera& camera){
shaderCompute->use();
glBindBufferBase( GL_SHADER_STORAGE_BUFFER, 4, posSSbo );
glBindBufferBase( GL_SHADER_STORAGE_BUFFER, 5, velSSbo );
glBindBufferBase( GL_SHADER_STORAGE_BUFFER, 6, birthSSbo );
glDispatchCompute( NUM_PARTICLES / WORK_GROUP_SIZE, 1, 1 );
glMemoryBarrier( GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT );
shaderCompute->stopUsing();
shader->use();
shader->setUniform("camera", camera.matrix());
shader->setUniform("model",position);
glBindBuffer( GL_ARRAY_BUFFER, posSSbo );
glVertexPointer( 4, GL_FLOAT, 0, (void *)0 );
glEnableClientState( GL_VERTEX_ARRAY );
glDrawArrays( GL_POINTS, 0, NUM_PARTICLES );
glDisableClientState( GL_VERTEX_ARRAY );
glBindBuffer( GL_ARRAY_BUFFER, 0 );
shader->stopUsing();
}
The header contains the needed structures and other variables so they do not fall out of scope for the specific object.
Here is the compute shader itself.
#version 430 core
#extension GL_ARB_compute_shader : enable
#extension GL_ARB_shader_storage_buffer_object : enable
layout( std140, binding=4 ) buffer Pos
{
vec4 Positions[ ]; // array of vec4 structures
};
layout( std140, binding=5 ) buffer Vel
{
vec4 Velocities[ ]; // array of vec4 structures
};
layout( std140, binding=6 ) buffer Tim
{
float BirthTimes[ ]; // array of structures
};
layout( local_size_x = 128, local_size_y = 1, local_size_z = 1 ) in;
const vec3 G = vec3( 0., -0.2, 0. );
const float DT = 0.016666;
void main() {
uint gid = gl_GlobalInvocationID.x; // the .y and .z are both 1
vec3 p = Positions[ gid ].xyz;
vec3 v = Velocities[ gid ].xyz;
vec3 pp = p + v*DT + .5*DT*DT*G;
vec3 vp = v + G*DT;
Positions[ gid ].xyz = pp;
Velocities[ gid ].xyz = vp;
}
For testing purposes I lowered the gravity.
I believe that nothing is out of scope, nor is there a needed bind, but yet it alludes me to why the particles are not drawing.
In addition, I also added a geometry shader that constructs a quad around each point but it did not solve anything.
Last 5 lines seems to me problematic:
glBindBuffer( GL_ARRAY_BUFFER, posSSbo );
glVertexPointer( 4, GL_FLOAT, 0, (void *)0 );
glEnableClientState( GL_VERTEX_ARRAY );
glDrawArrays( GL_POINTS, 0, NUM_PARTICLES );
glDisableClientState( GL_VERTEX_ARRAY );
glBindBuffer( GL_ARRAY_BUFFER, 0 );
My guess is You are trying to use old way of doing things in programmable pipeline.I am not sure how it is stated in OpenGL specs but it seems that in the newer versions (GL4.2) you are forced to bind your vertex buffers to VAO(may be that is vendor specific rules?).Once I needed to implement OIT and tried Cyril Crassin's demo which was using buffers with elements draw-just like you.I am using GL4.2 and NVidia cards.Nothing was showing up.I then bound them to a VAO and the issue was gone.So that is what I suggest you to try.
I'm preparing some buffers with 2f vertices, 2f texvertices and 4f colors. It's displayed right. The whole thing is in one class. If I have more instances (every generating it's own buffer id, never passed in a function so it's not cleaned up, wrapped as pointers in a std::list) only during the first draw (paused after first draw using gdb and I see all buffered things) all buffered data is visible. In the next draw only the last drawn buffer is visible.
I prepare them by generate, bind and then fill the buffer with data with this call:
glBufferData( GL_ARRAY_BUFFER, Size * 8 * sizeof( float ), f, GL_STATIC_DRAW );
where Size is a std::size_t with the number of vertices and f the float-Array. To draw the buffer I bind it, activate the clientstates: GL_VERTEX_ARRAY, GL_TEXTURE_COORD_ARRAY, GL_COLOR_ARRAY.
glDrawArrays( Mode, 0, Size );
where Mode is a GLenum with GL_TRIANGLES.
I fixed it by calling glBufferData before glDrawArrays every frame but that's not how it supposed to be. It supposed to be generating, binding, filling and then to draw by just binding and calling glDrawArrays, isn't it?
If necessary: I'm working with C++, gcc on a Windows 7 x64.
I was asked for more code:
void Buffer::CopyToGPU( )
{
glBindBuffer( GL_ARRAY_BUFFER, Object );
float* f = new float[ Size * 8 ];
for ( std::size_t s( 0 ) ; s < Size ; ++s )
CopyVertexToFloatArray( &f[ s * 8 ], Vortex[ s ] );
glBufferData( GL_ARRAY_BUFFER, Size * 8 * sizeof( float ), f, GL_STATIC_DRAW );
delete[] f;
glVertexPointer( 2, GL_FLOAT, 8 * sizeof( float ), NULL );
glTexCoordPointer( 2, GL_FLOAT, 8 * sizeof( float ), (char*)( 2 * sizeof( float ) ) );
glColorPointer( 4, GL_FLOAT, 8 * sizeof( float ), (char*)( 4 * sizeof( float ) ) );
}
void Buffer::Render( )
{
glBindBuffer( GL_ARRAY_BUFFER, Object );
glEnableClientState( GL_VERTEX_ARRAY );
glEnableClientState( GL_TEXTURE_COORD_ARRAY );
glEnableClientState( GL_COLOR_ARRAY );
//Actually draw the triangle, giving the number of vertices provided
glDrawArrays( Mode, 0, Size );
glDisableClientState( GL_VERTEX_ARRAY );
glDisableClientState( GL_TEXTURE_COORD_ARRAY );
glDisableClientState( GL_COLOR_ARRAY );
}
int main( ... ) // stripped buffer sfml2 initialization etc.
{
glClearColor( 0, 0, 0, 1 );
glEnable( GL_ALPHA_TEST );
glAlphaFunc( GL_GREATER , 0.1 );
glEnable( GL_TEXTURE_2D );
glEnable( GL_BLEND );
glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
while ( win.isOpen( ) ) // sf::Window
{
/// draw
glClear( GL_COLOR_BUFFER_BIT );
MVP.Apply( );
CallDraw( );
win.display( );
}
}
You seem to specify the attrib pointers when you update the buffer object. This is not how it works. The vertex attrib pointers are (depending on the GL version) either global state, or per-VAO state, but never per-VBO state. Currently, when you do something like
bufferA.CopyToGPU();
bufferB.CopyToGPU();
while(true) {
bufferA.render();
bufferB.render();
}
only buffer B will be used (leaving potential for out-ouf-bounds accesses as you think you use buffer A when rendering it), as the vertex array state is set to buffer B in the second call, overwriting any attrib pointers set in the first call. You need either to respecify the pointers when you draw each object, or use Vertex Array Objects to encapsulate those pointers. Note that the latter path is mandatory on GL >= 3.X core profile.