首页 > 其他分享 >1.2 HELLO 三角形

1.2 HELLO 三角形

时间:2023-07-25 14:56:49浏览次数:45  
标签:绑定 VAO 1.2 EBO VBO 解绑 三角形 GL HELLO

这一节,我觉得是相当有难度的。渲染一个三角形,就需要介绍GLSL语言,图形渲染管线(Graphics Pipeline)以及着色器(Shader),标准化设备坐标(NDC)等诸多概念。
图形渲染管线和坐标系统的变换当然很重要,但是我们现在还不需要懂,只要暂且弄懂这几件事就好了。

目录

至少要知道这个吧:我们在干啥?

通过阅读这一节,你应该已经大概明白了,我们的任务就是把顶点数据(Vertex Data)输入进去,经过了好多道工序,这些数据最终被“画”在屏幕上。在本节,除了顶点着色器和片段着色器是我们自己定义的,其他“工序”都是自动进行的。

需要懂的三个名词:VAO,VBO,EBO

● 顶点数组对象:Vertex Array Object(VAO)
● 顶点缓冲对象:Vertex Buffer Object(VBO)
● 元素缓冲对象:Element Buffer Object(EBO)/ 索引缓冲对象:Index Buffer Object(IBO)
我们先从这三个东西的含义来讨论它们。VAO,VBO和EBO,指明了我们输入的顶点数据是“怎么样的”。一个顶点可能有很多属性,比如位置、颜色,法向量等等。现在我们假设我们要画一个矩形,矩形长这样:

因为任何在OpenGL中的物体都是三维的,所以我们再添加一维z=0。

VBO:数据具体是什么?

VBO存放了具体的数据,假如数据是float类型,那么VBO中存放的就是:

{ 0.0f, 0.0f, 0.0f, 2.0f, 0.0f, 0.0f, 2.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f }
VAO:数据是什么意思?

VBO只是单纯的一堆计算机中的01数据罢了,VAO承担起了解释的责任。函数glVertexAttribPointer的含义可以看Learn OpenGL,不要期待我再复制粘贴到这里哦(虽然确实可以水字数)。

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

上面两行代码,规定了VBO的position属性。有人可能不服气了,你说position就是position啊?那我们提前瞅一眼vertex shader是怎么写的:

#version 330 core // 版本声明; 使用核心模式
layout (location = 0) in vec3 aPos; // 在顶点着色器中声明所有的输入顶点属性, 创建vec3输入变量aPos, 设定输入变量的位置值(location)

void main()
{
    gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);
}

在这里,我们可以简单的认为,有个vec3类型的变量叫aPos,给了它一个location=0的值,而glVertexAttribPointer的第一个参数,就是去找location=0的变量,然后把对应的数据输入到这个变量里。
剩下参数的意思大致如下,(VAO清清嗓子开始说话:)这些数据都传到aPos里,数据的类型是float,一个顶点的属性包括3个float。对于输入的position属性,开头偏移量为0,也就是从第1个数据开始传,一口气传3个。

EBO:数据怎么用?

我们通过绘制两个三角形组成了一个矩形,但是现在顶点只有4个而不是6个对吧?EBO规定了按照怎样的方式绘制数据。EBO把绘制顶点的顺序放在缓冲里:

{ 0, 1, 3, 1, 2, 3 }

然后一顿操作猛如虎,在当前窗口上用6个索引对应的顶点绘制2个三角形

VAO,VBO,EBO绑定与解绑顺序

你可能对三者的绑定和解绑顺序感到有点迷糊(至少我当时有点),让我们回到这张图:

VAO不仅有指向VBO的指针, 也有指向EBO的指针。我们可以很容易想到,绑定顺序应该是这样:

一开始绑定了VAO,然后在VAO仍然被绑定的情况下,绑定VBO。VAO就指向了VBO。又在这样的情况下绑定了EBO,VAO又指向了EBO。最后,我们解除了VAO的绑定,但是VAO对VBO和EBO的俩指向并没有消失。下次再使用这个VAO,它仍然保持了对VBO和EBO的指向(这样就不用每次使用VAO都重新绑定一次相应的VBO和EBO了)。

但是,如果我们先解绑EBO和VBO,那么VAO对它们两个的指向也会消失。

