[OpenGL]使用OpenGL绘制三角形

news2024/9/27 7:22:00

一、简介

本文介绍了如何在linux/win(wsl2)环境下,使用GLFW+GLAD实现绘制三角形。
本文内容基本根据LearnOpengGL-入门-你好,三角形整理完成,读者也可以参考LearnOpengGL-入门-你好,三角形自行学习如何使用OpenGL绘制三角形。

按照本文一步一步实现后,最终会得到以下结果:

Hello triangle

二、使用OpenGL绘制三角形

0. 环境需要

  • Linux 或者 windos下使用wsl2
  • 安装GLFW和GLAD,请参考[OpenGL] wsl2上安装使用cmake+OpenGL教程

1. 项目文件夹结构

文件夹结构

2. CMakeLists.txt 代码

cmake_minimum_required(VERSION 3.10)
set(CMAKE_CXX_STANDARD 14)

project(OpenGL_Hello_Triangle)

include_directories(include)

find_package(glfw3 REQUIRED)
file(GLOB project_file main.cpp glad.c)
add_executable(${PROJECT_NAME} ${project_file})
target_link_libraries(${PROJECT_NAME} glfw)

3. main.cpp 代码

#include <glad/glad.h>
#include <GLFW/glfw3.h>

#include <iostream>
// 用于处理窗口大小改变的回调函数
void framebuffer_size_callback(GLFWwindow *window, int width, int height);
// 用于处理用户输入的函数
void processInput(GLFWwindow *window);

// 指定窗口默认width和height像素大小
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;
/*-----vertex shader 和 fragment shader 代码-----*/
// 若对glsl编写的shader程序不了解,可以忽略此处的代码

