【OpenGL学习】(二)OpenGL渲染简单图形

文章目录

  • 【OpenGL学习】(二)OpenGL渲染简单图形
    • OpenGL渲染图形流程
    • 顶点,图元和片元
    • VAO,VBO ,EBO
    • 着色器
    • 示例:使用OpenGL渲染三角形

【OpenGL学习】(二)OpenGL渲染简单图形

OpenGL渲染图形流程

CPU(中央处理器)↓
初始化窗口与OpenGL上下文↓
编译并链接着色器程序 ↓
准备顶点数据(顶点位置、颜色、法线、纹理坐标等)↓
创建并绑定:- VAO(顶点数组对象)← 用于记录 VBO/EBO 的绑定状态和顶点属性配置↓创建并绑定:- VBO(顶点缓冲对象)← 存储所有顶点属性数据(位置信息、颜色、纹理坐标等)- EBO(索引缓冲对象,可选)← 存储索引数据,减少冗余顶点设置顶点属性指针(glVertexAttribPointer)← 告诉 OpenGL 如何解析 VBO 中的顶点数据启用顶点属性数组(glEnableVertexAttribArray)↑(这些设置会被 VAO 记录下来)调用:- glBufferData → 将顶点数据或索引数据从 CPU 传输到 GPU 显存中(显卡缓冲区)↓
====================  GPU 开始接管渲染流程 ====================
顶点着色器(Vertex Shader)- 每个顶点执行一次- 坐标变换:模型矩阵 × 视图矩阵 × 投影矩阵- 传出数据供后续阶段使用(如颜色、纹理坐标)↓
图元装配(Primitive Assembly)- 将一组顶点组装为图元(如三角形、线段)↓
(可选)几何着色器(Geometry Shader)- 每个图元执行一次- 可动态生成新的顶点或图元↓
光栅化(Rasterization)- 将图元转换为片元(像素候选)- 生成每个片元的屏幕位置↓
片段着色器(Fragment Shader)- 每个片元执行一次- 计算颜色值(光照、纹理采样、颜色混合等)↓
测试与混合阶段(由固定功能单元执行)- 深度测试、模板测试- α混合、遮挡判断↓
帧缓冲(Framebuffer)- 最终图像写入帧缓冲 → 显示在屏幕

在这里插入图片描述
图片来源:https://geekdaxue.co/read/Learn-OpenGL-CN/01-Getting-Started-04-Hello-Triangle.md

顶点,图元和片元

顶点(Vertex)是图形的基本构建单位,表示图形的一个顶点,通常包括:

  • 位置坐标(Position):如三维空间中的 (x, y, z)
  • 颜色信息(Color)
  • 纹理坐标(UV)
  • 法向量(Normal)
  • 其他自定义属性(如切线、位移等)

图元(Primitive)是由多个顶点组成的几何形状单元,比如:

  • 点(GL_POINTS)→ 每个顶点单独成图元
  • 线段(GL_LINES)→ 每两个顶点组成一条线
  • 三角形(GL_TRIANGLES)→ 每三个顶点组成一个三角形
  • 更多复合图元(如 GL_TRIANGLE_STRIP, GL_TRIANGLE_FAN)

片元(Fragment)是每个图元经过光栅化后生成的像素候选者,这些片元会传入片段着色器进行着色计算(颜色、纹理、光照等)。在经过深度测试、模板测试、混合等处理之后,部分片元会变成屏幕上的像素。

VAO,VBO ,EBO

VAO(顶点数组对象,Vertex Array Object)用于 存储顶点属性配置状态,如顶点属性指针、VBO绑定状态、EBO绑定状态等。每次绘制时只需绑定一次 VAO,而不用重复设置顶点属性。

VBO(顶点缓冲对象,Vertex Buffer Object)用于在 GPU 显存中存储顶点数据(如位置、颜色、法线、纹理坐标等),避免每次绘制时从 CPU 向 GPU 频繁传输数据,提升效率。

EBO(元素缓冲对象,Element Buffer Object)用于在 绘制图形时按照索引重用顶点数据。节省存储空间,避免重复顶点。

着色器

着色器(Shader)是运行在 GPU 上的小程序,用于控制图形渲染的每个阶段。它是实现可编程渲染管线的核心。

