Reputation: 139
I'm just starting out with OpenGL and I have made my own 'OOP' version of drawing a triangle in c++.
I am using glfw and glew if that matters.
I don't understand why I can't draw more than 1 triangle.
This is the main function:
int main()
{
Graphics::Window window(1280, 720, "Take me to heaven");
window.MakeOpenGLAvailable(); // makes context with OpenGL after which initializes GLEW
Graphics::Objects::Triangle firstTriangle(Graphics::Vec2( 0.0f, 0.0f),
Graphics::Vec2( 1.0f, 1.0f),
Graphics::Vec2(-1.0f, 1.0f));
Graphics::Objects::Triangle secondTriangle(Graphics::Vec2( 0.0f, 0.0f),
Graphics::Vec2(-1.0f, -1.0f),
Graphics::Vec2( 1.0f, -1.0f));
window.SetBackground(0.0f, 0.0f, 0.0f, 1.0f);
while (!window.isClosed() && !window.isKeyPressed(256)/*Escape*/)
{
window.DrawBackGround();
firstTriangle.Draw();
secondTriangle.Draw();
window.Update();
window.CheckForEvents();
}
return 0;
}
Here is what the constructor looks like for the triangle class:
namespace Graphics { namespace Objects {
Triangle::Triangle(Vec2& a, Vec2& b, Vec2& c, bool normalised, short drawtype)
: points{ a, b, c }
{
glGenBuffers(1, &this->triangleID);
glBindBuffer(GL_ARRAY_BUFFER, this->triangleID);
glBufferData(GL_ARRAY_BUFFER, sizeof(Vec2) * 3, points, (drawtype == 0) ? GL_STATIC_DRAW : ((drawtype == 1) ? GL_STREAM_DRAW : GL_DYNAMIC_DRAW));
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, (normalised == false) ? GL_FALSE : GL_TRUE, sizeof(Vec2), 0);
}
}
}
And this is the draw method for the triangle class:
void Triangle::Draw()
{
glBindBuffer(GL_ARRAY_BUFFER, this->triangleID);
glDrawArrays(GL_TRIANGLES, 0, 3);
}
This is the prototype for the Triangle class if that helps:
class Triangle : public Shape
{
public:
Triangle();
Triangle(Vec2& a, Vec2& b, Vec2& c, bool normalised = false, short drawtype = 0);
virtual ~Triangle();
void Prepare(); // this just binds the buffer though I didn't use it since I bound the buffer inside the Draw method
void Draw();
private:
Vec2 points[3];
unsigned int triangleID;
}
I will give away more code if necessary, but the thing is that the program only draws the secondTriangle and I don't seem to understand why... The majority of tutorials on the internet that I have come accross just define something like an array of floats with 6 vertex positions and instead of calling
glDrawArrays(GL_TRIANGLES, 0, 3)
they call
glDrawArrays(GL_TRIANGLES, 0, 6); // 12 etc.
What is stopping the first triangle from being drawn? Or is it being drawn and I can't see it? Am I doing anything stupid?
Upvotes: 2
Views: 4533
Reputation: 210918
e.g. Buffers for vertices (x, y, z), normals (x, y, z) and texture oordinates (u, v):
Draw the array:
GLsizei no_of_points; // number of vertices and attrbuts
std::vector<GLfloat> vertex; // linearized array (no_of_points * 3): [ Vx0, Vy0, Vz0, Vx1, Vy1, Vz1, .... ]
std::vector<GLfloat> normal; // linearized array (no_of_points * 3): [ Nx0, Ny0, Nz0, Nx1, Ny1, Nz1, .... ]
std::vector<GLfloat> color; // linearized array (no_of_points * 5): [ R0, G0, B0, A0, R1, G1, B1, A1, .... ]
GLuint vetexAttribIndex; // index of the vertex attrbute (shader)
GLuint normalAttribIndex; // index of the normal attribute (shader)
GLuint colorAttribIndex; // index of the color attribute (shader)
glVertexAttribPointer( vetexAttribIndex, 3, GL_FLOAT, GL_FALSE, 0, vertex.data() ); // 3: Vx, Vy, Vz
glVertexAttribPointer( normalAttribIndex, 3, GL_FLOAT, GL_TRUE, 0, normal.data() ); // 3: Nx, Ny, Nz - GL_TRUE: values should be normalized
glVertexAttribPointer( colorAttribIndex, 4, GL_FLOAT, GL_FALSE, 0, color.data() ); // 4: R, G, B, A
glEnableVertexAttribArray( vetexAttribIndex );
glEnableVertexAttribArray( normalAttribIndex );
glEnableVertexAttribArray( colorAttribIndex );
glDrawArrays( GL_TRIANGLES, 0, no_of_points );
glDisableVertexAttribArray( vetexAttribIndex );
glDisableVertexAttribArray( normalAttribIndex );
glDisableVertexAttribArray( colorAttribIndex );
See:
The Khronos OpenGL wiki about Vertex Specification clearly says:
The
glVertexAttribPointer
functions state where an attribute index gets its array data from.
This state can be retrieved with glGetVertexAttrib
.
More information about vertex attrbutes can be found in the OpenGL 4.6. core specification from Chapter 10.2 to 10.6.
e.g. Vertex, Normal vector and Texture coordiante
[ Vx0, Vy0, Vz0, Nx0, Ny0, Nz0, Tv0, Tu0,
Vx1, Vy1, Vz1, Nx1, Ny1, Nz1, Tv1, Tu1,
.....
]
Create the vertex array buffer:
GLsizei no_of_points;
std::vector<GLfloat> data; // attribute set: [ Vx0, Vy0, Vz0, Nx0, Ny0, Nz0, Tv0, Tu0, Vx1, Vy1, Vz1, Nx1, Ny1, Nz1, Tv1, Tu1, .... ]
GLuint vbo;
glGenBuffers( 1, &vbo );
glBindBuffer( GL_ARRAY_BUFFER, vbo );
glBufferData( GL_ARRAY_BUFFER, data.size() * sizeof(GLfloat), data.data(), GL_STATIC_DRAW );
glBindBuffer( GL_ARRAY_BUFFER, 0 );
See:
Draw the array:
GLuint vetexAttribIndex; // index of the vertex attrbute (shader)
GLuint normalAttribIndex; // index of the normal attribute (shader)
GLuint texCorAttribIndex; // index of the texture coordinate attribute (shader)
glBindBuffer( GL_ARRAY_BUFFER, vbo );
GLsizei stride = 8 * sizeof(GL_float); // size of one record in bytes: 8 * float [ Vx, Vy, Vz, Nx, Ny, Nz, Tv, Tu]
GLsizei offsV = 0 * sizeof(GL_float); // offset of the vertex inside the reccord
GLsizei offsNV = 3 * sizeof(GL_float); // offset of the normal vector inside the reccord
GLsizei offsTC = 6 * sizeof(GL_float); // offset of the tecture coordinate inside the reccord
glVertexAttribPointer( vetexAttribIndex, 3, GL_FLOAT, GL_FALSE, stride, offsV ); // 3: Vx, Vy, Vz
glVertexAttribPointer( normalAttribIndex, 3, GL_FLOAT, GL_TRUE, stride, offsNV ); // 3: Nx, Ny, Nz - GL_TRUE: values should be normalized
glVertexAttribPointer( texCorAttribIndex, 2, GL_FLOAT, GL_FALSE, stride, offsTC ); // 2: Tu, Tv
glEnableVertexAttribArray( vetexAttribIndex );
glEnableVertexAttribArray( normalAttribIndex );
glEnableVertexAttribArray( texCorAttribIndex );
glDrawArrays( GL_TRIANGLES, 0, no_of_points );
glBindBuffer( GL_ARRAY_BUFFER, 0 );
glDisableVertexAttribArray( vetexAttribIndex );
glDisableVertexAttribArray( normalAttribIndex );
glDisableVertexAttribArray( texCorAttribIndex );
e.g. Vertex, Normal vector and Texture coordiante
[ Vx0, Vy0, Vz0, Nx0, Ny0, Nz0, Tv0, Tu0,
Vx1, Vy1, Vz1, Nx1, Ny1, Nz1, Tv1, Tu1,
.....
]
Create the vertex array buffer and the index buffer:
GLsizei no_of_points;
std::vector<GLfloat> data; // attribute set: [ Vx0, Vy0, Vz0, Nx0, Ny0, Nz0, Tv0, Tu0, Vx1, Vy1, Vz1, Nx1, Ny1, Nz1, Tv1, Tu1, .... ]
std::vector<GLuint> indices; // indces: [ I0, I1, I2, I3, I4, ..... ]
GLuint vbo;
glGenBuffers( 1, &vbo );
glBindBuffer( GL_ARRAY_BUFFER, vbo );
glBufferData( GL_ARRAY_BUFFER, data.size() * sizeof(GLfloat), data.data(), GL_STATIC_DRAW );
glBindBuffer( GL_ARRAY_BUFFER, 0 );
GLuint ibo;
glGenBuffers( 1, &ibo );
glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, ibo );
glBufferData( GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(GLuint), indices.data(), GL_STATIC_DRAW );
glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 );
Draw the array:
GLuint vetexAttribIndex; // index of the vertex attrbute (shader)
GLuint normalAttribIndex; // index of the normal attribute (shader)
GLuint texCorAttribIndex; // index of the texture coordinate attribute (shader)
glBindBuffer( GL_ARRAY_BUFFER, vbo );
GLsizei stride = 8 * sizeof(GL_float); // size of one record in bytes: 8 * float [ Vx, Vy, Vz, Nx, Ny, Nz, Tv, Tu]
GLsizei offsV = 0 * sizeof(GL_float); // offset of the vertex inside the reccord
GLsizei offsNV = 3 * sizeof(GL_float); // offset of the normal vector inside the reccord
GLsizei offsTC = 6 * sizeof(GL_float); // offset of the tecture coordinate inside the reccord
glVertexAttribPointer( vetexAttribIndex, 3, GL_FLOAT, GL_FALSE, stride, offsV ); // 3: Vx, Vy, Vz
glVertexAttribPointer( normalAttribIndex, 3, GL_FLOAT, GL_TRUE, stride, offsNV ); // 3: Nx, Ny, Nz - GL_TRUE: values should be normalized
glVertexAttribPointer( texCorAttribIndex, 2, GL_FLOAT, GL_FALSE, stride, offsTC ); // 2: Tu, Tv
glEnableVertexAttribArray( vetexAttribIndex );
glEnableVertexAttribArray( normalAttribIndex );
glEnableVertexAttribArray( texCorAttribIndex );
glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, ibo );
glDrawElements( GL_TRIANGLES, (GLsizei)indices.size(), GL_UNSIGNED_INT, nullptr );
glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 );
glBindBuffer( GL_ARRAY_BUFFER, 0 );
glDisableVertexAttribArray( vetexAttribIndex );
glDisableVertexAttribArray( normalAttribIndex );
glDisableVertexAttribArray( texCorAttribIndex );
To handle different vertex attribute pointers and not to specify and enable or disable them alternately, a vertex array object can be generated (glGenVertexArrays
, which stores all the information about buffer location, data format, state and attribute index:
See OpenGL 4.6 core Specification - 10.3.1 Vertex Array Objects:
The buffer objects that are to be used by the vertex stage of the GL are collected together to form a vertex array object. All state related to the definition of data used by the vertex processor is encapsulated in a vertex array object.
....
The currently bound vertex array object is used for all commands which modify vertex array state, such as VertexAttribPointer and EnableVertexAttribArray; all commands which draw from vertex arrays, such as DrawArrays and DrawElements;
e.g. Vertex, Normal vector and Texture coordiante
[ Vx0, Vy0, Vz0, Nx0, Ny0, Nz0, Tv0, Tu0,
Vx1, Vy1, Vz1, Nx1, Ny1, Nz1, Tv1, Tu1,
.....
]
Create the vertex array buffer and the index buffer:
GLsizei no_of_points;
std::vector<GLfloat> data; // attribute set: [ Vx0, Vy0, Vz0, Nx0, Ny0, Nz0, Tv0, Tu0, Vx1, Vy1, Vz1, Nx1, Ny1, Nz1, Tv1, Tu1, .... ]
std::vector<GLuint> indices; // indces: [ I0, I1, I2, I3, I4, ..... ]
GLuint vbo;
glGenBuffers( 1, &vbo );
glBindBuffer( GL_ARRAY_BUFFER, vbo );
glBufferData( GL_ARRAY_BUFFER, data.size() * sizeof(GLfloat), data.data(), GL_STATIC_DRAW );
glBindBuffer( GL_ARRAY_BUFFER, 0 );
GLuint ibo;
glGenBuffers( 1, &ibo );
glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, ibo );
glBufferData( GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(GLuint), indices.data(), GL_STATIC_DRAW );
glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 );
Create the vertex array object:
GLuint vao;
glGenVertexArrays( 1, &vao );
glBindVertexArray( vao );
GLuint vetexAttribIndex; // index of the vertex attrbute (shader)
GLuint normalAttribIndex; // index of the normal attribute (shader)
GLuint texCorAttribIndex; // index of the texture coordinate attribute (shader)
glBindBuffer( GL_ARRAY_BUFFER, vbo );
GLsizei stride = 8 * sizeof(GL_float); // size of one record in bytes: 8 * float [ Vx, Vy, Vz, Nx, Ny, Nz, Tv, Tu]
GLsizei offsV = 0 * sizeof(GL_float); // offset of the vertex inside the reccord
GLsizei offsNV = 3 * sizeof(GL_float); // offset of the normal vector inside the reccord
GLsizei offsTC = 6 * sizeof(GL_float); // offset of the tecture coordinate inside the reccord
glVertexAttribPointer( vetexAttribIndex, 3, GL_FLOAT, GL_FALSE, stride, offsV ); // 3: Vx, Vy, Vz
glVertexAttribPointer( normalAttribIndex, 3, GL_FLOAT, GL_TRUE, stride, offsNV ); // 3: Nx, Ny, Nz - GL_TRUE: values should be normalized
glVertexAttribPointer( texCorAttribIndex, 2, GL_FLOAT, GL_FALSE, stride, offsTC ); // 2: Tu, Tv
glEnableVertexAttribArray( vetexAttribIndex );
glEnableVertexAttribArray( normalAttribIndex );
glEnableVertexAttribArray( texCorAttribIndex );
glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, ibo ); // Associate the element array buffer (index buffer) to the vertex array object
glBindVertexArray( 0 ); // Unbind the vertex array object
glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 ); // Unbinde the element array buffer. This has to be done after the vertex array object is unbound, otherwise the association to the vertex array object would be lost.
See:
Draw the array:
glBindVertexArray( vao );
glDrawElements( GL_TRIANGLES, (GLsizei)indices.size(), GL_UNSIGNED_INT, nullptr );
glBindVertexArray( 0 );
Note, in compare to the index buffer (ELEMENT_ARRAY_BUFFER
), the vertex buffer binding (ARRAY_BUFFER
) is a global state.
Each attribute which is stated in the VAOs state vector may refer to a different ARRAY_BUFFER
. This reference is stored when glVertexAttribPointer
is called, then the buffer which is currently bound to the target ARRAY_BUFFER
, is associated to the specified attribute index and the name (value) of the object is stored in the state vector of the currently bound VAO.
But the index buffer is a state of the VAO. When a buffer is bound to the target ELEMENT_ARRAY_BUFFER
, then this buffer is associated to the vertex array object which is currently bound.
Upvotes: 3