c++openglglslshadergl-triangle-strip

How to draw a VAO made out of 5 triangles in OpenGL?


I have recently written a program to draw a triangle with 3 different RGB values and I want to do the same with another separate VAO in the same program but I want this one composed of 5 triangles. Here is my main.cpp:

void framebuffer_size_callback(GFLWwindow* window, int width, int height);
void processInput(GLFWwindow *window);

// Shaders
const char *vertexShaderSource =
"#version 410\n"
"in vec3 vp;\n"
"void main()\n"
"{\n"
"gl_Position = vec4(aPos, 1.0);\n"
"}\0";

const char *fragmentShader1Source =
"#version 410\n"
"out vec4 FragColor;\n"
"in vec3 myColor;\n"
"void main()\n"
"{\n"
"FragColor = vec4(myColor, 1.0f);\n"
"}\n\0";

int main ()
{
// start GL context and O/S window using the GLFW helper library
if (!glfwInit ())
    {
    fprintf (stderr, "ERROR: could not start GLFW3\n");
    return 1;
    }
    // uncomment these lines if on Apple OS X
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

    GLFWwindow* window = glfwCreateWindow(640, 480, "LearnOpenGL", NULL, NULL);

    if (!window)
    {
        fprintf(stderr, "ERROR: could not open window with GLFW3\n");
        glfwTerminate();
        return 1;
    }
    glfwMakeContextCurrent(window);

    // start GLEW extension handler
    glewExperimental = GL_TRUE;
    glewInit();

    // get version info
    const GLubyte* renderer = glGetString(GL_RENDERER);
    const GLubyte* version = glGetString(GL_VERSION);

    printf("Renderer: %s\n", renderer);
    printf("OpenGL version supported %s\n", version);

    glEnable(GL_DEPTH_TEST); // enable depth-testing
    glDepthFunc(GL_LESS);

    /* OTHER STUFF GOES HERE */

    // Draw a single triangle VBO
    float points[] = {
    // positions       // colors
    0.0f, 0.5f, 0.0f,  1.0f, 0.0f, 0.0f,
    0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f,
    -0.5f, -0.5f, 0.0f, 0.0f 0.0f, 1.0f
    };

    GLuint VBO = 0;
    glGenBuffers(1, &VBO);
    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(points), points, GL_STATIC_DRAW);

    // Generate a VAO.
    GLuint VAO = 0;
    glGenVertexArrays(1, &VAO);
    glBindVertexArray(VAO);
    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(points), points, GL_STATIC_DRAW);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, NULL);
    glEnableVertexAttribArray(0);

    // Compile a Vertex Shader
    int vertexShader = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
    glCompileShader(vertexShader);

    // Compile a fragment shader.
    int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
    glCompileShader(fragmentShader);

    // Compile shaders into a executable shader program.
    int shaderProgram = glCreateProgram();
    glAttachShader(shaderProgram, fragmentShader);
    glAttachShader(shaderProgram, vertexShader);
    glLinkProgram(shaderProgram);

    // Drawing the triangles aka render loop
    while (!glfwWindowShouldClose(window))
    {
        processInput(window);
        // wipe the drawing surface clear
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        // Draw Triangle
        glUseProgram(shaderProgram);
        glBindVertexArray(VAO);
        glDrawArrays(GL_TRIANGLES, 0, 3);

        // Draw Triangle Fan; unfinished

        // swap buffers and poll IO events
        glfwPollEvents();
        glfwSwapBuffers(window);
    }

    // close GL context and any other GLFW resources
    glfwTerminate();
    return 0;
}

Do I simply create another float "points" matrix like I did with my first VBO or something else? The tutorial Im following wasn't perfectly clear on this part.

Also, Im using Xcode on my Mac and I created separate .cpp files for my Fragment and Vertex shaders. Should I switch those to header files instead?


