这一节,我觉得是相当有难度的。渲染一个三角形,就需要介绍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);

1.2 HELLO 三角形的更多相关文章

  1. canvas快速绘制圆形、三角形、矩形、多边形

    想看前面整理的canvas常用API的同学可以点下面: canvas学习之API整理笔记(一) canvas学习之API整理笔记(二) 本系列文章涉及的所有代码都将上传至:项目代码github地址,喜 ...

  2. Android快乐贪吃蛇游戏实战项目开发教程-05虚拟方向键(四)四个三角形按钮

    该系列教程概述与目录:http://www.cnblogs.com/chengyujia/p/5787111.html 一.如何判断点击的是哪个方向键按钮 在上篇教程中我们实现了左边的三角形按钮效果, ...

  3. Android快乐贪吃蛇游戏实战项目开发教程-04虚拟方向键(三)三角形按钮效果

    该系列教程概述与目录:http://www.cnblogs.com/chengyujia/p/5787111.html 一.知识点讲解 当我们点击系统自带的按钮时,按钮的外观会发生变化.上篇博文中我们 ...

  4. Android快乐贪吃蛇游戏实战项目开发教程-03虚拟方向键(二)绘制一个三角形

    该系列教程概述与目录:http://www.cnblogs.com/chengyujia/p/5787111.html 一.绘制三角形 在上一篇文章中,我们已经新建了虚拟方向键的自定义控件Direct ...

  5. 酷酷的CSS3三角形运用

    概述 在早期的前端Web设计开发年代,完成一些页面元素时,我们必须要有专业的PS美工爸爸,由PS美工爸爸来切图,做一些圆角.阴影.锯齿或者一些小图标. 在CSS3出现后,借助一些具有魔力的CSS3属性 ...

  6. CSS制作三角形和按钮

    CSS制作三角形和按钮 用上一篇博文中关于边框样式的知识点,能制作出三角形和按钮. 我先说如何制作三角形吧,相信大家在平时逛网站的时候都会看到一些导航栏中的三角形吧,比如说: 网易首页的头部菜单栏中, ...

  7. Effective前端3:用CSS画一个三角形

    p { text-indent: 2em } .triangle-container p { text-indent: 0 } img { margin: 15px 0 } 三角形的场景很常见,打开一 ...

  8. 通过CSS的border绘制三角形

    通过css的border 可以绘制出三角形, 不同的样式组合,有着不同的效果,可以控制它的大小,颜色,方向.看下面各种图形,相信可能还有很多图形,大家都没见过. 先写出公共的样式: .border { ...

  9. 如何用CSS画三角形

    很多时候页面都需要一个或者多个小型三角形!多数人直接用PS扣个图片预览 下面用CSS简单画几个最终效果如下图 <div class="border-all-color"> ...

  10. [Modern OpenGL系列(三)]用OpenGL绘制一个三角形

    本文已同步发表在CSDN:http://blog.csdn.net/wenxin2011/article/details/51347008 在上一篇文章中已经介绍了OpenGL窗口的创建.本文接着说如 ...

随机推荐

  1. 浅谈Git架构和如何避免代码覆盖的事故

    浅谈Git架构和如何避免代码覆盖的事故 Git 不同于 SVN 的地方在于, Git 是分布式的版本管理系统, 所有的客户端和服务器都保存了一份代码, 涉及到仓库仓之间的同步, 所以处理不当极易造成冲 ...

  2. sftp文件上传下载方法

    随着信息化.数字化的发展,企业对数据安全及应用安全意识普遍加强,在数据文件传输过程中,一般建议使用sftp协议进行文件传输,sftp文件操作脚本如下: sftp操作主要有三种方式,分别是sftp客户端 ...

  3. RHCA rh442 003 系统资源 查看硬件 tuned调优

    监控工具 zabbix 监控具体业务,列如数据库.触发式事件(断网 硬盘坏一个) 普罗米修斯 给容器做监控 管理人员,如何知道几千台服务器哪些出了问题,这得需要zabbix 系统硬件资源 cpu [r ...

  4. Jmeter函数助手12-threadNum

    threadNum函数用于获取当前线程编号.该函数没有参数,直接引用即可. 1.线程数可在组件[测试计划->线程组]设置.如下是不传入循环次数的${__threadNum}. "调试取 ...

  5. 【Java】在树结构中给节点追加数据

    一.功能需求 有个树状组件,展示区域层级,每个区域节点需要展示该地区下的统计信息 从来没做过,给我整不会了属实是 二.功能分析 原型有功能和老系统代码,查看源码后发现的结构框架 1.树组件是自己用ul ...

  6. 【Vue】可编辑表格与三级联动下拉

    需求是给员工分配岗位,设计上是一人多岗的存在... 单位 -- 部门 -- 岗位 这样的层级 功能效果: 因为员工可以在不同的单位下任职岗位,所以这个每一个岗位都是一个独立 查询单位列表是固定的,但是 ...

  7. 【Vue】Vue-Cli 安装

    首先需要Node.js环境支持: Node.js官网下载: https://nodejs.org/en/ 右边稳定版,左边最新版 下载安装程序之后双击运行,无脑下一步 打开终端输入版本查看命令: no ...

  8. 【转载】 5:0!AI战胜人类教官,AlphaDogfight大赛落幕

    原文:https://baijiahao.baidu.com/s?id=1675621109599102721&wfr=spider&for=pc ------------------ ...

  9. 程序员学习网站推荐:路线向导(roadmap.sh)

    网站地址: https://roadmap.sh/ 在外网的技术论坛上看到这个网站,上面给出多种编程语言的学习路线,也就是给出不同编程语言的从易到难的组成内容(语言特性),通过这个网站可以辅助学习编程 ...

  10. 清除 Nuxt 状态缓存:clearNuxtState

    title: 清除 Nuxt 状态缓存:clearNuxtState date: 2024/8/7 updated: 2024/8/7 author: cmdragon excerpt: 摘要:本文介 ...