// vertex shader, 用于处理各顶点的位置信息,此处的vertex shader不会对顶点位置进行操作
// 只需要记住此处我们将顶点position数据的 location 设为 0
// layout (location = 0) in vec3 aPos; 这句代码用来设置用到的 顶点 position 数据的location值
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";
// fragment shader,用于处理各顶点的颜色信息,此处的fragment shader将各顶点的颜色(r,g,b,alpha)设置为
// (1.0f,0.5f,0.2,1.0f)
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 main()
{

    // glfw 初始化 + 配置 glfw 参数
    glfwInit();
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

    // 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;
    }
    // 设置窗口window的上下文
    glfwMakeContextCurrent(window);
    // 配置window变化时的回调函数
    glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);

    // 使用 glad 加载 OpenGL 中的各种函数
    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
    {
        std::cout << "Failed to initialize GLAD" << std::endl;
        return -1;
    }

    /* 构建,编译 shader 程序 */
    // vertex shader
    unsigned int vertexShader = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
    glCompileShader(vertexShader);
    // 检查是否成功编译 vertex shader
    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
    unsigned int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
    glCompileShader(fragmentShader);
    // 检查是否成功编译 fragment shader
    glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
    if (!success)
    {
        glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);
        std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << std::endl;
    }

    // 链接 shaders
    unsigned int shaderProgram = glCreateProgram();
    glAttachShader(shaderProgram, vertexShader);
    glAttachShader(shaderProgram, fragmentShader);
    glLinkProgram(shaderProgram);
    // 检查是否成功链接 vertex shader 和 fragment shader
    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);

    /* 设置待绘制的三角形的数据 */
    // ------------------------------------------------------------------
    // 三角形 position
    float vertices[] = {
        0.0f,  0.5f,  0.0,  // 上
        -0.5f, -0.5f, 0.0f, // 左下
        0.5f,  -0.5f, 0.0f  // 右下
    };
    // 三角形 index
    // 我们只需要绘制一个三角形,三角形的三个点的position分别使用 vertices 数组中的第0,第1和第2个点的position数据
    unsigned int indices[] = {
        0, 1, 2 // first Triangle
    };
    /* 初始化 VBO, VAO 和 EBO */
    // 1. VBO 用来打包三角形各顶点的所有数据(比如position, normal, color等。本文中只用到了position数据,即
    // vertices数组),并将打包好的数据一起传给GPU。
    // 2. EBO 用来打包(存储)待绘制的三角形的索引数据,即 indices 数组,用于指定顶点的顺序。
    // 3. VAO 用于记录顶点属性的格式,即如何从 VBO中提取数据。并存储了EBO数据,从而管理顶点数据的输入配置。

    // 生成 VBO, VAO 和 EBO
    unsigned int VBO, VAO, EBO;
    glGenVertexArrays(1, &VAO); // 生成一个VAO对象
    glGenBuffers(1, &VBO);      // 生成一个VBO对象(VBO对象本质上是一个buffer)
    glGenBuffers(1, &EBO);      // 生成一个EBO对象(EBO对象本质上也是一个buffer)
    // 使用 VBO, VAO 和 EBO 的顺序为
    // 先绑定 VAO -> 绑定 VBO -> 设置如何读取VBO中数据 -> 绑定 EBO -> 解绑 VAO -> 解绑 VBO
    glBindVertexArray(VAO); // 绑定VAO

    glBindBuffer(GL_ARRAY_BUFFER, VBO); // 绑定VBO
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices,
                 GL_STATIC_DRAW); // 将vertices中的数据复制到刚刚绑定的VBO buffer中去,VBO buffer是GPU内存上的一块区域

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO); // 绑定EBO
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices,
                 GL_STATIC_DRAW); // 将indices中的数据复制到刚刚绑定的EBO buffer中去,EBO buffer是GPU内存上的一块区域

    // 使用glVertexAttribPointer函数告诉OpenGL该如何解析顶点数据
    // glVertexAttribPointer() 需要6个参数,每个参数的含义如下:
    // 1. 指定要配置的顶点属性, 即 shader 中指定 数据 location 值,此处填入 0
    // 2. 指定顶点属性的大小,shader中我们将VBO中的数据传给了一个 vec3 类型的变量 aPos,vec3 是一个包含3个float变量的
    // vector,因此此处填入 3
    // 3. 指定数据的类型,用于使用的是浮点型,因此此处填入 GL_FLOAT
    // 4. 指定是否自动对数据进行归一化,我们不需要自动诡异化,因此此处填入 GL_FALSE
    // 5.
    // 指定VBO中数据的步长,即连续的顶点属性组之间的间隔,因为我们的VBO(vertices数组)中的数据紧密相连,每组数据在3*float之后,因此此处填入
    // 3*sizeof(float)
    // 6. 指定读取VBO(vertices数组)的起始位置偏移,由于位置数据在数组的开头,所以这里填入 0
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void *)0); // 设置如何读取VBO中数据

    glEnableVertexAttribArray(0); // 启用 location = 0 处的数据,因此该函数的参数为 0

    glBindBuffer(GL_ARRAY_BUFFER, 0); // 解绑VBO

    // remember: do NOT unbind the EBO while a VAO is active as the bound element buffer object IS stored in the VAO;
    // keep the EBO bound.
    // glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
    // 注意!
    // 在解绑 VAO 之前不要解绑 EBO (但是可以解绑VBO)
    // 这是因为 VAO
    // 只是存储了如何读取VBO中数据的信息,当调用glVertexAttribPointer()函数之后VAO中就有了如何读取VBO数据的配置,而绑定EBO确是实实在在地将EBO数据存储到VAO中,因此在解绑VAO之前不能解绑EBO

    glBindVertexArray(0); // 解绑 VAO

    // 绘制主循环
    while (!glfwWindowShouldClose(window))
    {
        // input
        // -----
        processInput(window);

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

        // 使用我们之前编译连接好的 shader 程序
        glUseProgram(shaderProgram);
        glBindVertexArray(VAO); // 绑定VAO,指定当前绘制使用的VAO对象
        // glDrawArrays(GL_TRIANGLES, 0, 6);
        // 使用glDrawElements()函数指定使用基于EBO索引的绘制,函数参数如下:
        // 1. 指定绘制的模式,绘制模式选择绘制三角形,因此填入 GL_TRIANGLES
        // 2. 指定绘制的顶点个数,我们只需要绘制一个三角形,3个点,因此填入 3
        // 3. 指定绘制的索引类型,即EBO中的数据类型,类型为unsigned int,因此填入 GL_UNSIGNED_INT
        // 3. 指定绘制的索引起始偏移,此处填入 0
        glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_INT, 0);

        glfwSwapBuffers(window); // 在gfw中启用双缓冲,确保绘制的平滑和无缝切换
        glfwPollEvents(); // 用于处理所有挂起的事件,例如键盘输入、鼠标移动、窗口大小变化等事件
    }

    // 释放之前申请的 VBO, VAO, EBO资源和shader程序
    glDeleteVertexArrays(1, &VAO);
    glDeleteBuffers(1, &VBO);
    glDeleteBuffers(1, &EBO);
    glDeleteProgram(shaderProgram);

    // glfw 释放 glfw使用的所有资源
    glfwTerminate();
    return 0;
}

