-1

Is it possible to use the Visual Studio debugger to debug my GLFW/OpenGL code? I get an exception on a glDrawElements() call which provides useless info. Obviously I have not set things up correctly, but with errors occurring in library calls, how do I find out what I did wrong? Is there a way to take advantage of the availability of the GLFW source? I just set up the VS project the way GLFW directs.

The code is a modified version of one of learnopengl.com's first examples, so it is pretty simple. I am in the middle of trying to convert drawing of the first triangle from just VBO/VAO buffers to using an EBO buffer.

I commented the line that throws this in RenderAll() near the bottom.

Exception thrown at 0x00007FFC6A0F4A03 (nvoglv64.dll) in GLFW3TestProj.exe: 0xC0000005: Access violation reading location 0x0000000000000000.
#include <glad/glad.h>
#include <GLFW/glfw3.h>

#include <iostream>
using namespace std;

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

typedef struct {
    GLFWwindow* window;
    unsigned int shaderProgram1;
    unsigned int shaderProgram2;
    unsigned int VBO[2];
    unsigned int VAO[2];
    unsigned int EBO[2];
} T_RenderContext;
void RenderAll();
T_RenderContext renderContext;

int main()
{
    // glfw: initialize and configure
    // ------------------------------
    glfwInit();
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

#ifdef __APPLE__
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#endif

    // glfw window creation
    // --------------------
    renderContext.window = glfwCreateWindow(1250, 1000, "LearnOpenGL", NULL, NULL);
    if (renderContext.window == NULL)
    {
        std::cout << "Failed to create GLFW window" << std::endl;
        glfwTerminate();
        return -1;
    }
    glfwMakeContextCurrent(renderContext.window);
    glfwSetFramebufferSizeCallback(renderContext.window, framebuffer_size_callback);

    // glad: load all OpenGL function pointers
    // ---------------------------------------
    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
    {
        std::cout << "Failed to initialize GLAD" << std::endl;
        return -1;
    }


    // build and compile our shader program
    // ------------------------------------
    // vertex shader
    const char* vertexShaderSource = "#version 330 core\n"
        "layout (location = 0) in vec3 aPos;\n"
        "void main()\n"
        "{\n"
        "   gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n"
        "}\0";
    unsigned int vertexShader = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
    glCompileShader(vertexShader);
    // check for shader compile errors
    int success;
    char infoLog[512];
    glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
    if (!success)
    {
        glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);
        std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl;
    }
    // fragment shader 1
    const char* fragmentShaderSource1 = "#version 330 core\n"
        "out vec4 FragColor;\n"
        "void main()\n"
        "{\n"
        "   FragColor = vec4(0.937f, 0.608f, 0.0f, 1.0f);\n"
        "}\n\0";
    unsigned int fragmentShader1 = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fragmentShader1, 1, &fragmentShaderSource1, NULL);
    glCompileShader(fragmentShader1);
    // check for shader compile errors
    glGetShaderiv(fragmentShader1, GL_COMPILE_STATUS, &success);
    if (!success)
    {
        glGetShaderInfoLog(fragmentShader1, 512, NULL, infoLog);
        std::cout << "ERROR::SHADER::FRAGMENT1::COMPILATION_FAILED\n" << infoLog << std::endl;
    }
    // fragment shader 2
    const char* fragmentShaderSource2 = "#version 330 core\n"
        "out vec4 FragColor;\n"
        "void main()\n"
        "{\n"
        "   FragColor = vec4(0.937f, 0.0f, 0.608f, 1.0f);\n"
        "}\n\0";
    unsigned int fragmentShader2 = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fragmentShader2, 1, &fragmentShaderSource2, NULL);
    glCompileShader(fragmentShader2);
    // check for shader compile errors
    glGetShaderiv(fragmentShader2, GL_COMPILE_STATUS, &success);
    if (!success)
    {
        glGetShaderInfoLog(fragmentShader2, 512, NULL, infoLog);
        std::cout << "ERROR::SHADER::FRAGMENT2::COMPILATION_FAILED\n" << infoLog << std::endl;
    }
    // link shaders
    renderContext.shaderProgram1 = glCreateProgram();
    glAttachShader(renderContext.shaderProgram1, vertexShader);
    glAttachShader(renderContext.shaderProgram1, fragmentShader1);
    glLinkProgram(renderContext.shaderProgram1);

    renderContext.shaderProgram2 = glCreateProgram();
    glAttachShader(renderContext.shaderProgram2, vertexShader);
    glAttachShader(renderContext.shaderProgram2, fragmentShader2);
    glLinkProgram(renderContext.shaderProgram2);

    // check for linking errors
    glGetProgramiv(renderContext.shaderProgram1, GL_LINK_STATUS, &success);
    if (!success) {
        glGetProgramInfoLog(renderContext.shaderProgram1, 512, NULL, infoLog);
        std::cout << "ERROR::SHADER::PROGRAM1::LINKING_FAILED\n" << infoLog << std::endl;
    }
    glGetProgramiv(renderContext.shaderProgram2, GL_LINK_STATUS, &success);
    if (!success) {
        glGetProgramInfoLog(renderContext.shaderProgram2, 512, NULL, infoLog);
        std::cout << "ERROR::SHADER::PROGRAM2::LINKING_FAILED\n" << infoLog << std::endl;
    }
    glDeleteShader(vertexShader);
    glDeleteShader(fragmentShader1);
    glDeleteShader(fragmentShader2);


    // set up vertex data (and buffer(s)) and configure vertex attributes
    // ------------------------------------------------------------------
    typedef struct S_Triplet {
        float x; float y; float z;
    } T_Triplet;
    
    T_Triplet triangle1[3];
    triangle1[0] = { -0.5F, 0.1F, 0.0F };
    triangle1[1] = { 0.5F, 0.2F, 0.0F };
    triangle1[2] = { 0.0F, 0.866F, 0.0F };

    T_Triplet triangle2[3];
    triangle2[0] = { -0.5F, -0.2F, 0.0F };
    triangle2[1] = { 0.5F, -0.1F, 0.0F };
    triangle2[2] = { 0.0F, -0.866F, 0.0F };

    unsigned int indices1[] = { 0, 1, 2 };
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, renderContext.EBO[0]);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices1), indices1, GL_STATIC_DRAW);
    unsigned int indices2[] = { 0, 1, 2 };
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, renderContext.EBO[1]);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices2), indices2, GL_STATIC_DRAW);

    glGenVertexArrays(2, renderContext.VAO);
    glGenBuffers(2, renderContext.VBO);
    glGenVertexArrays(2, renderContext.VAO); // we can also generate multiple VAOs or buffers at the same time
    // first triangle setup
    // --------------------
    glBindVertexArray(renderContext.VAO[0]);
    glBindBuffer(GL_ARRAY_BUFFER, renderContext.VBO[0]);
    glBufferData(GL_ARRAY_BUFFER, sizeof(triangle1), triangle1, GL_STATIC_DRAW);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, renderContext.EBO[0]);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices1), indices1, GL_STATIC_DRAW);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);   // Vertex attributes stay the same
    glEnableVertexAttribArray(0);
    // glBindVertexArray(0); // no need to unbind at all as we directly bind a different VAO the next few lines
    // second triangle setup
    // ---------------------
    glBindVertexArray(renderContext.VAO[1]);    // note that we bind to a different VAO now
    glBindBuffer(GL_ARRAY_BUFFER, renderContext.VBO[1]);    // and a different VBO
    glBufferData(GL_ARRAY_BUFFER, sizeof(triangle2), triangle2, GL_STATIC_DRAW);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, renderContext.EBO[1]);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices2), indices2, GL_STATIC_DRAW);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)0); // because the vertex data is tightly packed we can also specify 0 as the vertex attribute's stride to let OpenGL figure it out
    glEnableVertexAttribArray(0);
    // glBindVertexArray(0); // not really necessary as well, but beware of calls that could affect VAOs while this one is bound (like binding element buffer objects, or enabling/disabling vertex attributes)


    // render loop
    // -----------
    while (!glfwWindowShouldClose(renderContext.window))
    {
        RenderAll();
    }

    // optional: de-allocate all resources once they've outlived their purpose:
    // ------------------------------------------------------------------------
    glDeleteVertexArrays(1, renderContext.VAO);
    glDeleteBuffers(1, renderContext.VBO);
    glDeleteProgram(renderContext.shaderProgram1);
    glDeleteProgram(renderContext.shaderProgram2);

    // glfw: terminate, clearing all previously allocated GLFW resources.
    // ------------------------------------------------------------------
    glfwTerminate();
    return 0;
}

