OpenGL程序调试输出
前言:调试输出是OpenGL4.3版本中增加的一种用于调试的扩展工具。它不仅比glGetError接口提供更多的错误信息,而且还允许通过debugger去定位错误源。
创建调试环境:环境的创建是与平台相关的任务,一般由各种封装库提供接口进行创建。常见的创建方式如下:
1.GLFW创建方式:需要在创建OpenGL环境之前进行调试环境的创建。创建代码如下所示:
glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GL_TRUE);
1
2.WGL创建方式:需要在创建OpenGL环境之后进行调试环境的创建。创建代码如下:
HGLRC CreateDebugContext(HDC hDC, HGLRC hShareContext, int major, int minor)
{
const int attriblist[] =
{
// 环境的主版本
WGL_CONTEXT_MAJOR_VERSION_ARB, major,
// 环境的次版本
WGL_CONTEXT_MINOR_VERSION_ARB, mior,
// 总是选择核心优化
WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
// 打开调试环境
WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_DEBUG_BIT_ARB,
0
};
return wglCreateContextAttribsARB(hDC, hShareContext, attriblist);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
3.GLX创建方式:无需先创建OpenGL环境就可以进行调试环境创建。创建代码如下:
GLXContext CreateDebugContext(Display* dpy, GLXFBConfig* config, GLXContext shareContext, int major, int minor)
{
const int attriblist[] =
{
// 环境的主版本
GLX_CONTEXT_MAJOR_VERSION_ARB, major,
// 环境的次版本
GLX_CONTEXT_MINOR_VERSION_ARB, mior,
// 总是选择核心优化
GLX_CONTEXT_PROFILE_MASK_ARB, GLX_CONTEXT_CORE_PROFILE_BIT_ARB,
// 打开调试环境
GLX_CONTEXT_FLAGS_ARB, GLX_CONTEXT_DEBUG_BIT_ARB,
0
};
return glXCreateContextAttribsARB(dpy, config, shareContext, attriblist);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
检查调试环境:要检查是否成功地初始化了调试环境,我们可以对OpenGL进行查询。检查代码如下所示:
GLint flags;
glGetIntegerv(GL_CONTEXT_FLAGS, &flags);
if (flags & GL_CONTEXT_FLAG_DEBUG_BIT)
{
// TODO 初始化调试输出成功
}
1
2
3
4
5
6
开关调试消息:传递调试消息默认是启动的,当然我们也可以禁止。通常使用GL_DEBUG_OUTPUT或者GL_DEBUG_OUTPUT_SYNCHRONOUS来调用glEnable接口进行启用,或者调用glDisable接口来进行禁用。
传递调试消息:调试环境存在两种方式将获取的调试消息传递给开发者。第一种方式就是设置回调函数。第二种方式就是通过远程渲染方式利用调试环境获取调试消息日志。
常用接口如下:
1.void glDebugMessageCallback(DEBUGPROC callback, void* userParam):设置一个新的调试消息回调函数callback给调试环境。当调试消息生成的时候就会触发这个回调函数,并将调试消息以及用户自定义参数userParam传递给回调函数callback。
其中DEBUGPROC声明如下所示:
void DEBUGPROC(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message, void *userParam):
.source表示消息来源。通常是GL_DEBUG_SOURCE_API(OpenGL API),GL_DEBUG_SOURCE_WINDOW_SYSTEM(窗口系统),GL_DEBUG_SOURCE_SHADER_COMPILER(着色器编译器),GL_DEBUG_SOURCE_THIRD_PARTY(第三方库),GL_DEBUG_SOURCE_APPLICATION(应用程序),GL_DEBUG_SOURCE_OTHER(其他)当中的一种。
.type表示消息类型。通常是GL_DEBUG_TYPE_ERROR(错误生成),GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR(过时功能),GL_DEBUG_TYPE_UNSINGNED_BEHAVIOR(未定义功能),GL_DEBUG_TYPE_PERFORMANCE(性能不优),GL_DEBUG_TYPE_PORTABILITY(不能移植),GL_DEBUG_TYPE_MARKER(注解),GL_DEBUG_TYPE_PUSH_GROUP(调用glPushDebugGroup),GL_DEBUG_TYPE_POP_GROUP(调用glPopDebugGroup),GL_DEBUG_TYPE_OTHER(其他)当中的一种。
.id表示消息标志。
.severity表示消息等级。通常是GL_DEBUG_SEVERITY_HIGH(致命消息。如:OpneGL错误,着色器编译失败等),GL_DEBUG_SEVERITY_MEDIUM(不致命消息。如:移植性能警告),GL_DEBUG_SEVERITY_LOW(没危害消息。如:冗余的状态切换),GL_DEBUG_SEVERITY_NOTIFICATION(普通通知消息)当中的一种。
.message表示调试消息。
.userParam表示用户自定义参数。与设置回调时传入的自定义参数相关联。
有了这一大堆的数据,我们可以创建一个非常有用的错误打印工具。代码如下所示:
void glDebugOutput(GLenum source,
GLenum type,
GLuint id,
GLenum severity,
GLsizei length,
const GLchar *message,
void *userParam)
{
// 忽略一些不重要的错误/警告代码
if(id == 131169 || id == 131185 || id == 131218 || id == 131204) return;
std::cout << "---------------" << std::endl;
std::cout << "Debug message (" << id << "): " << message << std::endl;
switch (source)
{
case GL_DEBUG_SOURCE_API: std::cout << "Source: API"; break;
case GL_DEBUG_SOURCE_WINDOW_SYSTEM: std::cout << "Source: Window System"; break;
case GL_DEBUG_SOURCE_SHADER_COMPILER: std::cout << "Source: Shader Compiler"; break;
case GL_DEBUG_SOURCE_THIRD_PARTY: std::cout << "Source: Third Party"; break;
case GL_DEBUG_SOURCE_APPLICATION: std::cout << "Source: Application"; break;
case GL_DEBUG_SOURCE_OTHER: std::cout << "Source: Other"; break;
} std::cout << std::endl;
switch (type)
{
case GL_DEBUG_TYPE_ERROR: std::cout << "Type: Error"; break;
case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR: std::cout << "Type: Deprecated Behaviour"; break;
case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR: std::cout << "Type: Undefined Behaviour"; break;
case GL_DEBUG_TYPE_PORTABILITY: std::cout << "Type: Portability"; break;
case GL_DEBUG_TYPE_PERFORMANCE: std::cout << "Type: Performance"; break;
case GL_DEBUG_TYPE_MARKER: std::cout << "Type: Marker"; break;
case GL_DEBUG_TYPE_PUSH_GROUP: std::cout << "Type: Push Group"; break;
case GL_DEBUG_TYPE_POP_GROUP: std::cout << "Type: Pop Group"; break;
case GL_DEBUG_TYPE_OTHER: std::cout << "Type: Other"; break;
} std::cout << std::endl;
switch (severity)
{
case GL_DEBUG_SEVERITY_HIGH: std::cout << "Severity: high"; break;
case GL_DEBUG_SEVERITY_MEDIUM: std::cout << "Severity: medium"; break;
case GL_DEBUG_SEVERITY_LOW: std::cout << "Severity: low"; break;
case GL_DEBUG_SEVERITY_NOTIFICATION: std::cout << "Severity: notification"; break;
} std::cout << std::endl;
std::cout << std::endl;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
过滤调试消息:可以过滤出需要的调试消息。使用接口如下所示:
void glDebugMessageControl(GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint * ids, GLboolean enabled):
.source表示消息来源。
.type表示消息类型。
.severity表示消息等级。
.count表示消息标志符的数目。
.ids表示消息标志符的数组。
.enabled表示是否启用,为GL_FALSE时表示丢弃消息。
自定义调试消息:可以自定义调试消息传送给调试环境。使用接口如下:
void glDebugMessageInsert(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* buf):
.source表示消息来源。
.type表示消息类型。
.id表示消息标志。
.severity表示消息等级。
.length表示自定义消息的长度。
.buf表示自定义的消息内容。
调试组:就是用来存储调试状态的堆栈,且堆栈大小通常是64组,可以通过GL_MAX_DEBUG_GROUP_STACK_DEPTH参数来调用glGetIntegerv接口获取。常用接口如下:
1.void glPushDebugGroup(GLenum source, GLuint id, Glint length, const GLchar* message):将当前的调试状态压入OpenGL管理的堆栈。
.source限制为GL_DEBUG_SOURCE_APPLICATION或者GL_DEBUG_SOURCE_THIRD_PARTY。
.type限制为GL_DEBUG_TYPE_PUSH_GROUP。
.severity限制为GL_DEBUG_SEVERITY_NOTIFICATION。
.id表示消息标志。
.length表示消息长度。
.message表示消息内容。
2.void glPopDebugGroup(void):将当前堆栈上的调试状态弹出。
命名对象:就是对OpenGL管理的对象标志关联一个名称,从而可以使用名称来访问该对象。常用接口如下:
1.void glObjectLabel(GLenum identifier, GLuint name, Glsizei length, const GLchar* label):为OpenGL拥有的对象生成标签。
.identifier表示分配对象的名称空间。通常是GL_BUFFER,GL_SHADER,GL_PROGRAM等来源中的一种。
.name表示生成对象的唯一标识。
.length表示标签长度。
.label表示标签内容。
2.void glGetObjectLabel(GLenum identifier, GLuint name, Glsizei bufsize, Glsizei * length, GLchar* label):获取glObjectLabel接口分配的对象标签。
.identifier表示分配对象的名称空间。
.name表示生成对象的唯一标识。
.length表示标签长度。
.label表示标签内容。
3.void glObjectPtrLabel(void* ptr, Glsizei length, const GLchar* label):为OpenGL拥有的指针对象生成标签。
.ptr表示指针对象。
.length表示标签长度。
.label表示标签内容。
4.void glGetObjectPtrLabel(void* ptr, Glsizei bufsize, Glsizei* length, const GLchar* label):获取glObjectPtrLabel接口分配的对象标签。
.ptr表示指针对象。
.length表示标签长度。
.label表示标签内容。
回溯调试错误源:可以在调试消息回调函数中打断点,当调试消息生成并调用该回调时就会触发对应断点。可以从断点堆栈中找到OpenGL程序的执行过程,从而找出错误源。如图所示:
在线例子:以下提供一个包含调试输出的大部分接口的在线例子。
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <stb_image.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <learnopengl/filesystem.h>
#include <learnopengl/shader.h>
#include <iostream>
void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void processInput(GLFWwindow *window);
// settings
const unsigned int SCR_WIDTH = 1280;
const unsigned int SCR_HEIGHT = 720;
GLenum glCheckError_(const char *file, int line)
{
GLenum errorCode;
while ((errorCode = glGetError()) != GL_NO_ERROR)
{
std::string error;
switch (errorCode)
{
case GL_INVALID_ENUM: error = "INVALID_ENUM"; break;
case GL_INVALID_VALUE: error = "INVALID_VALUE"; break;
case GL_INVALID_OPERATION: error = "INVALID_OPERATION"; break;
case GL_STACK_OVERFLOW: error = "STACK_OVERFLOW"; break;
case GL_STACK_UNDERFLOW: error = "STACK_UNDERFLOW"; break;
case GL_OUT_OF_MEMORY: error = "OUT_OF_MEMORY"; break;
case GL_INVALID_FRAMEBUFFER_OPERATION: error = "INVALID_FRAMEBUFFER_OPERATION"; break;
}
std::cout << error << " | " << file << " (" << line << ")" << std::endl;
}
return errorCode;
}
#define glCheckError() glCheckError_(__FILE__, __LINE__)
void APIENTRY glDebugOutput(GLenum source,
GLenum type,
GLuint id,
GLenum severity,
GLsizei length,
const GLchar *message,
const void *userParam)
{
if(id == 131169 || id == 131185 || id == 131218 || id == 131204) return; // ignore these non-significant error codes
std::cout << "---------------" << std::endl;
std::cout << "Debug message (" << id << "): " << message << std::endl;
switch (source)
{
case GL_DEBUG_SOURCE_API: std::cout << "Source: API"; break;
case GL_DEBUG_SOURCE_WINDOW_SYSTEM: std::cout << "Source: Window System"; break;
case GL_DEBUG_SOURCE_SHADER_COMPILER: std::cout << "Source: Shader Compiler"; break;
case GL_DEBUG_SOURCE_THIRD_PARTY: std::cout << "Source: Third Party"; break;
case GL_DEBUG_SOURCE_APPLICATION: std::cout << "Source: Application"; break;
case GL_DEBUG_SOURCE_OTHER: std::cout << "Source: Other"; break;
} std::cout << std::endl;
switch (type)
{
case GL_DEBUG_TYPE_ERROR: std::cout << "Type: Error"; break;
case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR: std::cout << "Type: Deprecated Behaviour"; break;
case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR: std::cout << "Type: Undefined Behaviour"; break;
case GL_DEBUG_TYPE_PORTABILITY: std::cout << "Type: Portability"; break;
case GL_DEBUG_TYPE_PERFORMANCE: std::cout << "Type: Performance"; break;
case GL_DEBUG_TYPE_MARKER: std::cout << "Type: Marker"; break;
case GL_DEBUG_TYPE_PUSH_GROUP: std::cout << "Type: Push Group"; break;
case GL_DEBUG_TYPE_POP_GROUP: std::cout << "Type: Pop Group"; break;
case GL_DEBUG_TYPE_OTHER: std::cout << "Type: Other"; break;
} std::cout << std::endl;
switch (severity)
{
case GL_DEBUG_SEVERITY_HIGH: std::cout << "Severity: high"; break;
case GL_DEBUG_SEVERITY_MEDIUM: std::cout << "Severity: medium"; break;
case GL_DEBUG_SEVERITY_LOW: std::cout << "Severity: low"; break;
case GL_DEBUG_SEVERITY_NOTIFICATION: std::cout << "Severity: notification"; break;
} std::cout << std::endl;
std::cout << std::endl;
}
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);
glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GL_TRUE); // comment this line in a release build!
#ifdef __APPLE__
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // uncomment this statement to fix compilation on OS X
#endif
// glfw window creation
// --------------------
GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);
glfwMakeContextCurrent(window);
if (window == NULL)
{
std::cout << "Failed to create GLFW window" << std::endl;
glfwTerminate();
return -1;
}
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
// tell GLFW to capture our mouse
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
// glad: load all OpenGL function pointers
// ---------------------------------------
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
std::cout << "Failed to initialize GLAD" << std::endl;
return -1;
}
// enable OpenGL debug context if context allows for debug context
GLint flags; glGetIntegerv(GL_CONTEXT_FLAGS, &flags);
if (flags & GL_CONTEXT_FLAG_DEBUG_BIT)
{
glEnable(GL_DEBUG_OUTPUT);
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); // makes sure errors are displayed synchronously
glDebugMessageCallback(glDebugOutput, nullptr);
glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, nullptr, GL_TRUE);
}
// configure global opengl state
// -----------------------------
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
// OpenGL initial state
Shader shader("debugging.vs", "debugging.fs");
// configure 3D cube
GLuint cubeVAO, cubeVBO;
GLfloat vertices[] = {
// back face
-0.5f, -0.5f, -0.5f, 0.0f, 0.0f, // Bottom-left
0.5f, 0.5f, -0.5f, 1.0f, 1.0f, // top-right
0.5f, -0.5f, -0.5f, 1.0f, 0.0f, // bottom-right
0.5f, 0.5f, -0.5f, 1.0f, 1.0f, // top-right
-0.5f, -0.5f, -0.5f, 0.0f, 0.0f, // bottom-left
-0.5f, 0.5f, -0.5f, 0.0f, 1.0f, // top-left
// front face
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f, // bottom-left
0.5f, -0.5f, 0.5f, 1.0f, 0.0f, // bottom-right
0.5f, 0.5f, 0.5f, 1.0f, 1.0f, // top-right
0.5f, 0.5f, 0.5f, 1.0f, 1.0f, // top-right
-0.5f, 0.5f, 0.5f, 0.0f, 1.0f, // top-left
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f, // bottom-left
// left face
-0.5f, 0.5f, 0.5f, -1.0f, 0.0f, // top-right
-0.5f, 0.5f, -0.5f, -1.0f, 1.0f, // top-left
-0.5f, -0.5f, -0.5f, -0.0f, 1.0f, // bottom-left
-0.5f, -0.5f, -0.5f, -0.0f, 1.0f, // bottom-left
-0.5f, -0.5f, 0.5f, -0.0f, 0.0f, // bottom-right
-0.5f, 0.5f, 0.5f, -1.0f, 0.0f, // top-right
// right face
0.5f, 0.5f, 0.5f, 1.0f, 0.0f, // top-left
0.5f, -0.5f, -0.5f, 0.0f, 1.0f, // bottom-right
0.5f, 0.5f, -0.5f, 1.0f, 1.0f, // top-right
0.5f, -0.5f, -0.5f, 0.0f, 1.0f, // bottom-right
0.5f, 0.5f, 0.5f, 1.0f, 0.0f, // top-left
0.5f, -0.5f, 0.5f, 0.0f, 0.0f, // bottom-left
// bottom face
-0.5f, -0.5f, -0.5f, 0.0f, 1.0f, // top-right
0.5f, -0.5f, -0.5f, 1.0f, 1.0f, // top-left
0.5f, -0.5f, 0.5f, 1.0f, 0.0f, // bottom-left
0.5f, -0.5f, 0.5f, 1.0f, 0.0f, // bottom-left
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f, // bottom-right
-0.5f, -0.5f, -0.5f, 0.0f, 1.0f, // top-right
// top face
-0.5f, 0.5f, -0.5f, 0.0f, 1.0f, // top-left
0.5f, 0.5f, 0.5f, 1.0f, 0.0f, // bottom-right
0.5f, 0.5f, -0.5f, 1.0f, 1.0f, // top-right
0.5f, 0.5f, 0.5f, 1.0f, 0.0f, // bottom-right
-0.5f, 0.5f, -0.5f, 0.0f, 1.0f, // top-left
-0.5f, 0.5f, 0.5f, 0.0f, 0.0f // bottom-left
};
glGenVertexArrays(1, &cubeVAO);
glGenBuffers(1, &cubeVBO);
// fill buffer
glBindBuffer(GL_ARRAY_BUFFER, cubeVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
// link vertex attributes
glBindVertexArray(cubeVAO);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)0);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat)));
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
// load cube texture
unsigned int texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
int width, height, nrComponents;
unsigned char *data = stbi_load(FileSystem::getPath("resources/textures/wood.png").c_str(), &width, &height, &nrComponents, 0);
if (data)
{
glTexImage2D(GL_FRAMEBUFFER, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
}
else
{
std::cout << "Failed to load texture" << std::endl;
}
stbi_image_free(data);
// set up projection matrix
glm::mat4 projection = glm::perspective(glm::radians(45.0f), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 10.0f);
glUniformMatrix4fv(glGetUniformLocation(shader.ID, "projection"), 1, GL_FALSE, glm::value_ptr(projection));
glUniform1i(glGetUniformLocation(shader.ID, "tex"), 0);
// render loop
// -----------
while (!glfwWindowShouldClose(window))
{
// input
// -----
processInput(window);
// render
// ------
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
shader.use();
GLfloat rotationSpeed = 10.0f;
GLfloat angle = (float)glfwGetTime() * rotationSpeed;
glm::mat4 model;
model = glm::translate(model, glm::vec3(0.0, 0.0f, -2.5));
model = glm::rotate(model, glm::radians(angle), glm::vec3(1.0f, 1.0f, 1.0f));
glUniformMatrix4fv(glGetUniformLocation(shader.ID, "model"), 1, GL_FALSE, glm::value_ptr(model));
glBindTexture(GL_TEXTURE_2D, texture);
glBindVertexArray(cubeVAO);
glDrawArrays(GL_TRIANGLES, 0, 36);
glBindVertexArray(0);
// glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.)
// -------------------------------------------------------------------------------
glfwSwapBuffers(window);
glfwPollEvents();
}
glfwTerminate();
return 0;
}
// renderQuad() renders a 1x1 XY quad in NDC
// -----------------------------------------
unsigned int quadVAO = 0;
unsigned int quadVBO;
void renderQuad()
{
if (quadVAO == 0)
{
float quadVertices[] = {
// positions // texture Coords
-1.0f, 1.0f, 0.0f, 0.0f, 1.0f,
-1.0f, -1.0f, 0.0f, 0.0f, 0.0f,
1.0f, 1.0f, 0.0f, 1.0f, 1.0f,
1.0f, -1.0f, 0.0f, 1.0f, 0.0f,
};
// setup plane VAO
glGenVertexArrays(1, &quadVAO);
glGenBuffers(1, &quadVBO);
glBindVertexArray(quadVAO);
glBindBuffer(GL_ARRAY_BUFFER, quadVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(quadVertices), &quadVertices, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3 * sizeof(float)));
}
glBindVertexArray(quadVAO);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glBindVertexArray(0);
}
// 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);
}
---------------------
作者:金朝
来源:CSDN
版权声明:本文为博主原创文章,转载请附上博文链接!