// 用于处理用户输入的函数
void processInput(GLFWwindow *window)
{
    // 当按下 Esc 按键时调用 glfwSetWindowShouldClose() 函数,关闭窗口
    if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
        glfwSetWindowShouldClose(window, true);
}

// 在使用 OpenGL 和 GLFW 库时,处理窗口大小改变的回调函数
// 当窗口大小发生变化时,确保 OpenGL 渲染的内容能够适应新的窗口大小,避免图像被拉伸、压缩或出现其他比例失真的问题
void framebuffer_size_callback(GLFWwindow *window, int width, int height)
{
    glViewport(0, 0, width, height);
}

4. 编译运行及结果

编译运行:

cd ./build
cmake ..
make 
./OpenGL_Hello_Triangle

绘制结果:

绘制结果

三、参考引用

[1]. LearnOpenGL-CN-入门-你好,三角形

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2125863.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【人工智能学习笔记】3_2 机器学习基础之机器学习经典算法介绍

线性回归算法的定义和任务类型 定义:线性回归是利用数理统计中回归分析,来确定两种或两种以上变量间相互依赖的定量关系的一种统计分析方法任务类型:回归应用场景:异常指标监控 农业贷款监控过拟合和欠拟合 定义:过拟合和欠拟合用来度量模型泛化能力的直观表现欠拟合:模型…

笔记共享平台|基于Java+vue的读书笔记共享平台系统(源码+数据库+文档)

笔记共享平台|读书笔记共享平台系统 目录 基于Javavue的读书笔记共享平台系统 一、前言 二、系统设计 三、系统功能设计 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码获取&#xff1a; 博主介绍&#xff1a;✌️大厂码农|毕设布道…

基于大数据的科研热点分析与挖掘系统

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长 QQ 名片 :) 1. 项目简介 科研活动的快速发展产生了大量的学术文献&#xff0c;如何从这些文献中提炼出有价值的科研热点和趋势成为了一个重要的问题。本项目旨在开发一个基于大数据的科研热点分析可视化系统&#xff0c;采…

微模块冷通道动环监控:智能化数据中心管理利器@卓振思众

在现代数据中心和机房管理中&#xff0c;微模块冷通道动环监控系统的引入&#xff0c;标志着对冷却和环境管理的新纪元。这一系统不仅提升了数据中心的运维效率&#xff0c;还对设备的安全性和稳定性提供了强有力的保障。本文将详细探讨微模块冷通道动环监控的功能和其在数据中…

Missing package to enable rendering OpenAI Gym in Colab

题意&#xff1a;“缺少用于在 Colab 中渲染 OpenAI Gym 的软件包。” 问题背景&#xff1a; Im attempting to render OpenAI Gym environments in Colab via a Mac using the StarAI code referenced in previous questions on this topic. However, it fails. The key erro…

基于开源WQ装备知识图谱的智能问答全流程构建

基于知识图谱的智能问答系统是一种利用知识图谱结构化数据来回答自然语言问题的技术。知识图谱存储了实体&#xff08;如人、地点、物品等&#xff09;及其之间的关系&#xff0c;允许智能系统查询相关的信息并推理答案。 1、知识图谱的智能问答系统核心流程&#xff1a; 问题…

如何通过海外云手机提升运营效率

随着技术的不断进步&#xff0c;市场上出现了越来越多的提高跨国电商运营效率的应用&#xff0c;海外云手机就是其中一个。海外云手机的优势体现在多个方面&#xff0c;那么如何通过使用海外云手机来提升运营效率&#xff1f;可以从以下几个方面了解。 首先&#xff0c;海外云手…

中国人民银行:数字人民币交易额已达7万亿元!中俄考虑使用国家数字货币进行双边结算!

近年来&#xff0c;数字货币的迅速发展引起了全球的广泛关注。中国人民银行&#xff08;PBOC&#xff09;近日透露&#xff0c;数字人民币&#xff08;e-CNY&#xff09;的交易额已接近1万亿美元&#xff0c;这标志着中国在数字货币领域的重大进展。同时俄罗斯也表示&#xff0…

shader 案例学习笔记之mix函数

mix函数&#xff1a; 在两个值之间进行插值&#xff1b; 使用&#xff1a; #ifdef GL_ES precision mediump float; #endifuniform vec2 u_resolution; uniform float u_time;vec3 colorA vec3(0.149,0.141,0.912); vec3 colorB vec3(1.0,0.83,0.224);void main(){vec2 st…

生成你想要的测试数据,除了用这6款工具,还能用AI