// Render all
void RenderAll() {
    // input
    // -----
    processInput(renderContext.window);

    // render
    // ------
    glClearColor(0.2f, 0.08f, 0.08f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);

    // now when we draw the triangle we first use the vertex and orange fragment shader from the first program
    glUseProgram(renderContext.shaderProgram1);
    // draw the first triangle using the data from our first VAO
    glBindVertexArray(renderContext.VAO[0]);
    ///////////glDrawArrays(GL_TRIANGLES, 0, 3);    // this call should output an orange triangle
//** THROWS EXCEPTION ** Exception thrown at 0x00007FFC6A0F4A03 (nvoglv64.dll) in GLFW3TestProj.exe: 0xC0000005: Access violation reading location 0x0000000000000000.
    glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_INT, 0);
    // then we draw the second triangle using the data from the second VAO
    // when we draw the second triangle we want to use a different shader program so we switch to the shader program with our magenta fragment shader.
    glUseProgram(renderContext.shaderProgram2);
    glBindVertexArray(renderContext.VAO[1]);
    glDrawArrays(GL_TRIANGLES, 0, 3);   // this call should output a magenta triangle

    // glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.)
    // -------------------------------------------------------------------------------
    glfwSwapBuffers(renderContext.window);
    glfwPollEvents();
}

