前言
之前使用使用opengl开发的时候,直接copy的开源代码修改后直接使用,很多代码逻辑和原理都没懂,后面有时间了系统学习了一下,做下记录,方便后续查阅
一、VBO&VAO
1.1 VBO
VBO(VertexBufferObject): 存储Mesh顶点属性数据。
存储方式:
SingleBuffer 存储方式:

InterleavedBuffer 存储方式:

1.2 VAO
VAO(VertexArrayObject): 存储一个Mesh所有属性描述。
主要解决的问题:
- 这个Mesh中,第N个点的位置属性数据怎么读取出来?
- 这个Mesh中,第N个点的颜色属性数据怎么读取出来?
- 这个Mesh中,第N个点的.….属性数据怎么读取出来?
方式: 为当前Mesh的每个属性,提供具体的描述信息
- 每个顶点xxx个数字
- 每个数字都是xxx类型
- 每个顶点的数据,步长为xxxbyte
- 此属性数据在顶点数据内的偏移量 offset
- 此属性存储在xxx号vbo
二、着色器Shader
Shader(着色器程序): 一种运行在GPU端的,类C语言,用于处理顶点数据以及决定像素片元最终着色。Shader对三角形数据的处理,分为顶点处理与片元处理,分别称为VertexShader与FragmentShader。
GLSL语言(Graphic Library Shader Language): 着色器是使用一种叫GLSL的类C语言写成的。GLSL是为图形计算量身定制的,它包含一些针对向量和矩阵操作的有用特性。特点如下:
- GLSL程序本质是一种将输入转化为输出的程序;
- GLSL程序是一种非常独立的程序,彼此之间无法通信;只能通过输入输出相互承接
2.1 顶点着色器VertexShader

layout (location = 0)比较难理解,这里的location = n,和glVertexAttribPointer函数的第一个参数对应;也就是说,VBO用来存放数据,可以是顶点位置、纹理、颜色等属性;而VAO是用来存放怎么从VBO里面读取数据的配置(例如:编号0存放的是位置读取配置,编号1是存放颜色读取配置等等)。
创建vao后,会使用glVertexAttribPointer将当前的顶点属性与顶点缓冲对象(VBO)关联起来。它的原型如下:
void glVertexAttribPointer (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid *pointer);
参数说明:
- index:指定要配置的顶点属性的编号,从0开始。例如,顶点位置、纹理、法线等属性可以分别编号为0、1、2。
- size:指定每个顶点属性的分量数(1、2、3或4),类似于向量的维度。例如,顶点位置通常有三个分量(x, y, z),所以size为3。
- type:指定每个分量的数据类型,可以是GL_BYTE、GL_UNSIGNED_BYTE、GL_SHORT、GL_UNSIGNED_SHORT、GL_INT、GL_UNSIGNED_INT、GL_FLOAT或GL_DOUBLE。
- normalized:指定是否将数据归一化到[0,1]或[-1,1]范围内。如果为GL_TRUE,数据将被归一化;如果为GL_FALSE,数据将保持原值。
- stride:指定连续两个顶点属性间的字节数。如果为0,则表示顶点属性是紧密排列的。
- pointer:指向缓冲对象中第一个顶点属性的第一个分量的地址(即偏移量)。
2.1 顶点着色器VertexShader

gl_Position 是glsl的内置变量,负责向后续阶段输出顶点位置处理的结果(NDC坐标);
2.2 片段着色器FragmentShader

三、Shader编译
glShaderSource 是 OpenGL 中的一个函数,用于将着色器源码加载到指定的着色器对象中。它是创建和编译着色器程序的重要步骤之一。函数原型:
void glShaderSource(GLuint shader, GLsizei count, const GLchar **string, const GLint *length);
参数说明:
- shader:指定要设置源码的着色器对象的句柄(由 glCreateShader 创建)。
- count:表示传入的字符串数组 string 的数量。
- string:一个指向字符串数组的指针,每个字符串都是着色器源码的一部分。
- length:一个整型数组,表示每个字符串的长度。如果为 NULL,则假定每个字符串以空字符 \0 结尾。
补充说明:实际使用过程中,可能是多个Shader搭配一起使用;例如:有A、B、C三个Shader,使用过程中,可能是A和C一起搭配使用,glShaderSource的count则需要传2,length则需要传数组,目前简单使用只会用到一个Shader。

1、 将vs与fs的源代码装入字符串
const char* vertexShaderSource =
"#version 460 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"
"}\n\0";
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"
"}\n\0";
2、创建 Shader 程序(vs、fs)
GLuint vertex, fragment;
vertex = glCreateShader(GL_VERTEX_SHADER);
fragment = glCreateShader(GL_FRAGMENT_SHADER);
3、为 shader 程序输入 shader 代码
glShaderSource(vertex, 1, &vertexShaderSource, NULL);
glShaderSource(fragment, 1, &fragmentShaderSource, NULL);
4、执行 shader 代码编译
glCompileShader (vertex);
int success = 0;
char infoLog[1024];//该字符串用来承接错误与否并输出
// 检查 vertex 编译结果,并把结果放在success里面
glGetShaderiv(vertex, GL_COMPILE_STATUS, &success);
if (!success) {
glGetShaderInfoLog(vertex, 1024, NULL, infoLog);
std::cout << "Error: SHADER COMPILE ERROR --VERTEX" << "\n" << infoLog << std::endl;
}
glCompileShader(fragment);
// 检查 fragment 编译结果,并把结果放在success里面
glGetShaderiv(fragment, GL_COMPILE_STATUS, &success);
if (!success) {
glGetShaderInfoLog(fragment, 1024, NULL, infoLog);
std::cout << "Error: SHADER COMPILE ERROR --FRAGMENT" << "\n" << infoLog << std::endl;
}
5、创建一个Program壳子
GLuint program = 0;
program = glCreateProgram();
6、将vs与fs编译好的结果放到program这个壳子里
glAttachShader(program, vertex);
glAttachShader(program, fragment);
7、执行program的链接操作,形成最终可执行shader程序
glLinkProgram(program);
//检查链接错误
glGetProgramiv(program,GL_LINK_STATUS, &success);
if (!success) {
glGetProgramInfoLog(program, 1024, NULL, infoLog);
std::cout << "Error: SHADER LINK ERROR " << "\n" << infoLog << std::endl;
}
8、shader链接完之后要清理,最终想拿到的是program,可以理解为将shader代码编译成一个exe文件,后续只需要使用该exe文件就行,也就是program
glDeleteShader(vertex);
glDeleteShader(fragment);

574

被折叠的 条评论
为什么被折叠?



