OpenGL系列 第一个程序
一、OpenGL相关库
本次推荐使用Opengl的相关库glfw和glad。
在参考资料中可以了解到OpenGL库的相关介绍。
glfw
glfw 可以从官方网站的下载页上获取。
glfw 有预编译的二进制版本和相应的头文件,但是为了完整性最好从编译源代码开始。下载源代码包之后,将其解压并打开,最后只需要 编译生成的库 和 include 文件夹。
glad
打开 glad 的在线服务,将语言(Language)设置为 C/C++,在API选项中,选择 3.3 以上的 OpenGL(gl) 版本(这里使用3.3版本,但更新的版本也能正常工作)。之后将模式(Profile)设置为 Core,并且保证生成加载器(Generate a loader)的选项是选中的。现在可以先(暂时)忽略拓展(Extensions)中的内容。都选择完之后,点击生成(Generate)按钮来生成库文件。
参考资料
二、第一个OpenGL程序
实例代码
main.cpp代码如下:
#include <iostream>
#include <glad\glad.h> //这个必须放在GLFW之前
#include <GLFW\glfw3.h>
// settings
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;
void framebuffer_size_callback(GLFWwindow* window, int width, int height);
int main()
{
// glfw: initialize and configure
// ------------------------------
glfwInit(); //glfw 初始化
//设置OpenGL版本等信息
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); // uncomment this statement to fix compilation on OS X
#endif
// glfw window creation
// --------------------
//创建GLFW窗口
GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);
if (window == NULL) {
std::cout << "Failed to create GLFW window" << std::endl;
glfwTerminate();
return -1;
}
//设置当前上下文
glfwMakeContextCurrent(window);
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
// glad: load all OpenGL function pointers
// ---------------------------------------
//把OpenGL的函数指针导入给GLAD
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {
std::cout << "Failed to initialize GLAD" << std::endl;
return -1;
}
// 以下正式进入 gl代码
// --------------------
//顶点着色器
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";
// 创建一个 顶点着色器(GL_VERTEX_SHADER) 对象
int vertexShader = glCreateShader(GL_VERTEX_SHADER);
// (替换着色器对象中的源代码) 可以理解为 覆盖 着色器对象 源码内容
glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
// 由于替换的源码 需要 编译一下着色器
glCompileShader(vertexShader);
int success;
char infoLog[512];
// glGetShaderiv原型: 获取 对象(param1) 的 状态(param2),将结果放入param3 中。
// 获取 着色器对象(vertexShader) 的 编译(GL_COMPILE_STATUS) 状态,将结果放入success 中。
glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
if (!success) {
// 如果 编译发生错误
// 获取 着色器对象(vertexShader) 的 缓存的Log内容, 将结果放入infoLog 中。
glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);
std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl;
}
//片段着色器
const char* fragmentShaderSource =
"#version 330 core\n"
"out vec4 FragColor;\n"
"void main()\n"
"{\n"
" FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);\n"
"}\n\0";
int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
glCompileShader(fragmentShader);
// check for shader compile errors
glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
if (!success) {
glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);
std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << std::endl;
}
// 创建一个 opengl程序对象
int shaderProgram = glCreateProgram();
// 将 着色器对象 附加到 程序对象上
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
// 链接程序对象
glLinkProgram(shaderProgram);
// check for linking errors
glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
if (!success) {
glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog);
std::cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog << std::endl;
}
// 由于 着色器对象 已经附加并链接到 程序对象 上,就需要释放掉 着色器对象。
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
// ------------------------------------------------------------------
float vertices[] = {
// ---- 位置 ----
0.5f, 0.5f, 0.0f, // 右上
0.5f, -0.5f, 0.0f, // 右下
-0.5f, -0.5f, 0.0f, // 左下
-0.5f, 0.5f, 0.0f, // 左上
};
unsigned int indices[] = { // note that we start from 0!
0, 1, 3, // first Triangle
1, 2, 3 // second Triangle
};
unsigned int VBO, VAO, EBO;
// 创建顶点
glGenVertexArrays(1, &VAO);
// 绑定顶点
glBindVertexArray(VAO);
// 创建 数据
glGenBuffers(1, &VBO);
// 绑定 数据
glBindBuffer(GL_ARRAY_BUFFER, VBO);
// 给数据赋值
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
// 定义常规顶点属性
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
// 启用顶点属性
glEnableVertexAttribArray(0);
// 创建 索引
glGenBuffers(1, &EBO);
// 绑定索引
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
// 给索引数据赋值
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
//循环渲染
while (!glfwWindowShouldClose(window)) {
// render
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
// draw our first triangle
// 使用程序
glUseProgram(shaderProgram);
// 绑定(如果已经绑定过顶点,则代表当下要激活)顶点数据
glBindVertexArray(VAO);
// 使用索引绘制方式,按 GL_TRIANGLES方式 设置索引个数和类型 绘制
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
// glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.)
// -------------------------------------------------------------------------------
//交换缓存
glfwSwapBuffers(window);
// //事件处理
glfwPollEvents();
}
// optional: de-allocate all resources once they've outlived their purpose:
// ------------------------------------------------------------------------
glDeleteVertexArrays(1, &VAO);
glDeleteBuffers(1, &VBO);
glDeleteBuffers(1, &EBO);
// glfw: terminate, clearing all previously allocated GLFW resources.
// ------------------------------------------------------------------
//停止
glfwTerminate();
return 0;
}
// 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);
}