// process all input: query GLFW whether relevant keys are pressed/released this frame and react accordingly
// ---------------------------------------------------------------------------------------------------------
void processInput(GLFWwindow* window)
{
    if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
        glfwSetWindowShouldClose(window, true);
}

// glfw: whenever the window size changed (by OS or user resize) this callback function executes
// ---------------------------------------------------------------------------------------------
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
    // make sure the viewport matches the new window dimensions; note that width and 
    // height will be significantly larger than specified on retina displays.
    glViewport(0, 0, width, height);

    RenderAll();
}
4
  • What you mostly need is a debug buid of the opengl code + a pdb file. However the 0xc0000005 indicates an access violations, So what I would do first is to check your callstack and inspect all the pointers you pass in to the function call for validity. Commented Dec 14, 2024 at 6:00
  • Further your question should contain a lot less code, just enough to reproduce the bug. Side notes : your code is "C" not "C++". typedef struct S_Triplet is "C" syntax, just use struct T_Triplet{ ... }; in C++. And why are you not using std::string, std::string_view etc. but char*? Commented Dec 14, 2024 at 6:02
  • A quick visual inspection shows that you call glGenVertexArrays(2, renderContext.VAO) twice. I suspect that the second call is meant to be glGenVertexArrays(2, renderContext.VBO). Not the answer to your question I know. Commented Dec 14, 2024 at 6:10
  • 1
    You use EBO without creating them. Commented Dec 14, 2024 at 16:16

1 Answer 1

0

