#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <iostream>
GLFWwindow* window;
unsigned int shaderProgram;
// 顶点着色器源码存储在C字符串中
const char* vertexShaderSource = "#version 330 core\n"
"layout (location = 0) in vec3 aPos;\n"
"layout (location = 1) in vec3 aColor;\n"
"uniform float mv;\n" // uniform 是cpu -> gpu的数据传输
"out vec3 ourColor;\n" // in out 是 gpu内部shader之间的数据传输
"out vec4 vertexPosition;\n"
"void main()\n"
"{\n"
" gl_Position = vec4(aPos, 1.0);\n"
" gl_Position.x = gl_Position.x + mv;\n"
" ourColor = aColor;\n"
" vertexPosition = gl_Position;\n"
"}\n";
// 片段着色器:计算像素最后的颜色输出(RGBA)
const char* fragmentShaderSource = "#version 330 core\n"
"out vec4 FragColor;\n"
"in vec3 ourColor;\n"
"in vec4 vertexPosition;\n"
"void main()\n"
"{\n"
""//FragColor = vec4(ourColor, 1.0);\n
" FragColor = vertexPosition;\n"
"}\n";
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
// 回调函数,在每次窗口大小被调整时调用(窗口第一次显示时也会被调用)
glViewport(0, 0, width, height);
}
void processInput(GLFWwindow* window)
{
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) // 检查是否按下esc键,否则返回GLFW_RELEASE
glfwSetWindowShouldClose(window, true); // 将WindowShouldClose属性设置为true
}
void shaderSet()
{
// 创建顶点着色器
// 首先创建一个着色器对象,通过ID引用
unsigned int vertexShader;
vertexShader = glCreateShader(GL_VERTEX_SHADER); // 指明创建的是顶点着色器
glShaderSource(vertexShader, 1, &vertexShaderSource, NULL); // 将着色器源码附加到着色器对象上
glCompileShader(vertexShader); // 编译着色器
// 检测着色器是否编译成功
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;
}
// 创建片段着色器
unsigned int fragmentShader;
fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL); // 将着色器源码赋给着色器对象
glCompileShader(fragmentShader);
// 检测着色器是否编译成功
glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);
std::cout << "ERROR:SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << std::endl;
}
// 链接:创建着色器程序
shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram); // 链接
// 判断链接是否成功
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);
}
void VAOSetRectangle()
{
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[] = { // 索引
0, 1, 3,
1, 2, 3
};
unsigned int VBO, VAO, EBO;
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
glGenBuffers(1, &EBO);
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
}
void VAOSetTriangle()
{
// 设置顶点数据和缓冲数据,配置顶点属性
// -------------------------------------------------------------
// 顶点输入: 由于opengl是在3D空间中工作,为了渲染2D三角形,将z的坐标设置为0.0
// 顶点数据中可同时包含位置和颜色数据
float vertices[] = {
// 三角形
// 位置 // 颜色
0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, // 右下
-0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, // 左下
0.0f, -0.5f, 0.0f, 0.0f, 0.0f, 1.0f // 顶部
};
// 生成VBO管理GPU存储顶点的内存。使用VBO可以一次性发送一大批数据到显卡上
// VAO顶点数组对象用于保存顶点缓冲对象VBO和索引缓冲对象EBO的绑定状态,这样每次用VBO或EBO渲染物体时只需要绑定VAO
unsigned int VBO, EBO, VAO;
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO); // 这个缓冲对象具有独一无二的ID
// 先绑定顶点数组对象VAO,再绑定和设置VBO,最后配置顶点属性
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO); // 绑定缓冲
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); // 分配显存空间,并把定义的顶点数据复制到缓冲的内存中, GL_STATIC_DRAW 表示数据不会或几乎不会改变
// 设置如何解析顶点数据,即设置顶点属性
// 位置属性,参数为 属性位置,元素个数,元素类型,是否需要标准化,步长,偏移
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0); // 每个顶点包含 3个位置值,3个颜色值. GL_FALSE表示是否需要标准化,由于给的顶点数据已经是标准化数据,不需要再进行标准化
glEnableVertexAttribArray(0); // 启用该属性
// 颜色属性
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3 * sizeof(float)));
glEnableVertexAttribArray(1);
// 解绑
//glBindBuffer(GL_ARRAY_BUFFER, 0);
//glBindVertexArray(0);
}
bool init()
{
// 初始化GLFW, 并创建GLFW窗口
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); // 设置opengl主版本
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); // 设置opengl次版本
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
// 创建窗口对象
window = glfwCreateWindow(800, 600, "LearnOpenGL", NULL, NULL);
if (window == NULL)
{
std::cout << "Failed to create GLFW window" << std::endl;
glfwTerminate();
return false;
}
glfwMakeContextCurrent(window); // 将opengl上下文和创建的窗口绑定起来
// 调用OpenGL函数前初始化GLAD,方便GLAD后续管理OpenGL函数指针。
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
std::cout << "Failed to initialize GLAD" << std::endl;
return false;
}
// 设置视口(渲染窗口)大小: 参数为左下角位置,宽和高
glViewport(0, 0, 800, 600);
// 注册窗口大小调整的回调函数
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
return true;
}
int main()
{
// 1. 初始化
// ---------
if (!init())
{
return -1;
}
// 2. 设置顶点数据和VAO,VBO
// -------------------------
// 最大可支持的顶点属性数
int nrAttributes;
glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &nrAttributes);
std::cout << "Maximum nr of vertex attributes supported: " << nrAttributes << std::endl;
// 画三角形
VAOSetTriangle();
// 通过索引画两个三角形
//VAOSetRectangle();
// 3. build and compile our shader program
// ---------------------------------------
shaderSet();
// uncomment this call to draw in wireframe polygons.
// 设置边框模式绘画,默认是GL_FILL模式
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
// 4. 渲染循环
// -----------
// 在关闭窗口前不断绘制图像并接受用户输入
while (!glfwWindowShouldClose(window))
{
// 输入
processInput(window);
// 渲染指令
// 清除颜色缓冲
glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT); // 清空屏幕的颜色缓冲,并把颜色缓冲填充为glClearColor设置的颜色
// 激活着色器
glUseProgram(shaderProgram); // 激活着色器程序对象,调用这个函数之后每个着色器调用和渲染调用都会使用这个程序对象
// 更新uniform颜色
float timeValue = glfwGetTime(); // 获取运行秒数
float greenValue = (sin(timeValue) / 2.0f) + 0.5f;
float mv = 0.5f;
int vertexColorLocation = glGetUniformLocation(shaderProgram, "ourColor"); // 查询 uniform ourColor的位置值,
int vertexMvLocation = glGetUniformLocation(shaderProgram, "mv");
glUniform4f(vertexColorLocation, 0.0f, greenValue, 0.0f, 1.0f); // 设置uinform 值。(查询uniform地址不要求之前使用过着色器程序,但是更新一个uniform之前必须先使用程序)
glUniform1f(vertexMvLocation, mv);
// 画三角形
//glBindVertexArray(VAO);
glDrawArrays(GL_TRIANGLES, 0, 6);
// glBindVertexArray(0); // 不需要每次解绑
// 画两个三角形,使用索引
//glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
glfwSwapBuffers(window); // 交换颜色缓冲,用于绘制
glfwPollEvents(); // 检查事件并调用对应的回调函数
}
// 5. 退出前清理
// -------------
//glDeleteVertexArrays(1, &VAO);
//glDeleteBuffers(1, &VBO);
glDeleteProgram(shaderProgram);
// 6. 退出
// -------
glfwTerminate(); // 退出
return 0;
}
opengl 学习笔记:画三角形
最新推荐文章于 2023-11-06 20:59:45 发布