OpenGL:三角形的诞生

先放一个图形渲染管线的每个阶段的抽象展示。要注意蓝色部分代表的是我们可以注入自定义的着色器的部分:

顶点缓冲对象(VBO)#

所有的顶点数据(位置、纹理、法线等)都需要存储在GPU上,OpenGL通过顶点缓冲对象管理这个内存。其声明和绑定的代码函数如下:

unsigned int vbo;
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, 6 * sizeof(float), positions, GL_STATIC_DRAW);

设置顶点属性#

在绑定VBO时,我们只是指定了一堆数据,但是对于其内存布局(layout)完全未知,为了让机器知道顶点属性,我们还需如下代码(这两行代码的顺序无所谓):

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

每个索引都可以绑定一种属性,比如上面的例子就是将每2个连续的float作为一组属性绑定在索引0上,这里的索引0在后面的顶点着色器中也会使用到。
代码中并没有指明这个属性具体是什么属性,比如位置还是法线等,它只是一组属性并且被绑定在了索引上。

你可能会觉得很奇怪,没有指明哪个内存空间就直接设置属性。这也是OpenGL状态机的体现,只要在GL_ARRAY_BUFFER上绑定的VBO,那么这个属性就对应的是这个VBO。

着色器(shader)#

着色器简单来说就是运行在显卡上的一段小程序,我们可以通过编程控制器输入输出进而控制图像的显示。OpenGL着色器是用OpenGL着色器语言(OpenGL Shading Language, GLSL)写成的,它是一种类C语言。

就目前而言,我们最需要了解的着色器就两个:顶点着色器和片段着色器。简单来说,我们首先在CPU上定义了顶点数据,之后将其转移到显存上(通过VBO),之后需要通过顶点着色器(一段小程序,对每个顶点都运行一次)处理顶点数据,并将必要的数据输出,再之后就是片段着色器(计算颜色等,对每个光栅块处理一次)进行处理,最后通过混合显示图像。

着色器间不能相互通信,它们之间唯一的沟通只有通过输入和输出。GLSL定义了in和out关键字专门来实现这个目的。每个着色器使用这两个关键字设定输入和输出,只要一个输出变量与下一个着色器阶段的输入匹配,它们的值就能够传递。

着色器代码如下:

#shader vertex
#version 330 core

layout(location = 0) in vec2 position;
layout(location = 1) in vec3 color;

out vec3 ourColor;

void main() {
	gl_Position = vec4(position, 0.0, 1.0);
	ourColor = color;
}

#shader fragment
#version 330 core

layout(location = 0) out vec4 color;

in vec3 ourColor;

void main() {
	color = ourColor;
}

在编译着色器时,将其转换为字符串进行编译,先假设其分别转换为std::string对象为vertexShaderfragmentShader
可以调用以下函数进行编译,类似于其他OpenGL对象,他们也需要一个id:

unsigned int id = glCreateShader(GL_VERTEX_SHADER);  // or "GL_FRAGMENT_SHADER" for fragmentShader
glShaderSource(id, 1, &vertexShader, nullptr);  // or fragmentShader
glCompileShader(id);

在编译完成后它们需要链接至程序中,代码如下:

unsigned int program = glCreateProgram();
glAttachShader(program, id);
glLinkProgram(program);

需要注意的是,在链接至程序中后即可删除编译的shader:

glDeleteShader(id);

之后可以通过以下函数选择是否启用此段程序:

glUseProgram(program);  // set 0 to unused;

索引缓冲对象(元素缓冲对象,IBO)#

当我们需要用两个三角形构建正方形时,是否需要六个节点信息呢(每个三角形三个)?当然可以,但这会浪费现存,毕竟四边形一共四个顶点,有50%的定点信息时重复的。OpenGL提供了索引缓冲对象(IBO)解决这钟需要重复使用同一个节点信息的情况。

glDrawElements函数从当前绑定到GL_ELEMENT_ARRAY_BUFFER目标的IBO中获取其索引。这意味着我们每次想要使用索引渲染对象时都必须绑定相应的IBO,这又有点麻烦。碰巧顶点数组对象也跟踪元素缓冲区对象绑定。在绑定VAO时,绑定的最后一个元素缓冲区对象存储为VAO的元素缓冲区对象。然后,绑定到VAO也会自动绑定该IBO。

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

其设置代码与VBO类似,如下所示:

unsigned int ibo;
glGenBuffers(1, &ibo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, 3 * 2 * sizeof(unsigned int), index, GL_STATIC_DRAW);

顶点数组对象(VAO)#

我们可以将一组对应的VBO及其顶点属性共同绑定在一个VAO上,那么在每次绘制时,只需要绑定给定的VAO就能够绘制对应的顶点和属性了。这样做的好处就在于无需在绘制时额外设置一系列的layout和属性等,直接利用VAO的解绑和绑定就能够绘制图案。

值得注意的是,VAO是必须的。在OpenGL核心模式下需要自己创建VAO才能正常绑定VBO等。这也意味着下面这段代码需要在上面所示代码的最前面:

unsigned int vao;
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);

在使用时可以通过glBindVertexArray(vao)指定特定的顶点数组对象,值得注意的时,它虽然绑定了对应的VBO和属性,但是并没有绑定shader等。在开启VAO时记得开启shader。

作者:Warren-j1an

出处:https://www.cnblogs.com/warren-j1an/p/18225096

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

posted @   warren-j1an  阅读(8)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· DeepSeek 开源周回顾「GitHub 热点速览」
more_horiz
keyboard_arrow_up light_mode palette
选择主题
menu
点击右上角即可分享
微信分享提示