A few things:

  • An OpenGL debug context and debug message callback:

    glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GLFW_TRUE);
    
    ...
    
    if(GLFW_TRUE == glfwGetWindowAttrib(renderContext.window, GLFW_OPENGL_DEBUG_CONTEXT)) {
        glEnable( GL_DEBUG_OUTPUT_SYNCHRONOUS );
        glDebugMessageControl( GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, NULL, GL_TRUE );
        glDebugMessageCallback( []( GLenum, GLenum, GLuint,  GLenum, GLsizei, const GLchar* message, const void* )
        {
            std::cerr << "GL: " << message << std::endl;
        }, 0 );
    }
    

    ...shows four glBufferData() calls throwing GL_INVALID_OPERATIONs:

    GL: GL_INVALID_OPERATION in glBufferData(no buffer bound)
    GL: GL_INVALID_OPERATION in glBufferData(no buffer bound)
    GL: GL_INVALID_OPERATION in glBufferData(no buffer bound)
    GL: GL_INVALID_OPERATION in glBufferData(no buffer bound)
    Segmentation fault (core dumped)
    

    Adding a std::terminate() to the debug callback and running under a debugger shows the first offending call:

    $ DEBUGINFOD_URLS="https://debuginfod.debian.net" gdb ./main
    GNU gdb (Debian 13.1-3) 13.1
    Reading symbols from ./main...
    (gdb) r
    Starting program: /tmp/q/main 
    [Thread debugging using libthread_db enabled]                                                                                                                                                                                               
    Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
    [New Thread 0x7fffe9e006c0 (LWP 70907)]                                                                                                                                                                                                     
    [New Thread 0x7fffe94006c0 (LWP 70908)]
    [New Thread 0x7fffe8a006c0 (LWP 70909)]
    [New Thread 0x7fffe3e006c0 (LWP 70910)]
    [New Thread 0x7fffe32006c0 (LWP 70911)]
    [New Thread 0x7fffe26006c0 (LWP 70912)]
    GL: GL_INVALID_OPERATION in glBufferData(no buffer bound)
    terminate called without an active exception
    
    Thread 1 "main" received signal SIGABRT, Aborted.
    __pthread_kill_implementation (threadid=<optimized out>, signo=signo@entry=6, no_tid=no_tid@entry=0) at ./nptl/pthread_kill.c:44
    Download failed: Invalid argument.  Continuing without source file ./nptl/./nptl/pthread_kill.c.                                                                                                                                            
    44      ./nptl/pthread_kill.c: No such file or directory.
    (gdb) bt
    #0  __pthread_kill_implementation (threadid=<optimized out>, signo=signo@entry=6, no_tid=no_tid@entry=0) at ./nptl/pthread_kill.c:44
    #1  0x00007ffff7aa9f1f in __pthread_kill_internal (signo=6, threadid=<optimized out>) at ./nptl/pthread_kill.c:78
    #2  0x00007ffff7a5afb2 in __GI_raise (sig=sig@entry=6) at ../sysdeps/posix/raise.c:26
    #3  0x00007ffff7a45472 in __GI_abort () at ./stdlib/abort.c:79
    #4  0x00007ffff7c9d919 in __gnu_cxx::__verbose_terminate_handler () at ../../../../src/libstdc++-v3/libsupc++/vterminate.cc:95
    #5  0x00007ffff7ca8e1a in __cxxabiv1::__terminate (handler=<optimized out>) at ../../../../src/libstdc++-v3/libsupc++/eh_terminate.cc:48
    #6  0x00007ffff7ca8e85 in std::terminate () at ../../../../src/libstdc++-v3/libsupc++/eh_terminate.cc:58
    #7  0x0000555555555523 in operator() (__closure=0x0, message=0x7fffffffcaf0 "GL_INVALID_OPERATION in glBufferData(no buffer bound)") at main.cpp:110
    #8  0x0000555555555566 in _FUN () at main.cpp:111
    #9  0x00007ffff4a6f6fa in _mesa_error (ctx=0x5555557ea270, error=1282, fmtString=<optimized out>) at ../src/mesa/main/errors.c:366
    #10 0x0000555555555907 in main () at main.cpp:150
    (gdb) frame 10
    #10 0x0000555555555907 in main () at main.cpp:150
    150         glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices1), indices1, GL_STATIC_DRAW);
    (gdb) l
    145
    146         //glGenBuffers(2, renderContext.EBO);
    147
    148         unsigned int indices1[] = { 0, 1, 2 };
    149         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, renderContext.EBO[0]);
    150         glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices1), indices1, GL_STATIC_DRAW);
    151         unsigned int indices2[] = { 0, 1, 2 };
    152         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, renderContext.EBO[1]);
    153         glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices2), indices2, GL_STATIC_DRAW);
    154
    (gdb) 
    
  • As @rafix07 pointed out you're trying to bind renderContext.EBO without first having generated buffers for it.

    Use glGenBuffers() to populate renderContext.EBO:

    glGenBuffers(2, renderContext.EBO);
    
  • Raw string literals make for more readable inline GLSL, no newlines or excessive quotes needed:

    const char* const vert = R"GLSL(
    #version 330 core
    layout (location = 0) in vec3 aPos;
    void main()
    {
       gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);
    }
    )GLSL";
    

All together:

screenshot of orange and pink triangles

