OpenGL学习笔记(二)

OpenGL初级篇(二)

图形渲染管线可分为两个主要部分:
第一部分把3D坐标转换为2D坐标
第二部分把2D坐标转变为实际的有颜色的像素(坐标精确表示一个点在2D空间中的位置,像素是这个点的近似值,受屏幕/窗口分辨率的限制)
OpenGL着色器是用OpenGL着色器语言(OpenGL Shading Lanuage,GLSL)

绘制三角形

OpenGL的基本图元:点(Point)、线(Line)、面(Traingle)
OpenGL是一个3D图形库,故在OpenGL指定的坐标都是3D坐标。顶点输入把3D坐标变换为屏幕的2D像素,仅当3D坐标的x、y、z三轴上都为**-1.01.0** 的范围内时才处理它,所有的标准化设备坐标(Normalized Device Coordinates,NDC) 才会最终呈现在屏幕上。由于要渲染一个2D三角形,需将顶点的z坐标设置为0.0。
在这里插入图片描述
通常深度可理解为z坐标,它代表一个像素在空间中与相机的距离,如果离相机远就可能被别的像素遮挡,会被丢弃,以节省资源
在这里插入图片描述
以创建三角形为例,屏幕的原点在中间,x轴朝右,y轴朝上,屏幕坐标如下图:
在这里插入图片描述

OpenGL缓存数据

顶点缓冲对象 (Vertex Buffer Object,VBO)
是在显卡存储空间中开辟出的一块内存缓存区,用于存储顶点的各类属性信息,如顶点坐标,顶点法线,顶点颜色数据等。
顶点着色器渲染时,可直接从VBO中取出顶点的各类属性数据,由于VBO在显存而不是在内存中,不需要从CPU传输数据,处理效率更高。所以可以理解为VBO就是显存中的一个存储区域,可以保持大量的顶点属性信息。并且可以开辟很多个VBO,每个VBO在OpenGL中有它的唯一标识ID,这个ID对应着具体的VBO的显存地址,通过这个ID可以对特定的VBO内的数据进行存取操作。
顶点缓冲对象的缓冲类型是GL_ARRAY_BUFFER,允许同时绑定个缓冲,只要它们是不同的缓冲类型
可使用glBindBuffer函数把新创建的缓冲绑定到GL_ARRAY_BUFFER目标上。

使用VBO画图
在这里插入图片描述
顶点数组对象 (Vertex Array Object,VAO)
是一个保存了所有顶点数据属性的状态结合,它存储了顶点数据的格式以及顶点数据所需的VBO对象的引用。
VAO本身并没有存储顶点的相关属性数据,这些信息是存储在VBO中的,VAO相当于是对很多个VBO的引用,把一些VBO组合在一起作为一个对象统一管理。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

顶点着色器(vertex shader)

一个简单的顶点着色器(只包含位置数据)

// 版本:OpenGL 3.3, 核心模式
#version 330 core
layout (location = 0) in vec3 aPos;
void main()
{
gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);
}

创建和编译着色器步骤:

  1. 将着色器源码存放到字符串变量中(一般存放在独立文件读取,这里为了简单采用这种方法)

const char* vertexShaderSource = “#version 330 core\n”
“layout (location = 0) in vec3 aPos;\n”
“void main()\n”
“{\n”
" gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n"
“}\0”;

  1. 创建着色器对象

unsigned int vertexShader;
vertexShader = glCreateShader(GL_VERTEX_SHADER);

  1. 将源码附加到着色器对象并编译

glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
glCompileShader(vertexShader);

  1. 错误检查

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;
}

片元着色器(fragment shader)

一个简单的片元着色器程序:

#version 330 core
out vec4 FragColor;
void main()
{
FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);
}

片元着色器的创建和编译过程与顶点着色器类似:

const char* fragmentShaderSource = “#version 330 core\n”
“out vec4 FragColor;\n”
“void main()\n”
“{”
" FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);\n"
“}\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;
}

一个**着色器程序对象(shader program object)**就是多个着色器结合链接到一起的最终版本。激活的着色器程序对象将在调用渲染操作时使用。创建着色器程序对象和连接着色器如下代码所示:

最终效果

在这里插入图片描述

元素缓冲区对象

元素缓冲区对象(element buffer object, EBO):是一个类似顶点缓冲区对象的缓冲区,存储OpenGL用于决定绘制那个顶点的索引。这也叫做索引绘制(indexed drawing)
元素缓冲区对象的创建和绑定

// 顶点数据
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, 1, 3, // 第一个三角形
1, 2, 3 // 第二个三角形
};
// 1. 创建元素缓冲区对象
unsigned int EBO;
glGenBuffers(1, &EBO);
// 2. 绑定元素缓冲区对象
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
// 3. 拷贝索引数组到元素还钱给OpenGL使用
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);

使用元素缓冲区对象绘制图形(在渲染循环中):

// 设置线框模式(wireframe mode)
//glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
// 绘制元素
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);

最终效果

在这里插入图片描述
设置线框模式:

glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);

最终效果

在这里插入图片描述

个人小结

着色器的编译
对于每个着色器对象:
1)创建一个着色器对象:
2)将着色器源代码编译为对象
3)验证着色器编译是否成功
在这里插入图片描述

OpenGL绘图的基本流程
在这里插入图片描述
OpenGL对象
在这里插入图片描述
OpenGL对象中VBO、VAO和EBO之间的关系
在这里插入图片描述
顶点缓冲区对象(VBO):管理GPU上的一块用于存放顶点数据的显存。用于存放进入OpenGL渲染管道的顶点数据。
顶点数组对象(VAO):存放顶点属性信息,需与对应的VBO关联。用于告诉OpenGL,VBO上的顶点数据应该如何解释(如每个顶点数据的长度,间距等)。内部同时保存一个EBO对象,因此绑定VAO会自动绑定对应的EBO。
元素缓冲区对象(EBO):类似于VBO,但是存放的是索引数据。

本文主要参考LearnOpenGL

猜你喜欢

转载自blog.csdn.net/weixin_42050609/article/details/125123998