着色器的主要类型:

着色器类型作用
顶点着色器 (Vertex Shader)处理每个顶点的位置变换、法线、纹理坐标等
片段着色器 (Fragment Shader)处理每个像素(片元)的颜色计算、纹理映射、光照等
几何着色器(可选) (Geometry Shader)处理图元(点、线、三角形),可生成新图元
曲面细分控制/评估着色器(可选)用于对几何细分
计算着色器(Compute Shader)用于并行计算任务,不用于图形绘制

着色器使用的语言是GLSL(OpenGL Shading Language)。GLSL的语法类似于 C 语言,且可以运行于 GPU 上。我们需要在 C++ 中编写 GLSL 代码(字符串形式),然后使用 OpenGL API 编译和链接着色器。

示例:使用OpenGL渲染三角形

#include <glad/glad.h>  // 加载 OpenGL 函数指针
#include <GLFW/glfw3.h> // GLFW 用于创建窗口和处理输入#include <iostream>   // 回调函数声明:当窗口大小发生改变时调用
void framebuffer_size_callback(GLFWwindow* window, int width, int height);
// 处理输入的函数声明
void processInput(GLFWwindow* window);// 设置窗口宽高
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;// 顶点着色器GLSL源码
const char* vertexShaderSource = "#version 330 core\n" // 使用 OpenGL 3.3 对应的 GLSL 版本(即 GLSL 3.30)
"layout (location = 0) in vec3 aPos;\n"  // 顶点位置属性,位置值为0
"void main()\n"
"{\n"
"   gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n"  // 设置顶点位置
"}\0";// 片段着色器GLSL源码
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()
{// 初始化并配置 GLFWglfwInit();glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); // OpenGL 主版本号glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); // OpenGL 次版本号glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // 使用核心模式#ifdef __APPLE__glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // MacOS 需要加这句
#endif// 创建 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,用于加载 OpenGL 函数if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)){std::cout << "Failed to initialize GLAD" << std::endl;return -1;}// ------------------构建并编译着色器程序------------------// 顶点着色器unsigned int 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 = 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;}// 着色器程序unsigned int 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);// --------------------------------------------------------// 设置顶点数据和缓冲,并配置顶点属性float vertices[] = {-0.5f, -0.5f, 0.0f, // 左下角0.5f, -0.5f, 0.0f, // 右下角0.0f,  0.5f, 0.0f  // 顶部};// unsigned int VBO, VAO;glGenVertexArrays(1, &VAO); // 创建顶点数组对象glGenBuffers(1, &VBO);      // 创建顶点缓冲对象glBindVertexArray(VAO);     // 绑定 VAOglBindBuffer(GL_ARRAY_BUFFER, VBO); // 绑定 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); // 启用顶点属性glBindBuffer(GL_ARRAY_BUFFER, 0); // 解绑 VBO,为了安全,非必须glBindVertexArray(0);            // 解绑 VAO// 可以取消注释以使用线框模式绘制:也就是不填充图形,只画出边框线// glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);// 渲染循环while (!glfwWindowShouldClose(window)){// 处理输入processInput(window);// 清屏并设置背景颜色glClearColor(0.2f, 0.3f, 0.3f, 1.0f);glClear(GL_COLOR_BUFFER_BIT);// 绘制三角形glUseProgram(shaderProgram); // 使用着色器程序glBindVertexArray(VAO);      // 绑定 VAOglDrawArrays(GL_TRIANGLES, 0, 3); // 从第0个顶点绘制3个顶点构成的三角形// 交换缓冲区并查询IO事件glfwSwapBuffers(window);glfwPollEvents();}// 可选:释放所有资源glDeleteVertexArrays(1, &VAO);glDeleteBuffers(1, &VBO);glDeleteProgram(shaderProgram);// 释放 GLFW 资源glfwTerminate();return 0;
}// 处理输入:如果按下 ESC 键,则关闭窗口
void processInput(GLFWwindow* window)
{if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)glfwSetWindowShouldClose(window, true);
}// 当窗口大小发生改变时,自动调整视口大小
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{glViewport(0, 0, width, height); // 设置 OpenGL 视口大小
}