// g++ -g -o main main.cpp src/glad.c -Iinclude $(pkg-config --cflags --libs glfw3)
// DEBUGINFOD_URLS="https://debuginfod.debian.net" gdb ./main
#include <glad/glad.h>
#include <GLFW/glfw3.h>

#include <iostream>

void CheckStatus( GLuint obj, bool isShader )
{
    GLint status = GL_FALSE, log[ 1 << 11 ] = { 0 };
    ( isShader ? glGetShaderiv : glGetProgramiv )( obj, isShader ? GL_COMPILE_STATUS : GL_LINK_STATUS, &status );
    if( status == GL_TRUE ) return;
    ( isShader ? glGetShaderInfoLog : glGetProgramInfoLog )( obj, sizeof( log ), NULL, (GLchar*)log );
    std::cerr << (GLchar*)log << "\n";
    std::exit( EXIT_FAILURE );
}

void AttachShader( GLuint program, GLenum type, const char* src )
{
    GLuint shader = glCreateShader( type );
    glShaderSource( shader, 1, &src, NULL );
    glCompileShader( shader );
    CheckStatus( shader, true );
    glAttachShader( program, shader );
    glDeleteShader( shader );
}

const char* const vert = R"GLSL(
#version 330 core
layout (location = 0) in vec3 aPos;
void main()
{
   gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);
}
)GLSL";

const char* const frag1 = R"GLSL(
#version 330 core
out vec4 FragColor;
void main()
{
    FragColor = vec4(0.937f, 0.608f, 0.0f, 1.0f);
}
)GLSL";

const char* const frag2 = R"GLSL(
#version 330 core
out vec4 FragColor;
void main()
{
    FragColor = vec4(0.937f, 0.0f, 0.608f, 1.0f);
}
)GLSL";

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

typedef struct {
    GLFWwindow* window;
    unsigned int shaderProgram1;
    unsigned int shaderProgram2;
    unsigned int VBO[2];
    unsigned int VAO[2];
    unsigned int EBO[2];
} T_RenderContext;
void RenderAll();
T_RenderContext renderContext;

int main()
{
    glfwSetErrorCallback( []( int, const char* desc ) { std::cerr << desc << "\n"; std::exit( EXIT_FAILURE ); } );

    glfwInit();
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GLFW_TRUE);

