OpenGL(二)-更详细版的三角形

news2024/12/23 5:40:11

在上篇blog中已经画了一个三角形了,这篇讲解一下一个三角形的渲染过程。
上篇blog中的glbegin搭配glend的流程,在OpenGL3.2中已经被弃用了,3.3以后推荐使用VBO+EBO+VAO的流程。

图形渲染管线

作用:将三维坐标经过一系列变换,生成一个二维坐标,这个二维坐标的值就是渲染的结果。
在这里插入图片描述
在这里插入图片描述
渲染管线的过程就如上图所示,不同的图形API可能每个阶段的任务和名称有所不同,但是整体流程都大同小异。
顶点数据中包含了很多数据,包括:顶点坐标,顶点颜色,顶点法线等。

  • 顶点着色器:处理顶点
  • 曲面细分着色器:4.0以后才有的功能,自动将一个曲面细分为很多三角形,在复杂地形构建中很实用
  • 几何着色器:处理一个图元,可以同时访问图元中所有的三角形的所有顶点
  • 光栅化:将3D连续的三角形进行格栅化,对应输出像素。连续的三角形变成离散的像素点数据
  • 片段着色器:片段着色器的主要目的是计算一个像素的最终颜色,这也是所有OpenGL高级效果产生的地方。通常,片段着色器包含3D场景的数据(比如光照、阴影、光的颜色等等),这些数据可以被用来计算最终像素的颜色。

数据的流动

我们使用显卡进行渲染,需要将数据由内存放到显存里面,OpenGL中使用 顶点缓冲对象(Vertex Buffer Objects, VBO) 管理这部分用于存放顶点数据的显存。但是VBO只是存储数据,OpenGL读取的数据的时候,需要知道数据的格式,数据存储的位置,每个顶点数据的大小等等。这步我们可以使用 顶点数组对象(Vertex Array Object, VAO) 来记录,后续当我们想使用这个VBO的数据时,先绑定一下对应的VAO,就可以知道数据的具体信息了。

相邻三角形的两个顶点是共用的,如果按照上面的存储方式三角形的共同顶点会被存储三次,这样会浪费很多空间,所以常见的存储方式是,将顶点单独存储起来,三角形中存储顶点索引,渲染的时候根据索引在顶点数据序列中找顶点数据。在OpenGL中,使用 元素缓冲对象(Element Buffer Object,EBO) 来实现这个功能,

其中蓝色部分,是要使用着色器语言编程的,OpenGL使用GLSL着色器语言。

渲染管线是软硬相耦合的,具体发展历史可以看这两篇blog:
GPU硬件发展
GPU软件发展

代码

使用VAO和VBO画三角形的代码
OpenGL代码:

#include <iostream>
#include "glad/glad.h" //管理OpenGL的函数指针的
#include "GLFW/glfw3.h"
#include "gl/GL.h"



void framebufferSizeCallback(GLFWwindow* window, int width, int height);
void processInput(GLFWwindow* window);
void rendering(GLFWwindow* window);
unsigned int shaderProgramInit();

int main()
{
	glfwInit(); // 初始化GLFW
	glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
	glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); 
	glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);// 配置GLFW,OpenGL版本,Core核心模式

	GLFWwindow* window = glfwCreateWindow(800, 600, "LearnOpenGL", NULL, NULL); //创建窗口
	if (window == NULL)
	{
		std::cout << "Failed to create GLFW window" << std::endl;
		glfwTerminate();
		return -1;
	}

	glfwMakeContextCurrent(window); //将上下文设置为该窗口

	// 给GLAD传入用来加载系统相关的OpenGL函数指针地址的函数
	if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
	{
		std::cout << "Failed initialize GLAD" << std::endl;
		return -1;
	}
	glViewport(0, 0, 800, 600); 
	//设置视口,即窗口中需要渲染的部分,前两个参数是左下角坐标,后两个是宽高
	//当用户改变窗口的大小的时候,视口也应该被调整,可以设置一个窗口回调函数,自动调整
	//将回调函数注册到GLFW
	glfwSetFramebufferSizeCallback(window, framebufferSizeCallback);

	while (!glfwWindowShouldClose(window)) //渲染主循环
	{
		glClearColor(0.2f, 0.3f, 0.3f, 1.0f); //使用颜色清除buffer
		glClear(GL_COLOR_BUFFER_BIT);

		processInput(window); //处理输入事件
		rendering(window);
		glfwSwapBuffers(window); //交换渲染buffer,将glfw窗口显示到屏幕。双缓冲buffer,之前龚大大视频讲过
		glfwPollEvents(); //检查有没有触发什么事件,调用相应的回调函数
	}

	glfwTerminate(); //结束glfw
	return 0;

}

