一.关键名词解释
VAO: Vertex Array Object, 顶点数组对象,你要绘制的图形。
VBO:Vertex Buffer Object, 顶点缓冲对象,所有顶点的集合。
EBO:Element Buffer Object, 元素缓冲对象,顶点的索引值。
IBO: Index Buffer Object, 索引缓冲对象。
管线:又称图像渲染管线,将原始的3D坐标转换为屏幕上有颜色的像素。分为两部分:
第一部分就是将3D坐标转成平面的2D坐标,第二部分将2D坐标转成屏幕有颜色的像素。
着色器:管线接受3D坐标,然后转化成有颜色的像素。期间的工作很复杂,GPU上有成千上万个小处理器核心,它们能够并行处理小程序,这些小程序可以是默认的,也可以是开发者自定义的,这些用来最终处理成2D有颜色的像素的程序,统称为着色器。
顶点着色器:可编程的着色器,前面所说的并行小程序的其中一种,它的作用是将用户输入的3D坐标空间位置(向量vec3)转成空间位置(向量vec4)。向量4中最后个参数w原文作者后续会说,这里先记着。
图元装配:告诉opengl你要绘制的图片是什么类型的,绘制指令需要指定你要绘制成什么样的形状,这里,常用的有:GL_POINTS(点), GL_TRIANGLES(三角形), GL_LINE_STRIPS(线)。
几何着色器:根据图元装配指定的类型,会生成新的顶点和新的三角形。(具体还未清楚具体作用)。
光栅化阶段:将图元映射到屏幕上真正的像素点,生成供片段着色器使用的片段。在使用片段着色器之前,裁切掉视图以外的像素点,提高效率。
片段着色器:可编程的着色器,主要是计算一个像素真正的颜色。通常片段着色器包含3D厂场景的数据(包含光照,阴影,光的颜色), 这些都被计算在内。
测试与混合:所有颜色确定之后,那就是透明度的问题对吧,当然,还会确认这个像素呈现的深度和模板值。深度原文作者理解的就是z轴的值,代表离你的距离,是代表在物体的前面还是后面,能理解吗?模板值,原文作者后续会讲,后面再整理。
总之,着色器渲染的过程:顶点输入(接受用户输入的3D坐标数组)==》顶点着色器==》图元装配==》几何着色器==》光栅化阶段==》片段着色器==》测试与混合
二.开始创建六边形编码
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";
// 片段着色器的代码
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";
// 构建顶点着色器
// 1.创建着色器对象
unsigned int vertexShader;
vertexShader = glCreateShader(GL_VERTEX_SHADER);
// 2.将着色器代码关联到对象上并编译
glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
glCompileShader(vertexShader);
// 3.检查编译是否成功
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;
}
// 构建片段着色器
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;
}
2.编译完成之后链接到程序对象
// 编译完成之后链接到程序对象
unsigned int shaderProgram;
shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram);
glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
// 判断是否链接程序成功
if(!success) {
glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog);
}
// 删除着色器对象,在把着色器对象链接到程序对象以后,
// 记得删除着色器对象,我们不再需要它们
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
3.构建顶点,创建VBO,VAO,EBO
// 顶点输入六边形
float vertices[] = {
-0.5f, 0.5f, 0.0f,
0.0f, 0.5f, 0.0f,
0.5f, 0.5f, 0.0f,
1.0f, 0.0f, 0.0f,
0.5f, -0.5f, 0.0f,
0.0f, -0.5f, 0.0f,
-0.5f, -0.5f, 0.0f,
-1.0f, 0.0f, 0.0f
};
unsigned int indices[] = {0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 0};
// 建立缓冲对象
unsigned int VBO;
unsigned int VAO;
unsigned int EBO;
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
glGenBuffers(1, &EBO);
glBindVertexArray(VAO);
// 绑定到缓冲buffer
glBindBuffer(GL_ARRAY_BUFFER, VBO);
// 刷入缓冲buffer
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
// 解析顶点数据
// 第一个参数: 0, 着色器的location = 0,对应这里的0
// 第二个参数: 3, 顶点数据是3个坐标构成,这里是3
// 第三个参数: 数据类型GL_FLOAT
// 第四个参数: 是否标准化,标准化就会映射到0~1之间
// 第五个参数步长: 表示每个顶点的所占空间的大小
// 第六个参数: 代表偏移,默认位置为0
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
// 启动顶点属性
glEnableVertexAttribArray(0);
// 解绑VAO
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
4.绘制使用VAO,释放资源
// 等待用户关闭窗口
while(!glfwWindowShouldClose(window))
{
processInput(window);
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glUseProgram(shaderProgram);
// 绘制物体
glBindVertexArray(VAO);
glDrawElements(GL_LINES, 16, GL_UNSIGNED_INT, 0);
// 双缓冲交换
glfwSwapBuffers(window);
// 响应各种交互事件
glfwPollEvents();
}
// 释放资源
glDeleteVertexArrays(1, &VAO);
glDeleteBuffers(1, &VBO);
glDeleteBuffers(1, &EBO);
glfwTerminate();
return 0;