LearnOpenGL(二)之三角形

一、重要概念

  • 顶点数组对象:Vertex Array Object,VAO
  • 顶点缓冲对象:Vertex Buffer Object,VBO
  • 元素缓冲对象:Element Buffer Object,EBO 或 索引缓冲对象 Index Buffer Object,IBO

以数组的形式传递3个3D坐标作为图形渲染管线的输入,用来表示一个三角形,这个数组叫做顶点数据(Vertex Data);顶点数据是一系列顶点的集合。
一个顶点(Vertex)是一个3D坐标的数据的集合。而顶点数据是用顶点属性(Vertex Attribute)表示的,它可以包含任何我们想用的数据。

顶点着色器是图形渲染管线中的一个阶段,它负责处理输入顶点数据并将其转换为裁剪空间(Clip Space)或者屏幕空间(Screen Space)的坐标。
顶点着色器通常是图形渲染中的第一个阶段,在渲染过程中,每个顶点都会经过顶点着色器的处理。

片段着色器的主要目的是计算一个像素的最终颜色,这也是所有

OpenGL高级效果产生的地方。通常,片段着色器包含3D场景的

数据(比如光照、阴影、光的颜色等等),这些数据可以被用来

计算最终像素的颜色。

在所有对应颜色值确定以后,最终的对象将会被传到最后一个阶

段,我们叫做Alpha测试和混合(Blending)阶段

这个阶段检测片段的对应的深度(和模板(Stencil))值,用它们来

判断这个像素是其它物体的前面还是后面,决定是否应该丢弃。

这个阶段也会检查alpha值(alpha值定义了一个物体的透明度)

并对物体进行混合(Blend)。所以,即使在片段着色器中计算出来

了一个像素输出的颜色,在渲染多个三角形的时候最后的像素颜

色也可能完全不同。

二、顶点输入

OpenGL不是简单地把所有的3D坐标变换为屏幕上的2D像素;

OpenGL仅当3D坐标在3个轴(x、y和z)上-1.0到1.0的范围内时

才处理它。所有在这个范围内的坐标叫做标准化设备坐标

(Normalized Device Coordinates),此范围内的坐标最终显示在屏

幕上(在这个范围以外的坐标则不会显示)。

通过使用由glViewport函数提供的数据,进行视口变换(Viewport

Transform),标准化设备坐标(Normalized Device Coordinates)会

变换为屏幕空间坐标(Screen-space Coordinates)。所得的屏幕空

间坐标又会被变换为片段输入到片段着色器中。 定义这样的顶点

数据以后,我们会把它作为输入发送给图形渲染管线的第一个处

理阶段:顶点着色器。它会在GPU上创建内存用于储存我们的顶

点数据,还要配置OpenGL如何解释这些内存,并且指定其如何

发送给显卡。顶点着色器接着会处理我们在内存中指定数量的顶

点。

我们通过顶点缓冲对象(Vertex Buffer Objects, VBO)管理这个内

存,它会在GPU内存(通常被称为显存)中储存大量顶点。使用

这些缓冲对象的好处是我们可以一次性的发送一大批数据到显卡

上,而不是每个顶点发送一次。从CPU把数据发送到显卡相对较

慢,所以只要可能我们都要尝试尽量一次性发送尽可能多的数

据。当数据发送至显卡的内存中后,顶点着色器几乎能立即访问

顶点,这是个非常快的过程。

三、链接顶点属性

//顶点数据:

float vertices[] = {

-0.5f,  -0.5f,  0.0f,

 0.5f,  -0.5f,  0.0f,

 0.0f,   0.5f,  0.0f

};

顶点数据会被解析为下面这样子:

  • 位置数据被储存为32位(4字节)浮点值。
  • 每个位置包含3个这样的值。
  • 在这3个值之间没有空隙(或其他值)。这几个值在数组中紧密排列(Tightly Packed)。
  • 数据中第一个值在缓冲开始的位置。

使用glVertexAttribPointer函数告诉OpenGL该如何解析顶点数据(应用到逐个顶点属性上):

glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);