void framebufferSizeCallback(GLFWwindow* window, int width, int height)
{
	glViewport(0, 0, width, height);
}

void processInput(GLFWwindow* window)
{
	if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
		glfwSetWindowShouldClose(window, true);
}

void rendering(GLFWwindow* window)
{
	//OpenGL的坐标系是-1到1之间,中心点在屏幕中心,与OpenCV和DX的不同。
	float vertices[] = {
		-0.5f, -0.5f, 0.0f,
		 0.5f, -0.5f, 0.0f,
		 0.0f,  0.5f, 0.0f
	};
	unsigned int shaderProgram = shader_program_init();
	

	unsigned int VBO;
	glGenBuffers(1, &VBO); //创建顶点缓冲buffer,在显存上分配一段空间,存储顶点数据

	unsigned int VAO;
	glGenVertexArrays(1, &VAO); //创建VAO,存储应该使用的VBO位置,以及该VBO中顶点数据的组成和分布
	glBindVertexArray(VAO);


	glBindBuffer(GL_ARRAY_BUFFER, VBO); //将这段内存绑定到顶点缓存上,GL_ARRAY_BUFFER表示缓存buffer的类型
	glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); 
	// 传输数据,类似于cudaMemcpy作用,
	// 最后的GL_STATIC_DRAW是向显卡表示如何管理该段数据,
	// GL_STATIC_DRAW表示该段数据几乎不变化,GL_DYNAMIC_DRAW表示该段数据经常变换,GL_STREAM_DRAW表示该段数据每次渲染都会变换
	// 显卡会根据数据的特性选择如何管理数据,以达到最快速度

	//渲染程序已经搞定了,接下来要告诉程序如何从缓冲区获取并解析顶点数据
	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
	// arg1表示顶点坐标信息在顶点数据中的哪个位置,arg2表示数据格式,arg3决定是数据否需要归一化,arg4表示每个顶点数据的大小,作为step来判断取下一个顶点数据
	// arg5表示顶点数据在缓冲区的那个位置,因为在缓冲区开头位置,所以直接为0
	
	//一般不需要主动解绑VAO和VBO,当我们再次绑定其他VAO,VBO时,会自动把旧的解绑换新的。

	//当我们要渲染一个物体的时候
	glEnableVertexAttribArray(0);// 开启顶点渲染功能
	glBindVertexArray(VAO); //告诉显卡,用这个VAO
	glUseProgram(shaderProgram); //告诉显卡,使用这个着色器
	glDrawArrays(GL_TRIANGLES, 0, 3);

}

unsigned int shaderProgramInit()
{
	const char* vertexShaderSource = "#version 330 core\n"
		"layout (location = 0) in vec3 aPos;\n" //告诉顶点坐标信息在哪里,position=0,也就是一开始就是坐标信息
		"void main()\n"
		"{\n"
		"   gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n"
		"}\0"; // GLSL代码,先存储到一个字符串中,后续多了可以存到文件里

	unsigned int vertexShader; //着色器ID
	vertexShader = glCreateShader(GL_VERTEX_SHADER); //创建VERTEX_SHADER类型的shader,返回shader的ID
	glShaderSource(vertexShader, 1, &vertexShaderSource, NULL); //将着色器源代码绑定到shader
	glCompileShader(vertexShader); //编译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;
	}

	const char* fragmentShaderSource = "#version 330 core \n"
		"out vec4 FragColor;\n" //声明输出变量FragColor,类型为vec4
		"void main()\n"
		"{FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);}\0";
	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;
	}

	unsigned int shaderProgram;
	shaderProgram = glCreateProgram(); //创建着色程序,可以理解为前面都是在编译目标文件,这一步是将所有目标文件链接成一个可执行文件
	glAttachShader(shaderProgram, vertexShader);
	glAttachShader(shaderProgram, fragmentShader); //添加shader,着色器程序会自动把上一个shader的输出作为下一个shader的输入,如果输出输入格式不匹配,就会报错
	glLinkProgram(shaderProgram);
	glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
	if (!success) {
		glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog);
		std::cout << "ERROR::SHADER::PROGRAM::LINK_FAILED\n" << infoLog << std::endl;
	}

	glDeleteShader(vertexShader);
	glDeleteShader(fragmentShader); //清理掉之前的shader,已经不需要了
	
	return shaderProgram;
}

