5/21

2. Display a triangle

The next step consists in using OpenGL functions to display a 3D shape in the window. We will consider in this example a minimalistic static triangle with uniform color.

OpenGL programs can generally be viewed as three steps

  • Initialization step

    • Setting the shader programs (1)

    • Sending data to GPU (2)

  • Displaying data (3)

The two steps from the initialization part can be performed in any order, while the display is usually performed in the animation loop and requires the two previous steps.


The full code of the function is provided below, however each new part of this code will be explained in the next subparts.

triangle

[ code, main.cpp ]

#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <iostream>
#include <vector>


// Function to be called when OpenGL detects an error
void APIENTRY error_opengl_callback( GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, const void* userParam);


int main()
{
    std::cout<<"*** Init GLFW ***"<<std::endl;
    const int glfw_init_value = glfwInit();
    if( glfw_init_value != GLFW_TRUE ) {
        std::cerr<<"Failed to Init GLFW"<<std::endl;
        abort();
    }


    std::cout<<"*** Create window ***"<<std::endl;
    const int window_width  = 500;
    const int window_height = 500;
    const std::string title = "My Window";
    GLFWwindow* window = glfwCreateWindow(window_width, window_height, title.c_str(), /*monitor*/ nullptr, /*share*/ nullptr);
    if( window==nullptr ) {
        std::cerr<<"Failed to create GLFW Window"<<std::endl;
        abort();
    }

    glfwMakeContextCurrent(window);

    std::cout<<"*** Init GL3w ***"<<std::endl;
    const int glad_init_value = gladLoadGL();
    if( glad_init_value == 0 ) {
        std::cerr<<"Failed to Init GL3W"<<std::endl;
        abort();
    }

    std::cout<<"*** Set OpenGL error callback ***"<<std::endl;
    glEnable              ( GL_DEBUG_OUTPUT );
    glDebugMessageCallback( (GLDEBUGPROC) error_opengl_callback, 0 );
    glDebugMessageControl ( GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, NULL, true );

    std::cout<<"OpenGL information: VENDOR      : "<<glGetString(GL_VENDOR)<<std::endl;
    std::cout<<"                    RENDERDER   : "<<glGetString(GL_RENDERER)<<std::endl;
    std::cout<<"                    VERSION     : "<<glGetString(GL_VERSION)<<std::endl;
    std::cout<<"                    GLSL VERSION: "<<glGetString(GL_SHADING_LANGUAGE_VERSION)<<std::endl;



    // ************************************************* //
    //             1 - Setup Shaders                     //
    // ************************************************* //

    std::cout<<"*** Setup Shader ***"<<std::endl;

    // ******************************** //
    // 1.1 Define vertex and fragment shader
    // ******************************** //

    const char* vertex_shader_txt = "                                      \n \
            #version 430 core                                              \n \
            layout (location = 0) in vec4 position;                        \n \
            void main()                                                    \n \
            {                                                              \n \
                gl_Position = position;                                    \n \
            }";
    const char* fragment_shader_txt = "                                    \n \
            #version 430 core                                              \n \
            out vec4 FragColor;                                            \n \
            void main()                                                    \n \
            {                                                              \n \
                FragColor = vec4(1.0, 0.0, 0.0, 1.0);                      \n \
            }";



    // ******************************** //
    // 1.2 Create shader program
    // ******************************** //

    // (Warning: the following code doesn't perform error checking)

    //  A. Compile each shader separately
    // ******************************************* //

    const GLuint vertex_shader   = glCreateShader(GL_VERTEX_SHADER);
    const GLuint fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);

    glShaderSource(vertex_shader, 1, &vertex_shader_txt, nullptr);
    glCompileShader(vertex_shader);

    glShaderSource(fragment_shader, 1, &fragment_shader_txt, nullptr);
    glCompileShader(fragment_shader);

    //  B. Link shaders into shader program
    // ******************************************* //

    const GLuint shader = glCreateProgram();
    glAttachShader(shader, vertex_shader);
    glAttachShader(shader, fragment_shader);
    glLinkProgram(shader);

    glDeleteShader(vertex_shader);
    glDeleteShader(fragment_shader);


    // ************************************************* //
    //           2 - Sending data to GPU                 //
    // ************************************************* //

    std::cout<<"*** Setup Data ***"<<std::endl;

    // 2.1 Setup contiguous array of floating point value
    // ******************************************* //
    const std::vector<GLfloat> position = {
        -0.5f, -0.5f, 0.0f,
         0.5f, -0.5f, 0.0f,
         0.0f,  0.5f, 0.0f
    };

    // 2.2 Create VBO - Send data to GPU
    // ******************************************* //

    GLuint vbo = 0;
    glGenBuffers(1, &vbo);
    glBindBuffer(GL_ARRAY_BUFFER, vbo);
    glBufferData(GL_ARRAY_BUFFER, position.size()*sizeof(GLfloat), &position[0], GL_STATIC_DRAW );
    glBindBuffer(GL_ARRAY_BUFFER, 0);

    // 2.3 Create VAO - Relation between VBO organization and input variables of shaders
    // ******************************************* //
    GLuint vao = 0;
    glGenVertexArrays(1,&vao);
    glBindVertexArray(vao);
    glBindBuffer(GL_ARRAY_BUFFER, vbo);
    glEnableVertexAttribArray( 0 );
    glVertexAttribPointer( 0, 3, GL_FLOAT, GL_FALSE, 0, nullptr );
    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glBindVertexArray(0);






    // ******************************** //
    // Main loop
    // ******************************** //

    std::cout<<"*** Start GLFW loop ***"<<std::endl;
    while( !glfwWindowShouldClose(window) ) {

        // ************************************************* //
        //           3 - Displaying Data                     //
        // ************************************************* //

        glUseProgram(shader);             // Activate shader program
        glBindVertexArray(vao);           // Activate attributes for the drawing
        glDrawArrays(GL_TRIANGLES, 0, 3); // Draw 3 vertices
        glBindVertexArray(0);
        glUseProgram(0);


        glfwSwapBuffers(window);
        glfwPollEvents();
    }
    std::cout<<"*** Terminate GLFW loop ***"<<std::endl;


    glfwTerminate();

    return 0;
}


// Function to be called when OpenGL detects an error
// See https://www.khronos.org/opengl/wiki/Debug_Output
void APIENTRY error_opengl_callback( GLenum /*source*/,
                                   GLenum /*type*/,
                                   GLuint /*id*/,
                                   GLenum severity,
                                   GLsizei /*length*/,
                                   const GLchar* message,
                                   const void* /*userParam*/ )
{
    if( severity != GL_DEBUG_SEVERITY_NOTIFICATION ) {

        // Print all error messages which are not notification

        std::cerr << "\n**** Error OpenGL Callback **** \n  " << message;

    }
}