比较官方的说法如下:

  • 当目标是GL_ELEMENT_ARRAY_BUFFER的时候,VAO会储存glBindBuffer的函数调用。这也意味着它也会储存解绑调用,所以确保你没有在解绑VAO之前解绑索引数组缓冲,否则它就没有这个EBO配置了。

  • 您可以在之后解除绑定VAO,这样其他VAO调用就不会意外地修改这个VAO,但这种情况很少发生。修改其他VAO需要调用glBindVertexArray(),所以我们通常不会在没有直接必要的情况下解绑定VAO(或VBO)。

具体的绑定/解绑VAO的代码如下, VBO和EBO也类似。

glBindVertexArray(VAO_1); // 绑定VAO_1
glBindVertexArray(VAO_2); // 一行代码直接实现解绑VAO_1, 并绑定VAO_2
glBindVertexArray(0);     // 解绑VAO_2

在Render Loop里,也有绑定/解绑VAO的操作,我们一起来看下:

glBindVertexArray(0); // 首先,解绑
	while (!glfwWindowShouldClose(window))
    {
        // [...]
        // draw our first triangle
        glUseProgram(shaderProgram);
        // 在while循环开始之前, VAO解绑了。而在while循环中VAO一直是被绑着的,
        // 但是为了规范性还是在render loop每次循环都绑定了
        glBindVertexArray(VAO); // seeing as we only have a single VAO there's no need to bind it every time, but we'll do so to keep things a bit more organized
        //glDrawArrays(GL_TRIANGLES, 0, 6);
        glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
        // 为啥no need呢, 看我写的官方说法第二条()
        // glBindVertexArray(0); // no need to unbind it every time 
    }

下面是绑定/解绑的代码实现,可以稍微看一眼:

// 创建顺序: VAO->VBO->EBO
// VAO
glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO);
// VBO
glGenBuffers(1, &VBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
// EBO
glGenBuffers(1, &EBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
// 解绑顺序: VAO->VBO->EBO
glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);

GLSL语言

和C语言类似,真的不算难,看懂本节的vertex shader和fragment shader应该还是轻松的,但是不难不代表它不重要哦。后面几节着色器会写成文件形式,然后将着色器程序(shader program)的操作都封装到一个头文件中,会方便很多。

// vertex shader
#version 330 core // 版本声明; 使用核心模式
layout (location = 0) in vec3 aPos; // 在顶点着色器中声明所有的输入顶点属性, 创建vec3输入变量aPos, 设定输入变量的位置值(location)
void main()
{
  	gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);
}
// fragment shader
#version 330 core
out vec4 FragColor;

void main()
{
    FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);
} 

代码实现与结果分析

我的代码可能和练习中的参考答案不太一样,如果有需要可以查阅。但是前两节的代码因为我之前重装系统没保存所以有些找不到了(目移)。从Texture开始的代码应该是都在的(应该)

● (仅存的)练习1-3代码:https://www.luogu.com.cn/paste/h5rvfget

本节代码

完成了!记录下一个小插曲,写代码的时候,写成了

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

然后开始报错0.0,查了一下发现第二行的代码必须写在最前面QAQ(不好好看教程是这样的)

左图:EBO+线框模式。啊,右图用了VAO+填充,怎么变这样了。哦懂了,就像教程前面说的那样,我的vertices没有相应的改变,改成这样就可以输出六个点,然后正常输出矩形。

在VAO还active的时候给EBO解绑,果然神魔都没有了

练习1-1: 添加更多顶点到数据中,使用glDrawArrays,尝试绘制两个彼此相连的三角形

我的小疑惑:在哪里设置线条的颜色啊。哦,我知道了,是在这里啊。大声的念出来,out的名字是啥?FragColor!!

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"
"}\0";

wwww于是又画了一个,放在右图了(什么嘛我的配色还是蛮有艺术细菌的)

练习1-2: 创建相同的两个三角形,但对它们的数据使用不同的VAO和VBO
练习1-3: 创建两个着色器程序,第二个程序使用一个不同的片段着色器,输出黄色;再次绘制这两个三角形,让其中一个输出为黄色

学到的知识:

// 同时生成两个可以这么写
glGenVertexArrays(2, VAOs);
glGenBuffers(2, VBOs);
// 同时清除两个可以这么写
glDeleteVertexArrays(2, VAOs);
glDeleteBuffers(2, VBOs);