使用VAO+VBO+EBO画矩形的代码:


void rectangleRendering()
{
	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开始! 
		// 此例的索引(0,1,2,3)就是顶点数组vertices的下标,
		// 这样可以由下标代表顶点组合成矩形

		0, 1, 3, // 第一个三角形
		1, 2, 3  // 第二个三角形
	};

	unsigned int shaderProgram = shaderProgramInit();

	unsigned int VBO;
	glGenBuffers(1, &VBO);
	glBindBuffer(GL_ARRAY_BUFFER, VBO);
	glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

	unsigned int VAO;
	glGenVertexArrays(1, &VAO); //创建VAO,存储应该使用的VBO位置,以及该VBO中顶点数据的组成和分布
	glBindVertexArray(VAO);

	unsigned int EBO;
	glGenBuffers(1, &EBO);
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
	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);
	glUseProgram(shaderProgram);
	glBindVertexArray(VAO);
	glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
	// 跟glDrawArrays基本一样,arg1表示模式,arg2表示绘画点数,arg3表示数据类型,arg4表示索引偏移量
}

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

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

相关文章

【Day09】

目录 Mybatis-基础操作-环境准备 Mybatis-基础操作-删除 Mybatis-基础操作-删除&#xff08;预编译SQL&#xff09; Mybatis-基础操作-新增 Mybatis-基础操作-新增(主键返回) Mybatis-基础操作-更新 Mybatis-基础操作-查询&#xff08;根据ID查询&#xff09; Mybatis-基…

YOLOv8改进 | Conv篇 | YOLOv8引入DWR

1. DWR介绍 1.1 摘要:当前的许多工作直接采用多速率深度扩张卷积从一个输入特征图中同时捕获多尺度上下文信息,从而提高实时语义分割的特征提取效率。 然而,这种设计可能会因为结构和超参数的不合理而导致多尺度上下文信息的访问困难。 为了降低多尺度上下文信息的绘制难度…

【系统分析师】-2024-2010年系统分析师历年论文题目

目录 2024.5月 2023 2022 2021 2020 2019 预测2024年11月 2024.5月 信息系统工程 论基于架构的软件设计方法信息系统工程 论性能测试方法及其应用信息系统工程 论云原生应用开发数据库及应用 论多源数据集成方法及其应用 2023 信息系统工…

HTB-Pennyworth(cve查询 和 exp使用)

前言 各位师傅大家好&#xff0c;我是qmx_07,今天给大家讲解Pennyworth靶场 渗透过程 信息搜集 服务器端口开放了8080http端口 访问网站 服务器使用jenkins cms系统&#xff0c;版本是2.289.1 通过弱口令爆破&#xff0c;账户是root,密码是password 通过命令执行nday 连…

Leetcode面试经典150题-76.最小覆盖子串

解法都在代码里&#xff0c;不懂就留言或者私信 理论上提交这个就是最优解 class Solution {public String minWindow(String s, String t) {if(s.length() < t.length()) {return "";}/**转成字符数组 */char[] sArr s.toCharArray();char[] tArr t.toCharAr…

Docker编译环境的使用(ubuntu)