在软件测试中&#xff0c;测试数据是测试用例的基础&#xff0c;对测试结果的准确性和全面性有着至关重要的影响。因此&#xff0c;在进行软件测试时&#xff0c;需要生成测试数据以满足测试场景和要求。 本文将介绍什么情况下需要生成测试数据&#xff0c;常用的测试数据生成…

路径规划与轨迹跟踪系列算法学习 MATLAB 模型预测控制MPC

下面这张图的程序 019 路径规划与轨迹跟踪是自动驾驶汽车、无人机以及其他自动化系统中的关键技术之一。MATLAB 提供了丰富的工具箱来实现这些功能&#xff0c;其中模型预测控制&#xff08;Model Predictive Control, MPC&#xff09;是一种广泛使用的路径跟踪控制方法。下面是…

快速搭建最简单的前端项目vue+View UI Plus

1 引言 ‌‌Vue是一套用于构建Web前端界面的渐进式JavaScript框架。‌‌它以其易学易用、性能出色、灵活多变而深受开发者喜爱&#xff0c;并且与其他前端框架&#xff08;如‌React和‌Angular&#xff09;相比&#xff0c;在国内市场上受到了广泛的认可和使用。点击进入官方…

【线程池】Java 线程池 ThreadPoolExecutor 类源码介绍

文章目录 前言线程池是什么线程池解决了哪些问题本文主要讲述什么感谢读者 线程池 UML 类图ThreadPoolExecutor 内部设计核心参数内部类任务队列拒绝策略 ThreadPoolExecutor 源码线程池生命周期线程池构造函数execute() 【提交任务】addWorker() 方法 【添加工作线程并启动】了…

【微服务】⭐️华为云obs功能抽取到公共服务,供所有项目使用

目录 &#x1f378;前言 &#x1f37b;一、公共服务搭建 &#x1f37a;二、代码实现 1.工具类编写 2.项目引入使用 &#x1f379;三、章末 &#x1f378;前言 小伙伴们大家好&#xff0c;上次讲了如何本地对接华为云Obs对象存储服务&#xff0c;在本地项目中通过sdk引入调用…

【QT】常用控件-下

欢迎来到Cefler的博客&#x1f601; &#x1f54c;博客主页&#xff1a;折纸花满衣 &#x1f3e0;个人专栏&#xff1a;QT 目录 &#x1f449;&#x1f3fb;QComboBox&#x1f449;&#x1f3fb; QSpinBox&#x1f449;&#x1f3fb;QDateTimeEdit&#x1f449;&#x1f3fb;QD…

时序预测 | MATLAB实现BKA-XGBoost(黑翅鸢优化算法优化极限梯度提升树)时间序列预测

时序预测 | MATLAB实现BKA-XGBoost(黑翅鸢优化算法优化极限梯度提升树)时间序列预测 目录 时序预测 | MATLAB实现BKA-XGBoost(黑翅鸢优化算法优化极限梯度提升树)时间序列预测预测效果基本介绍模型描述程序设计参考资料 预测效果 基本介绍 Matlab实现BKA-XGBoost时间序列预测&a…

datasophon升级海豚调度dolphinscheduler为3.2.2

一、参考博主升级3.2.1文章&#xff1a;datasophon升级海豚调度为3.2.1_海豚调度3.2.2 mysql包找不到-CSDN博客 二、升级后woker-server启动报错如下&#xff1a; 原因是worker-server下conf/common.properties中的&#xff1a;resource.storage.typeNONE&#xff0c; 解决很简…

如何划分类/单一职权原则SRP

参考&#xff1a;单一职责 -- 每个类只负责一个功能_每个类应该只负责一个功能,遵循单一职责原则。-CSDN博客 类有且只有一个原因需要修改它&#xff0c;这样的才是一个结构简洁的类。 结合上面的例子&#xff0c;需要注意的点&#xff1a; 1.比如搜索数据库&#xff0c;需要…

Procdump抓ToDesk密码

目录 前言 1.工具教程 2.转储数据 3.密码获取 4.总结 前言 本文是因为在公众号上看到一篇文章随想着实战中利用ToDesk秀操作失败后&#xff0c;实验环境成功复现后写下。ProcDump[1] 是一个命令行实用工具&#xff0c;其主要用途是监视应用程序的 CPU 峰值&#xff0c;并在…

mybatis 查询Not Found TableInfoCache

近期在工程迁移中遇到一个mybatis查询的问题&#xff0c;检查代码没有问题&#xff0c;但是报Not Found TableInfoCache 解决过程 是不是数据库对应表错误或者实体类指定的表名错误 查看配置文件链接的数据源是否正确TableName中指定的表名然后去数据库看一下是否存在 如果…