标签:绑定,VAO,1.2,EBO,VBO,解绑,三角形,GL,HELLO
From: https://www.cnblogs.com/vivaldi370/p/Graphics_1_2.html

相关文章

  • 1.1 HELLO 窗口
    跟着教程,开始第一步创建窗口吧!这一节不涉及太多知识。本节会出现一些名词,我们现在只需要知道它们大概是干什么的就行。●GLFW:一个专门针对OpenGL的C语言库,通过它提供的接口,我们就可以渲染物体了;●GLAD:用来管理OpenGL函数指针的库,在调用所有OpenGL函数之前,我们要初始GLAD;●......
  • [爬虫]1.2.2 CSS选择器
    CSS(CascadingStyleSheets)是一种样式表语言,用于描述HTML元素的样式。CSS选择器是CSS规则的一部分,它决定了CSS规则应用于哪些元素。在网络爬虫的开发中,我们经常使用CSS选择器来定位和选取HTML元素。以下是一些常见的CSS选择器:1.元素选择器元素选择器选择所有给定的HTML元......
  • 1.2.3 计算机系统的层次结构
    计算机系统的层次结构下层是上层的基础,上层是下层的扩展三种级别的语言注:编译、汇编、解释程序,可统称“翻译程序”......
  • (Relax 数论1.26)POJ 1496 Word Index(计算一个字符串在字典中的位置)
    大致题意:(与POJ1850基本一致)输出某个str字符串在字典中的位置,由于字典是从a=1开始的,因此str的位置值就是在str前面所有字符串的个数+1规定输入的字符串必须是升序排列。不降序列是非法字符串要求用循环输入,输入若干组字符串,若输入非法字符串则输出0,但不结束程序,这是和POJ1850......
  • kubernets(k8s) 部署1.23.6版本
    kubernets(k8s)部署1.23.6版本环境准备环境介绍操作系统:CentOSLinuxrelease7.6.1810(Core)k8s组件版本:1.23.6docker版本:Dockerversion24.0.4,build3713ee12核CPU、2G内存、20G硬盘节点:(本实验采用虚拟机)k8s-master172.16.100.130    k8s-node1172.16.......
  • Node.js入门 - 永恒的Hello World!
    文章原作者为LeeJacobson,已经作者授权翻译用于非商业用途。介绍 这是我的关于Node.js系列入门教程的第一篇。必须说明一下,我并不是Node.js的专家,但是尝试向别人解释这是怎么回事是自我学习的一个好方法。如果你发现有些地方并不是那么正确,请提出来让我知道以便修正,谢之。 最近......
  • Ruby实践—HelloWorld
    开发环境OS:WindowsXPRuby:Ruby1.9.1Rails:Rails2.3.5IDE:RubyMine2.0.1 1、创建Rails工程 2、修改/config/database.yml自动创建的工程中默认数据库连接的是sqlite,如果没有安装此数据库,需要修改该配置(本例中使用的是mysql)#MysqlVersion5.1.46development:adapter:mys......
  • 1.2 分支结构 参考代码
    P5709[深基2.习6]ApplesPrologue/苹果和虫子#include<cstdio>#include<algorithm>usingnamespacestd;intmain(){intm,t,s,ans;scanf("%d%d%d",&m,&t,&s);if(t==0)ans=0;elseans=m-(s+t-1......
  • [爬虫]1.1.2 网络爬虫的工作原理
    网络爬虫(WebCrawler),也被称为网页蜘蛛(Spider),是一种用来自动浏览互联网的网络机器人。其主要目标通常是为搜索引擎创建复制的网页内容,但它们也可以被用于其他目的,比如数据挖掘。现在,我们一起来深入理解一下网络爬虫的工作原理。整个过程可以被大致分为四个步骤:发送HTTP请求从服......
  • Blazor前后端框架Known-V1.2.6
    V1.2.6Known是基于C#和Blazor开发的前后端分离快速开发框架,开箱即用,跨平台,一处代码,多处运行。Gitee:https://gitee.com/known/KnownGithub:https://github.com/known/Known概述基于C#和Blazor实现的快速开发框架,前后端分离,开箱即用。跨平台,单页应用,混合桌面应用,Web和桌面......