#ifdef __APPLE__
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#endif

    // glfw window creation
    // --------------------
    renderContext.window = glfwCreateWindow(1250, 1000, "LearnOpenGL", NULL, NULL);
    if (renderContext.window == NULL)
    {
        std::cout << "Failed to create GLFW window" << std::endl;
        glfwTerminate();
        return -1;
    }
    glfwMakeContextCurrent(renderContext.window);
    glfwSetFramebufferSizeCallback(renderContext.window, framebuffer_size_callback);

    // glad: load all OpenGL function pointers
    // ---------------------------------------
    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
    {
        std::cout << "Failed to initialize GLAD" << std::endl;
        return -1;
    }

    if(GLFW_TRUE == glfwGetWindowAttrib(renderContext.window, GLFW_OPENGL_DEBUG_CONTEXT)) {
        glEnable( GL_DEBUG_OUTPUT_SYNCHRONOUS );
        glDebugMessageControl( GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, NULL, GL_TRUE );
        glDebugMessageCallback( []( GLenum, GLenum, GLuint,  GLenum, GLsizei, const GLchar* message, const void* )
        {
            std::cerr << "GL: " << message << std::endl;
        }, 0 );
    }

    GLuint prog;

    prog = glCreateProgram();
    AttachShader( prog, GL_VERTEX_SHADER, vert );
    AttachShader( prog, GL_FRAGMENT_SHADER, frag1 );
    glLinkProgram( prog );
    CheckStatus( prog, false );
    renderContext.shaderProgram1 = prog;

    prog = glCreateProgram();
    AttachShader( prog, GL_VERTEX_SHADER, vert );
    AttachShader( prog, GL_FRAGMENT_SHADER, frag2 );
    glLinkProgram( prog );
    CheckStatus( prog, false );
    renderContext.shaderProgram2 = prog;

    // set up vertex data (and buffer(s)) and configure vertex attributes
    // ------------------------------------------------------------------
    typedef struct S_Triplet {
        float x; float y; float z;
    } T_Triplet;

    T_Triplet triangle1[3];
    triangle1[0] = { -0.5F, 0.1F, 0.0F };
    triangle1[1] = { 0.5F, 0.2F, 0.0F };
    triangle1[2] = { 0.0F, 0.866F, 0.0F };

    T_Triplet triangle2[3];
    triangle2[0] = { -0.5F, -0.2F, 0.0F };
    triangle2[1] = { 0.5F, -0.1F, 0.0F };
    triangle2[2] = { 0.0F, -0.866F, 0.0F };

    glGenBuffers(2, renderContext.EBO);

    unsigned int indices1[] = { 0, 1, 2 };
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, renderContext.EBO[0]);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices1), indices1, GL_STATIC_DRAW);
    unsigned int indices2[] = { 0, 1, 2 };
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, renderContext.EBO[1]);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices2), indices2, GL_STATIC_DRAW);

    glGenVertexArrays(2, renderContext.VAO);
    glGenBuffers(2, renderContext.VBO);

    // first triangle setup
    // --------------------
    glBindVertexArray(renderContext.VAO[0]);
    glBindBuffer(GL_ARRAY_BUFFER, renderContext.VBO[0]);
    glBufferData(GL_ARRAY_BUFFER, sizeof(triangle1), triangle1, GL_STATIC_DRAW);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, renderContext.EBO[0]);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices1), indices1, GL_STATIC_DRAW);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
    glEnableVertexAttribArray(0);

    // second triangle setup
    // ---------------------
    glBindVertexArray(renderContext.VAO[1]);
    glBindBuffer(GL_ARRAY_BUFFER, renderContext.VBO[1]);
    glBufferData(GL_ARRAY_BUFFER, sizeof(triangle2), triangle2, GL_STATIC_DRAW);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, renderContext.EBO[1]);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices2), indices2, GL_STATIC_DRAW);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);
    glEnableVertexAttribArray(0);


    // render loop
    // -----------
    while (!glfwWindowShouldClose(renderContext.window))
    {
        RenderAll();
    }

    // optional: de-allocate all resources once they've outlived their purpose:
    // ------------------------------------------------------------------------
    glDeleteVertexArrays(1, renderContext.VAO);
    glDeleteBuffers(1, renderContext.VBO);
    glDeleteProgram(renderContext.shaderProgram1);
    glDeleteProgram(renderContext.shaderProgram2);

    // glfw: terminate, clearing all previously allocated GLFW resources.
    // ------------------------------------------------------------------
    glfwTerminate();
    return 0;
}

// Render all
void RenderAll() {
    // input
    // -----
    processInput(renderContext.window);

    // render
    // ------
    glClearColor(0.2f, 0.08f, 0.08f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);

    // now when we draw the triangle we first use the vertex and orange fragment shader from the first program
    glUseProgram(renderContext.shaderProgram1);
    // draw the first triangle using the data from our first VAO
    glBindVertexArray(renderContext.VAO[0]);
    glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_INT, 0);

    // then we draw the second triangle using the data from the second VAO
    // when we draw the second triangle we want to use a different shader program so we switch to the shader program with our magenta fragment shader.
    glUseProgram(renderContext.shaderProgram2);
    glBindVertexArray(renderContext.VAO[1]);
    glDrawArrays(GL_TRIANGLES, 0, 3);   // this call should output a magenta triangle

    // glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.)
    // -------------------------------------------------------------------------------
    glfwSwapBuffers(renderContext.window);
    glfwPollEvents();
}

// process all input: query GLFW whether relevant keys are pressed/released this frame and react accordingly
// ---------------------------------------------------------------------------------------------------------
void processInput(GLFWwindow* window)
{
    if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
        glfwSetWindowShouldClose(window, true);
}

// glfw: whenever the window size changed (by OS or user resize) this callback function executes
// ---------------------------------------------------------------------------------------------
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
    // make sure the viewport matches the new window dimensions; note that width and
    // height will be significantly larger than specified on retina displays.
    glViewport(0, 0, width, height);

    RenderAll();
}
Sign up to request clarification or add additional context in comments.

1 Comment

A valuable answer! Thank you for taking the time to investigate and reply.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.