Solution

  • You have to specify an input attribute for the color (aColor) and to pass the color attribute from the vertex shader to the fragment shader (myColor). Use Layout Qualifiers to specify the attribute indices.

    #version 330 core
    
    layout(location = 0) in vec3 aPos;
    layout(location = 1) in vec3 aColor;
    
    out vec3 myColor;
    
    void main()
    {
        myColor = aColor;
        gl_Position = vec4(aPos, 1.0);
    }
    
    #version 330 core
    
    out vec4 FragColor;
    in vec3 myColor;
    
    void main()
    {
        FragColor = vec4(myColor, 1.0f);
    }
    

    Note your current vertex shader does not compile. Check if compiling of a shader succeeded checked by glGetShaderiv and the parameter GL_COMPILE_STATUS and if the linking of a program was successful can be checked by glGetProgramiv and the parameter GL_LINK_STATUS. See the answer to OpenGL ignores Quads and makes them Triangles for some code snippets.


    Your vertices are tuples with 6 components (x, y, z, r, g, b):

    float points[] = {
        // positions       // colors
        0.0f, 0.5f, 0.0f,  1.0f, 0.0f, 0.0f,
        0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f,
       -0.5f, -0.5f, 0.0f, 0.0f 0.0f, 1.0f
    };
    

    Use glVertexAttribPointer to specify 2 vertex attributes. The stride and the offset have to be specified in bytes. The stride is 6 * sizeof(float). The offset of the vertex coordinates is 0 and the offset of the color attributes is 3 * sizeof(float). e.g:

    GLuint VBO = 0;
    glGenBuffers(1, &VBO);
    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(points), points, GL_STATIC_DRAW);
    
    // Generate a VAO.
    GLuint VAO = 0;
    glGenVertexArrays(1, &VAO);
    glBindVertexArray(VAO);
    
    glEnableVertexAttribArray(0);
    glEnableVertexAttribArray(1);
    
    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), NULL);
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3 * sizeof(float)));
    

    If you want to draw more complex meshes, then you have to extend the vertes arrays. Just add another 3 vertices and colors to points array for the next triangle. Alternatively you can use a different primitive type like GL_TRIANGLE_STRIP or GL_TRIANGLE_FAN. See Triangle primitives

    Example code:

    #include <iostream>
    #include <vector>
    
    // Shaders
    const char *vertexShaderSource = R"(#version 330 core
    
    layout(location = 0) in vec3 aPos;
    layout(location = 1) in vec3 aColor;
    
    out vec3 myColor;
    
    void main()
    {
        myColor = aColor;
        gl_Position = vec4(aPos, 1.0);
    }
    )";
    
    const char *fragmentShaderSource = R"(#version 330 core
    
    out vec4 FragColor;
    in vec3 myColor;
    
    void main()
    {
        FragColor = vec4(myColor, 1.0f);
    }
    )";
    
    bool CompileStatus( GLuint shader );
    bool LinkStatus( GLuint program );
    
    float radians( float deg ) { return deg * 3.141529 / 180.0; }
    
    int main ()
    {
        // start GL context and O/S window using the GLFW helper library
        if (!glfwInit())
        {
            fprintf (stderr, "ERROR: could not start GLFW3\n");
            return 1;
        }
        // uncomment these lines if on Apple OS X
        glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
        glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
        glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
        glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    
        GLFWwindow* window = glfwCreateWindow(640, 480, "LearnOpenGL", NULL, NULL);
    
        if (!window)
        {
            fprintf(stderr, "ERROR: could not open window with GLFW3\n");
            glfwTerminate();
            return 1;
        }
        glfwMakeContextCurrent(window);
    
        // start GLEW extension handler
        glewExperimental = GL_TRUE;
        glewInit();
    
        // get version info
        const GLubyte* renderer = glGetString(GL_RENDERER);
        const GLubyte* version = glGetString(GL_VERSION);
    
        printf("Renderer: %s\n", renderer);
        printf("OpenGL version supported %s\n", version);
    
        glEnable(GL_DEPTH_TEST); // enable depth-testing
        glDepthFunc(GL_LESS);
    
        /* OTHER STUFF GOES HERE */
    
        // Draw a single triangle VBO
        float points[] = {
            // positions                                         // colors
            0.0f,                     0.0f,                      0.0f,   1.0f, 0.0f, 0.0f,
            0.5f,                     0.0f,                      0.0f,   0.0f, 1.0f, 0.0f,
            0.5f * cos(radians(72)),  0.5f * sin(radians(72)),   0.0f,   0.0f, 0.0f, 1.0f,
            0.5f * cos(radians(144)), 0.5f * sin(radians(144)),  0.0f,   1.0f, 1.0f, 0.0f,
            0.5f * cos(radians(216)), 0.5f * sin(radians(216)),  0.0f,   0.0f, 1.0f, 1.0f,
            0.5f * cos(radians(288)), 0.5f * sin(radians(288)),  0.0f,   1.0f, 0.0f, 1.0f,
            0.5,                      0.0f,                      0.0f,   1.0f, 0.5f, 0.0f
        };
    
        GLuint VBO = 0;
        glGenBuffers(1, &VBO);
        glBindBuffer(GL_ARRAY_BUFFER, VBO);
        glBufferData(GL_ARRAY_BUFFER, sizeof(points), points, GL_STATIC_DRAW);
    
        // Generate a VAO.
        GLuint VAO = 0;
        glGenVertexArrays(1, &VAO);
        glBindVertexArray(VAO);
    
        glEnableVertexAttribArray(0);
        glEnableVertexAttribArray(1);
    
        glBindBuffer(GL_ARRAY_BUFFER, VBO);
        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), NULL);
        glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3 * sizeof(float)));
    
        // Compile a Vertex Shader
        int vertexShader = glCreateShader(GL_VERTEX_SHADER);
        glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
        glCompileShader(vertexShader);
        CompileStatus( vertexShader );
    
        // Compile a fragment shader.
        int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
        glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
        glCompileShader(fragmentShader);
        CompileStatus( fragmentShader );
    
        // Compile shaders into a executable shader program.
        int shaderProgram = glCreateProgram();
        glAttachShader(shaderProgram, fragmentShader);
        glAttachShader(shaderProgram, vertexShader);
        glLinkProgram(shaderProgram);
        LinkStatus( shaderProgram );
    
        // Drawing the triangles aka render loop
        while (!glfwWindowShouldClose(window))
        {
            //processInput(window);
            // wipe the drawing surface clear
            glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    
            // Draw Triangle
            glUseProgram(shaderProgram);
            glBindVertexArray(VAO);
            glDrawArrays(GL_TRIANGLE_FAN, 0, 7);
    
            // Draw Triangle Fan; unfinished
    
            // swap buffers and poll IO events
            glfwPollEvents();
            glfwSwapBuffers(window);
        }
    
        // close GL context and any other GLFW resources
        glfwTerminate();
        return 0;
    }
    
    bool CompileStatus( GLuint shader )
    {
        GLint status = GL_TRUE;
        glGetShaderiv( shader, GL_COMPILE_STATUS, &status );
        if (status == GL_FALSE)
        {
            GLint logLen;
            glGetShaderiv( shader, GL_INFO_LOG_LENGTH, &logLen );
            std::vector< char >log( logLen );
            GLsizei written;
            glGetShaderInfoLog( shader, logLen, &written, log.data() );
            std::cout << "compile error:" << std::endl << log.data() << std::endl;
        }
        return status != GL_FALSE;
    }
    
    bool LinkStatus( GLuint program )
    {
        GLint status = GL_TRUE;
        glGetProgramiv( program, GL_LINK_STATUS, &status );
        if (status == GL_FALSE)
        {
            GLint logLen;
            glGetProgramiv( program, GL_INFO_LOG_LENGTH, &logLen );
            std::vector< char >log( logLen );
            GLsizei written;
            glGetProgramInfoLog( program, logLen, &written, log.data() );
            std::cout << "link error:" << std::endl << log.data() << std::endl;
        }
        return status != GL_FALSE;
    }