Alexandru Ica
Alexandru Ica

Reputation: 139

Drawing multiple triangles in OpenGL

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

Answers (1)

Rabbid76
Rabbid76

Reputation: 210918

Vertex array

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:


Vertex buffer object

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 );

Index buffer object

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 );

Vertex array object

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

Related Questions