是否可以使用 Visual Studio 调试器来调试我的 GLFW/OpenGL 代码?我在 glDrawElements() 调用上遇到异常,它提供了无用的信息。显然我没有正确设置,但是在库调用中发生错误,我如何找出我做错了什么?有没有办法利用 GLFW 源的可用性?我只是按照 GLFW 指示的方式设置了 VS 项目。
该代码是 learnopengl.com 第一个示例的修改版本,因此非常简单。我正在尝试将第一个三角形的绘制从 VBO/VAO 缓冲区转换为使用 EBO 缓冲区。
我评论了在底部附近的 RenderAll() 中引发此问题的行。
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();
}
一些事情:
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 );
}
...显示四个
glBufferData()
调用抛出 GL_INVALID_OPERATION
:
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)
将
std::terminate()
添加到调试回调并在 GDB 下运行会显示第一个有问题的调用:
$ 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)
正如@rafix07指出的,您正在尝试绑定
renderContext.EBO
,而无需先为其生成缓冲区。
使用
glGenBuffers()
填充 renderContext.EBO
:
glGenBuffers(2, renderContext.EBO);
原始字符串文字使内联GLSL更具可读性,不需要换行符或过多的引号:
const char* const vert = R"GLSL(
#version 330 core
layout( location = 0 ) in vec4 position;
void main()
{
gl_Position = position;
};
)GLSL";
大家一起:
// 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();
}