目录 Ubuntu安装docker 重启docker 拉取镜像 进入docker安装软件 提交docker 添加用户到docker组 进入docker 添加build用户 停止容器 保存docker镜像 load镜像 删除容器 Ubuntu安装docker sudo apt install docker.io 国内可用的源 Welcome to nginx! (tence…

git使用基础教程

(一)Git下载 git官网 - downloads 创建本地数据仓 1.创建文件夹 2.当前目录 cmd ---git init gitee.com注册登录 git创建项目 ide 项目地址本地数据库 ide项目上传云端 (一)git 1.git下载 2.新建仓库 3. 配置Configure---Version Control---Git----path to Git Get from…

某里227逆向分析

声明: 该文章为学习使用,严禁用于商业用途和非法用途,违者后果自负,由此产生的一切后果均与作者无关。 本文章未经许可禁止转载,禁止任何修改后二次传播,擅自使用本文讲解的技术而导致的任何意外,作者均不负责,若有侵权,请联系作者立即删除! 前言 这次会简单的讲解…

【中国国际航空-注册/登录安全分析报告】

前言 由于网站注册入口容易被黑客攻击&#xff0c;存在如下安全问题&#xff1a; 1. 暴力破解密码&#xff0c;造成用户信息泄露 2. 短信盗刷的安全问题&#xff0c;影响业务及导致用户投诉 3. 带来经济损失&#xff0c;尤其是后付费客户&#xff0c;风险巨大&#xff0c;造…

【LeetCode】最接近的三数之和

题目要求 解题思路 这道题解题方法和三数之和解题思路一样&#xff0c;可以参考上一篇博客 代码实现 class Solution { public:int threeSumClosest(vector<int>& nums, int target) {//排序sort(nums.begin(),nums.end());int lennums.size();//固定一个&#x…

流媒体协议RTSP(其二)

欢迎诸位来阅读在下的博文~ 在这里&#xff0c;在下会不定期发表一些浅薄的知识和经验&#xff0c;望诸位能与在下多多交流&#xff0c;共同努力 文章目录 前期博客一、RTSP简介二、请求消息结构三、应答消息结构四、RTSP交互流程 前期博客 流媒体与直播的基础理论&#xff08…

Datawhle X 李宏毅苹果书AI夏令营深度学习笔记之——卷积神经网络的前世今生

一、卷积神经网络简介 卷积神经网络&#xff08;Convolutional Neural Network, CNN&#xff09;是一种深度学习模型&#xff0c;尤其擅长处理图像和视频等高维度的数据。CNN 通过模仿人类视觉系统的工作方式&#xff0c;自动学习数据中的空间层次结构&#xff0c;使得它在计算…

启动第一个docker容器

1 、 docker pull ubuntu:20.04 下载镜像 2、 docker image ls 查看镜像 3、 docker run --nametest -itd 9df6d6105df2 创建并运行一个容器 4、 查看容器 docker ps -a 5、 登录容器 docker exec -it test /bin/bash 6 退出容器 exit 7 删除容器 docker rm 238514292c…

JVM面试真题总结(二)

文章收录在网站&#xff1a;http://hardyfish.top/ 文章收录在网站&#xff1a;http://hardyfish.top/ 文章收录在网站&#xff1a;http://hardyfish.top/ 文章收录在网站&#xff1a;http://hardyfish.top/ volatile关键字能防止指令重排序吗?如何实现? volatile关键字可…

2024国赛数学建模B题完整分析参考论文38页(含模型和可运行代码)

2024 高教社杯全国大学生数学建模完整分析参考论文 B 题 生产过程中的决策问题 目录 摘要 一、问题重述 二、问题分析 三、 模型假设 四、 模型建立与求解 4.1问题1 4.1.1问题1思路分析 4.1.2问题1模型建立 4.1.3问题1样例代码&#xff08;仅供参考&#xff09; 4.…

基于人工智能的图像风格迁移系统

目录 引言项目背景环境准备 硬件要求软件安装与配置系统设计 系统架构关键技术代码示例 数据预处理模型训练模型预测应用场景结论 1. 引言 图像风格迁移是一种计算机视觉技术&#xff0c;它可以将一种图像的风格&#xff08;如梵高的绘画风格&#xff09;迁移到另一幅图像上…

在VMware虚拟机中编译文件的时候报错:找不到头文件ft2build.h

以下是报错内容&#xff0c;提示说找不到头文件ft2build.h freetype_show_font.c:12:10: fatal error: ft2build.h: No such file or directory #include <ft2build.h> ^~~~~~~~~~~~ compilation terminated. 在编译之前已经交叉编译了freetype&#xff0c;…

如何使用 Python 读取 Excel 文件:从零开始的超详细教程

“日出东海落西山 愁也一天 喜也一天 遇事不钻牛角尖” 文章目录 前言文章有误敬请斧正 不胜感恩&#xff01;||Day03为什么要用 Python 读取 Excel 文件&#xff1f;准备工作&#xff1a;安装所需工具安装 Python安装 Pandas安装 openpyxl 使用 Pandas 读取 Excel 文件什么是 …

归并排序-非递归实现

归并排序的非递归实现 我们可以把 一个数组 先拆分成 最小单元&#xff0c;这是分&#xff0c; 拆分成最小单元之后&#xff0c;我们对每个最小单元进行一次合并&#xff0c;这是治 最小单元 合并一次之后&#xff0c;我们继续 在上一次合并的基础上拆分&#xff0c;并且合…

心觉:你为什么没有更多的钱

很多人希望自己可以赚更多的钱 但是他的内心又很讨厌钱&#xff0c;他自己并不知道 一边希望自己赚更多钱&#xff0c;一边在骨子里觉得“金钱是万恶之源” 这是一种神经质的错乱 这种现象在什么情况下会表现得比较明显呢&#xff1f; 某位高官因为贪污受贿落马了&#xf…