在这里插入图片描述
只画边框线:

参考:
https://learnopengl-cn.github.io/01%20Getting%20started/04%20Hello%20Triangle/

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

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

相关文章

基于STM32的INA226电压电流检测仪

系统总体框图 功率检测装置原理图功能及模块连接说明 一、系统功能概述 该装置以STM32F103C8T6微控制器为核心&#xff0c;集成功率检测、数据交互、状态显示和用户提示功能&#xff0c;通过模块化设计实现稳定运行。 二、各模块功能及连接方式 按键模块 功能&#xff1a…

YOLOv2目标检测算法:速度与精度的平衡之道

一、YOLOv2的核心改进&#xff1a;从V1到V2的蜕变 YOLOv2作为YOLO系列的第二代算法&#xff0c;在继承V1端到端、单阶段检测的基础上&#xff0c;针对V1存在的小目标检测弱、定位精度低等问题进行了全方位升级&#xff0c;成为目标检测领域的重要里程碑。 &#xff08;一&am…

将嵌入映射到 Elasticsearch 字段类型:semantic_text、dense_vector、sparse_vector

作者&#xff1a; Andre Luiz 讨论如何以及何时使用 semantic_text、dense_vector 或 sparse_vector&#xff0c;以及它们与嵌入生成的关系。 通过这个自定进度的 Search AI 实践学习亲自体验向量搜索。你可以开始免费云试用&#xff0c;或者在本地机器上尝试 Elastic。 多年来…

uniapp取消浏览自动填充

为了防止浏览器自动将记住的密码回填进type"password"输入框&#xff0c;所以在type"password"输入框上面加了两行代码&#xff0c;使浏览器将密码填充到新加的输入框里&#xff0c;并将这两个input隐藏掉 <input type"password" autocomple…

从数据包到可靠性:UDP/TCP协议的工作原理分析

之前我们已经使用udp/tcp的相关接口写了一些简单的客户端与服务端代码。也了解了协议是什么&#xff0c;包括自定义协议和知名协议比如http/https和ssh等。现在我们再回到传输层&#xff0c;对udp和tcp这两传输层巨头协议做更深一步的分析。 一.UDP UDP相关内容很简单&#xf…

显卡、Cuda和pytorch兼容问题

这里写目录标题 驱动与CUDA版本兼容性问题1. **驱动与CUDA版本兼容性问题**2. **任务特性与硬件适配差异**3. **优化策略与框架配置差异**4. **散热与功耗限制**5. **数据传输与CPU瓶颈**排查建议总结 查询PyTorch中实际使用的CUDA版本**1. 查询PyTorch中实际使用的CUDA版本***…

DeepSeek 大模型部署全指南:常见问题、优化策略与实战解决方案

DeepSeek 作为当前最热门的开源大模型之一&#xff0c;其强大的语义理解和生成能力吸引了大量开发者和企业关注。然而在实际部署过程中&#xff0c;无论是本地运行还是云端服务&#xff0c;用户往往会遇到各种技术挑战。本文将全面剖析 DeepSeek 部署中的常见问题&#xff0c;提…

Hadoop的目录结构和组成

Hadoop 目录结构 bin 目录&#xff1a;包含了 Hadoop 的各种命令行工具&#xff0c;如hadoop、hdfs等&#xff0c;用于启动和管理 Hadoop 集群&#xff0c;以及执行各种数据处理任务。etc 目录&#xff1a;存放 Hadoop 的配置文件&#xff0c;包括core-site.xml、hdfs-site.xm…

Python Matplotlib 库【绘图基础库】全面解析

让AI成为我们的得力助手&#xff1a;《用Cursor玩转AI辅助编程——不写代码也能做软件开发》 一、发展历程 Matplotlib 由 John D. Hunter 于 2003 年创建&#xff0c;灵感来源于 MATLAB 的绘图系统。作为 Python 生态中最早的可视化工具之一&#xff0c;它逐渐成为科学计算领…

车载以太网驱动智能化:域控架构设计与开发实践