glEnableVertexAttribArray(0);

  • 第一个参数指定我们要配置的顶点属性。在顶点着色器中使用layout(location = 0)定义了position顶点属性的位置值,它可以把顶点属性的位置值设置为0。因为我们希望把数据传递到这一个顶点属性中,所以这里我们传入0
  • 第二个参数指定顶点属性的大小。顶点属性是一个vec3,它由3个值组成,所以大小是3。
  • 第三个参数指定数据的类型,这里是GL_FLOAT(GLSL中vec*都是由浮点数值组成的)。
  • 下个参数定义我们是否希望数据被标准化(Normalize)。如果我们设置为GL_TRUE,所有数据都会被映射到0(对于有符号型signed数据是-1)到1之间。我们把它设置为GL_FALSE。
  • 第五个参数叫做步长(Stride),它告诉我们在连续的顶点属性组之间的间隔。由于下个组位置数据在3个float之后,我们把步长设置为3 * sizeof(float)。要注意的是由于我们知道这个数组是紧密排列的(在两个顶点属性之间没有空隙)我们也可以设置为0来让OpenGL决定具体步长是多少(只有当数值是紧密排列时才可用)。一旦我们有更多的顶点属性,我们就必须更小心地定义每个顶点属性之间的间隔(这个参数的意思简单说就是从这个属性第二次出现的地方到整个数组0位置之间有多少字节)。
  • 最后一个参数的类型是void*,所以需要我们进行这个奇怪的强制类型转换。它表示位置数据在缓冲中起始位置的偏移量(Offset)。由于位置数据在数组的开头,所以这里是0。
//关键代码如下
#include <iostream>
#include <QDebug>
#include <glad/glad.h>
#include <GLFW/glfw3.h>int main(int argc, char *argv[])
{glfwInit();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); //mac os
#endifGLFWwindow* window = glfwCreateWindow(800, 600, "LearnOpenGL", NULL, NULL);if (window == NULL){qDebug() << "Failed to create GLFW window";glfwTerminate();return -1;}glfwMakeContextCurrent(window);glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)){qDebug() << "Failed to initialize GLAD";return -1;}//glViewport(0, 0, 800, 600);// 0. 当我们渲染一个物体时要使用着色器程序//顶点着色器unsigned int vertexShader;vertexShader = glCreateShader(GL_VERTEX_SHADER);glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);glCompileShader(vertexShader);// check for shader compile errorsint  success;char infoLog[512];glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);if(!success){glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);qDebug() << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog;}//片段着色器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);qDebug() << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog;}//把顶点和片段着色器附加到程序对象上unsigned int shaderProgram;shaderProgram = glCreateProgram();glAttachShader(shaderProgram, vertexShader);glAttachShader(shaderProgram, fragmentShader);glLinkProgram(shaderProgram);glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);if(!success){glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog);qDebug() << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog;}glDeleteShader(vertexShader);glDeleteShader(fragmentShader);//顶点数据:float vertices[] = {-0.5f, -0.5f, 0.0f,// left0.5f, -0.5f, 0.0f,// right0.0f,  0.5f, 0.0f,// top};// 1. 复制顶点数组到缓冲中供OpenGL使用unsigned int VBO, VAO;glGenVertexArrays(1, &VAO);glGenBuffers(1, &VBO);glBindVertexArray(VAO);// bind the Vertex Array Object first, then bind and set vertex buffer(s), and then configure vertex attributes(s).glBindBuffer(GL_ARRAY_BUFFER, VBO);glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);// 2. 设置顶点属性指针glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);glEnableVertexAttribArray(0);// note that this is allowed, the call to glVertexAttribPointer registered VBO as the vertex attribute's bound vertex buffer object so afterwards we can safely unbindglBindBuffer(GL_ARRAY_BUFFER, 0);// You can unbind the VAO afterwards so other VAO calls won't accidentally modify this VAO, but this rarely happens. Modifying other// VAOs requires a call to glBindVertexArray anyways so we generally don't unbind VAOs (nor VBOs) when it's not directly necessary.glBindVertexArray(0);//   glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);/* Loop until the user closes the window */while (!glfwWindowShouldClose(window)){processInput(window);glClearColor(0.2f, 0.3f, 0.3f, 1.0f);/* Render here */glClear(GL_COLOR_BUFFER_BIT);// 3. 绘制物体glUseProgram(shaderProgram);glBindVertexArray(VAO);glDrawArrays(GL_TRIANGLES, 0, 3);/* Swap front and back buffers */glfwSwapBuffers(window);/* Poll for and process events */glfwPollEvents();}// optional: de-allocate all resources once they've outlived their purpose:glDeleteVertexArrays(1, &VAO);glDeleteBuffers(1, &VBO);glDeleteProgram(shaderProgram);glfwTerminate();return 0;
}

代码下载:点击跳转

觉得有帮助的话,打赏一下呗。。

           

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

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

相关文章

《2024最新Java面试题及答案(带完整目录)》

2024最新Java面试题及答案有史以来见过的最全面试题 获取链接&#xff1a;《2024最新Java面试题及答案&#xff08;带完整目录&#xff09;》 更多技术书籍&#xff1a;技术书籍分享&#xff0c;前端、后端、大数据、AI、人工智能... ​ ​ ​ 4.1.9.8. 可重入锁&#xff…

