How to specify vertex attributes in client-side vertex array? - opengl

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.

Related

glVertexAttribPointer() - when should this be called?

I have some example code that calls glVertexAttribPointer() in 2 places. Is this necessary or can it just be done once?
First time - associating the vertex buffer data:
glVertexAttribPointer( 0, 3, GL_FLOAT, GL_FALSE, sizeof( COLVERTEX ), 0 );
glBufferData( GL_ARRAY_BUFFER, sizeof( v ), v, GL_STATIC_DRAW );
Second time - in the rendering callback function:
glEnableVertexAttribArray(0);
glBindBuffer( GL_ARRAY_BUFFER, vboQuad );
glVertexAttribPointer( 0, 3, GL_FLOAT, GL_FALSE, sizeof( COLVERTEX ), 0 );
glDrawArrays( GL_QUADS, 0, 4 );
glDisableVertexAttribArray(0);
Is it really necessary to describe the vertex attributes twice?
BufferData fills VBO with raw data. At this point it doesn't matter how data is supposed to be interpreted when drawing (e.g. the same data may be interpreted as vertex positions at one draw but as normals in another). So yes, you can remove this first call.
If you use vertex array objects, you could set vertex attribute pointers only once (via binding VBO, enabling vertex attibute, and setting vertex attribute pointer) and then just call glBindVertexArray before drawing and have all recorded vertex attrubtes set up (you don't even need to bind VBO containing vertex attributes before draw call).

OpenGL - Correct way to load multi faces

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.

OpenGL drawing meshes incorrectly

I'm attempting to make an OpenGL Engine in C++, but cannot render meshes correctly. Meshes, when rendered, create faces that connect two random points on the mesh, or a random point on the mesh with 0,0,0.
The problem can be seen here:
(I made it a wireframe to see the problem more clearly)
Code:
// Render all meshes (Graphics.cpp)
for( int curMesh = 0; curMesh < numMesh; curMesh++ ) {
// Save pointer of buffer
meshes[curMesh]->updatebuf();
Buffer buffer = meshes[curMesh]->buffer;
// Update model matrix
glm::mat4 mvp = Proj*View*(meshes[curMesh]->model);
// Initialize vertex array
glBindBuffer( GL_ARRAY_BUFFER, vertbuffer );
glBufferData( GL_ARRAY_BUFFER, sizeof(GLfloat)*buffer.numcoords*3, meshes[curMesh]->verts, GL_STATIC_DRAW );
// Pass information to shader
GLuint posID = glGetAttribLocation( shader, "s_vPosition" );
glVertexAttribPointer( posID, 3, GL_FLOAT, GL_FALSE, 0, (void*)0 );
glEnableVertexAttribArray( posID );
// Check if texture applicable
if( meshes[curMesh]->texID != NULL && meshes[curMesh]->uvs != NULL ) {
// Initialize uv array
glBindBuffer( GL_ARRAY_BUFFER, uvbuffer );
glBufferData( GL_ARRAY_BUFFER, sizeof(GLfloat)*buffer.numcoords*2, meshes[curMesh]->uvs, GL_STATIC_DRAW );
// Pass information to shader
GLuint uvID = glGetAttribLocation( shader, "s_vUV" );
glVertexAttribPointer( uvID, 2, GL_FLOAT, GL_FALSE, 0, (void*)(0) );
glEnableVertexAttribArray( uvID );
// Set mesh texture
glActiveTexture( GL_TEXTURE0 );
glBindTexture( GL_TEXTURE_2D, meshes[curMesh]->texID );
GLuint texID = glGetUniformLocation( shader, "Sampler" );
glUniform1i( texID, 0 );
}
// Actiavte shader
glUseProgram( shader );
// Set MVP matrix
GLuint mvpID = glGetUniformLocation( shader, "MVP" );
glUniformMatrix4fv( mvpID, 1, GL_FALSE, &mvp[0][0] );
// Draw verticies on screen
bool wireframe = true;
if( wireframe )
for(int i = 0; i < buffer.numcoords; i += 3)
glDrawArrays(GL_LINE_LOOP, i, 3);
else
glDrawArrays( GL_TRIANGLES, 0, buffer.numcoords );
}
// Mesh Class (Graphics.h)
class mesh {
public:
mesh();
void updatebuf();
Buffer buffer;
GLuint texID;
bool updated;
GLfloat* verts;
GLfloat* uvs;
glm::mat4 model;
};
My Obj loading code is here: https://www.dropbox.com/s/tdcpg4vok11lf9d/ObjReader.txt (It's pretty crude and isn't organized, but should still work)
This looks like a primitive restart issue to me. Hard to tell what exactly is the problem without seeing some code. It would help a lot to see the about 20 lines above and below and including the drawing calls render the teapot. I.e. the 20 lines before the corresponding glDrawArrays, glDrawElements or glBegin call and the 20 lines after.
subtract 1 from the indices for your use, since these are 1-based indices, and you will almost certainly need 0-based indices.
This is because your triangles are not connected for the wireframe to look perfect.
In case triangles is not connected you should construct index buffer.

glVertexAttribPointer() working only with the first stream

I am trying to use glVertexAttribPointer() to give some data to my vertex shader. The thing is that it's working only with the FIRST attribute...
Here is my OpenGL code:
struct Flag_vertex
{
GLfloat position_1[ 8 ];
GLfloat position_2[ 8 ];
};
Flag_vertex flag_vertex;
... // fill some data to flag_vertex
GLuint vertexbuffer_id;
glGenBuffers( 1, &vertexbuffer_id );
glBindBuffer( GL_ARRAY_BUFFER, vertexbuffer_id );
glBufferData( GL_ARRAY_BUFFER, sizeof(flag_vertex), &flag_vertex, GL_STATIC_DRAW );
glEnableVertexAttribArray( 0 );
glEnableVertexAttribArray( 1 );
glBindBuffer( GL_ARRAY_BUFFER, vertexbuffer_id );
glVertexAttribPointer( 0, 2, GL_FLOAT, GL_FALSE, 0, (void*)offsetof(Flag_vertex, position_1) );
glVertexAttribPointer( 1, 2, GL_FLOAT, GL_FALSE, 0, (void*)offsetof(Flag_vertex, position_2) );
and my shader is something like:
#version 420 core
layout(location = 0) in vec2 in_position_1;
layout(location = 1) in vec2 in_position_2;
out vec2 texcoord;
void main()
{
gl_Position = vec4(in_position_X, 0.0, 1.0);
texcoord = in_position_X * vec2(0.5) + vec2(0.5);
}
If I use "in_position_1" my texture RENDERS PERFECTLY, but if I use in_position_2 nothing happens...
Tip: before link my shaders I am doing:
glBindAttribLocation( programID, 0, "in_position_1");
glBindAttribLocation( programID, 1, "in_position_2");
Why it works only with the first stream? I need more data going to my vertex... I need to send color, etc... any hint?
glVertexAttribPointer( 0, 2, GL_FLOAT, GL_FALSE, 0, (void*)offsetof(Flag_vertex, position_1) );
glVertexAttribPointer( 1, 2, GL_FLOAT, GL_FALSE, 0, (void*)offsetof(Flag_vertex, position_2) );
These lines don't make sense. At least, not with how Flag_vertex is defined. If Flag_vertex is really supposed to be a vertex (and not a quad), then it makes no sense for it to have 8 floats. If each Flag_vertex defines a full quad, then you named it wrong; it's not a vertex at all, it's Flag_quad.
So it's hard to know what you're even trying to accomplish here.
Also:
If I use "in_position_1" my texture RENDERS PERFECTLY, but if I use in_position_2 nothing happens...
Of course it does. Your position data is in attribute 0. Your position data is therefore not in attribute 1. If you pretend attribute 1 has your position data when it clearly doesn't, you will not get reasonable results.
Your problem is that you're always using attribute 0 when you should be using both of them. You shouldn't be picking one or the other. Use in_position_1 for the position and in_position_2 for the texture coordinate. And try to name them reasonably, based on what they do (like position and texture_coord or something). Don't use numbers for them.
Tip: before link my shaders I am doing:
That is the exact same thing as the layout(location=#) setting in the shader. If you want it in the shader, then put it in the shader. If you want it in your OpenGL code, then put it in your OpenGL code. Don't put it in both places.

combining glVertexPointer and glVertexAttribPointer gives problems

I'm having an issue when first rendering a vertexbuffer with a program,
and then rendering a different vertexbuffer without program.
for the first buffer, when a program is enabled, i use code similar to:
glBindBuffer( GL_ARRAY_BUFFER, m_id );
GLint location = glGetAttribLocation( pID, "position" );
glEnableVertexAttribArray( location );
glVertexAttribPointer( location, 3, GL_FLOAT, GL_FALSE, 3 * sizeof( GLfloat ), 0 );
glDrawArrays( m_mode, 0, m_numVertices );
for the second, without program:
glBindBuffer( GL_ARRAY_BUFFER, m_id );
glEnableClientState( GL_VERTEX_ARRAY );
glVertexPointer( 3, GL_FLOAT, 3 * sizeof( GLfloat ), 0 );
glDrawArrays( m_mode, 0, m_numVertices );
both codepaths work fine individually, but when done in the order
"with program"->"without program", the second seems to use the buffer of the first,
and in the order "without program"->"with program", the first is not drawn (in the second iteration).
now this suggests to me that I'm missing some state change done by the glEnableVertexAttribArray block, but I don't understand what state change is causing the problems.
ps the reason I'm rendering with and without program is that in the scenegraph lib im using you can turn programs on or off per node.
Try adding
glDisableVertexAttribArray( location ); // location of "position"
before switching to fixed function rendering.