title: 车载以太网驱动专用车智能化&#xff1a;域控架构设计与开发实践 date: 2023-12-01 categories: 新能源汽车 tags: [车载以太网, 电子电气架构, 域控架构, 专用车智能化, SOME/IP, AUTOSAR] 引言&#xff1a;专用车智能化转型的挑战与机遇 专用车作为城市建设与工业运输…

图论模板(部分)

图论模板&#xff08;部分&#xff09; maincpp #include <iostream> #include <climits> #include <limits>typedef unsigned long long ull; typedef long long ll; typedef long double ld; typedef std::pair<int, int> PII;#define rep(i, n) f…

2025年【道路运输企业安全生产管理人员】考试题库及道路运输企业安全生产管理人员考试内容

一、考试概述 2025年道路运输企业安全生产管理人员考试题库由【安全生产模拟考试一点通】平台发布&#xff0c;涵盖安全生产法律法规、车辆技术管理、从业人员管理、应急预案编制等核心领域。考试重点考察考生对安全生产主体责任、风险管控、隐患排查等实务操作的掌握程度&…

分贝计在噪音污染源识别中的用途

分贝计在噪音污染源识别中的作用 噪音污染是现代社会面临的一个普遍问题&#xff0c;尤其在城市化进程加快的背景下&#xff0c;交通、工业、建筑和娱乐活动等产生的噪音对人们的生活质量和健康造成了严重影响。为了有效管理和控制噪音污染&#xff0c;首先需要准确识别噪音的…

deepin v23.1 搜狗输入法next配置中文输入法下默认用英文标点

deepin23.1下, fcitx5的 deepin next搜狗输入法的属性页无法配置中文状态下默认用英文标点, 但是可以改以下配置来实现这一点. 搜狗输入法运行期间&#xff0c;用户修改的配置被存储在以下位置&#xff1a; ~/.config/cpis/module/im/fcitx5/com.sogou.ime.ng.fcitx5.deepin/k…

C语言:在 Win 10 上,g++ 如何编译 gtk 应用程序

在 Windows 10 上使用 g&#xff08;或 gcc&#xff09;编译基于 GTK 的 C 语言程序是完全可行的&#xff0c;且相比 Tcc 更为推荐&#xff0c;因为 g&#xff08;GNU 编译器套件&#xff09;对 GTK 的支持更加完善&#xff0c;配置也更简单。以下是详细步骤和注意事项&#xf…

84.评论日记

原链接 这个视频我发了四五条评论。评论内容甚至和下面这个视频内的其他评论一样。 找了另外的账号也发了。 发现&#xff0c;无论是我这个账号&#xff0c;还是其他的账号&#xff0c;评论都无法看到。 我大胆猜测有一种机制&#xff0c;某些官号会被设置成一种高检测的等…

【RabbitMQ】整合 SpringBoot,实现工作队列、发布/订阅、路由和通配符模式

文章目录 工作队列模式引入依赖配置声明生产者代码消费者代码 发布/订阅模式引入依赖声明生产者代码发送消息 消费者代码运行程序 路由模式声明生产者代码消费者代码运行程序 通配符模式声明生产者代码消费者代码运行程序 工作队列模式 引入依赖 我们在创建 SpringBoot 项目的…

Python-92:最大乘积区间问题

问题描述 小R手上有一个长度为 n 的数组 (n > 0)&#xff0c;数组中的元素分别来自集合 [0, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024]。小R想从这个数组中选取一段连续的区间&#xff0c;得到可能的最大乘积。 你需要帮助小R找到最大乘积的区间&#xff0c;并输出这…

windows触摸板快捷指南

以下是结构化整理后的触控手势说明&#xff0c;采用清晰的层级划分和标准化表述&#xff1a; **触控手势操作规范****1. 单指操作****2. 双指操作****3. 三指操作****4. 四指操作** **优化说明&#xff1a;** 触控手势操作规范 1. 单指操作 手势功能描述等效操作单击滑动选择…

VSCode launch.json 配置参数详解

使用 launch.json 配置调试环境时&#xff0c;会涉及到多个参数&#xff0c;用于定义调试器的行为和目标执行环境。以下是一些常用的配置参数&#xff1a; 1、"type" &#xff1a;指定调试器的类型&#xff0c;例如 "node" 表示 Node.js 调试器&#xff0…