【KMP】优质模板推荐+解读

KMP – y神模板 引言&#xff1a;由于本人与4月20日前周五的一节数据结构课中偶然听到了老师讲kmp这个算法的概念并且发现老师讲的听不懂一点儿导致异常难受&#xff0c;于是花了两天左右自行弄懂了kmp算法的逻辑&#xff0c;并写下本文以便以后复习&#xff0c;并作留念。 声明…

数据结构10:堆和堆排序

文章目录 树的概念及结构树的概念树的相关概念树的表示树在实际中的应用表示文件系统的目录树结构 二叉树概念及结构概念特殊的二叉树二叉树的性质二叉树的存储结构顺序存储链式存储 二叉树的顺序结构及实现二叉树的顺序结构堆的概念及结构 堆的实现堆的插入堆的删除堆的创建向…

8.1 二叉排序树 —— C语言实现

系列文章目录 参考船说系列——数据结构与算法中的第八章内容。 二叉排序树AVL树红黑树B-树 文章目录 系列文章目录前言一、二叉排序树基础二、二叉排序树的操作2.1 插入2.2 删除 三、代码演示总结参考文献 前言 数据结构 结构定义 结构操作 结构操作是用来维护结构性质的…

spring高级篇(二)

1、Aware和InitializingBean Aware和InitializingBean都与Bean的生命周期管理相关。 Aware接口: 概念: Aware接口是Spring框架中的一个标记接口&#xff0c;它表示一个类能够感知到&#xff08;aware of&#xff09;Spring容器的存在及其特定的环境。Spring框架提供了多个Awar…

自然语言处理基础面试

文章目录 TF-IDFbag-of-wordsBert 讲道理肯定还得有Transformer&#xff0c;我这边先放着&#xff0c;以后再加吧。 TF-IDF TF&#xff08;全称TermFrequency&#xff09;&#xff0c;中文含义词频&#xff0c;简单理解就是关键词出现在网页当中的频次。 IDF&#xff08;全称…

【Pytorch】PytorchCPU版或GPU报错异常处理(10X~4090D)

Pytorch为CPU版或GPU使用报错异常处理 文章目录 Pytorch为CPU版或GPU使用报错异常处理0.检查阶段1. 在conda虚拟环境中安装了torch2.卸载cpuonly3.从tsinghua清华源安装不完善误为cpu版本4.用tsinghua清华源安装成cpu错误版本5.conda中torch/vision/cudatoolkit版本与本机cuda版…

Python基础学习之**kwargs

在Python编程中&#xff0c;**kwargs 是一个强大的工具&#xff0c;它允许我们在函数定义中接受任意数量的关键字参数。kwargs 是 "keyword arguments" 的缩写&#xff0c;实际上是一个字典&#xff0c;其中包含了传递给函数的所有关键字参数。本文将详细介绍 **kwar…

文本美学:text-image打造视觉吸引力

当我最近浏览 GitHub 时&#xff0c;偶然发现了一个项目&#xff0c;它能够将文字、图片和视频转化为文本&#xff0c;我觉得非常有趣。于是我就花了一些时间了解了一下&#xff0c;发现它的使用也非常简单方便。今天我打算和家人们分享这个发现。 项目介绍 话不多说&#xf…

用或非门构成的基本触发器

用或非门构成的基本触发器 电路组成 & 逻辑符号 注意&#xff1a;与用与非门构成的基本触发器相比&#xff0c;不仅 R 、 S R、S R、S 的几何位置不同&#xff0c;而且其上无反号&#xff0c;即高电平有效&#xff0c; Q Q Q 和 Q ‾ \overline{Q} Q​ 仍表示触发器的状…

最短路问题之Bellman-Ford,SPFA算法,例题 负环

Bellman-Ford算法&#xff1a; Bellman-Ford算法用于解决带有负权边的单源最短路径问题。其基本思想是通过不断地松弛边来逐步求解最短路径。算法的主要步骤如下&#xff1a; 初始化&#xff1a;将源点到各个顶点的距离初始化为无穷大&#xff0c;源点的距离初始化为0。重复更…

Linux使用Libevent库实现一个网页服务器---C语言程序

Web服务器 这一个库的实现 其他的知识都是这一个专栏里面的文章 实际使用 编译的时候需要有一个libevent库 gcc httpserv.c -o httpserv -levent实际使用的时候需要指定端口以及共享的目录 ./httpserv 80 .这一个函数会吧这一个文件夹下面的所有文件共享出去 实际的效果, 这…