写在前面
从这节开始,会接触到很多基本概念,原书我也是读了很多遍,一遍一遍去理解其中的意思,以及他们之间的关系。
概念
顶点数组对象:VAO
顶点缓冲对象:VBO
索引缓冲对象:EBO|IBO
OpenGL是一个3D空间,而屏幕和窗口是2D的,所以OpenGL的大部分工作是将3D坐标转换为2D像素
转换的过程就是由图形渲染管线管理的。
图像渲染管线分为两部分:1.把3D坐标转换为2D坐标。2.把2D坐标转变为有颜色的像素
渲染的几个阶段:
顶点数据->顶点着色器->图元装配->几何着色器->光栅化->片段着色器->测试与混合
渲染的每一个阶段都有自己的程序,该程序是使用OpenGL着色器语言进行编写的,这些小程序叫做着色器(shader)
顶点着色器:把3D坐标转换为另一种3d坐标,同时对顶点属性进行一些基本处理
图元装配:将所有的点装配成指定的图元形状
几何着色器:通过产生新的点和新的图元来生成形状
光栅化:将图元映射为最终屏幕上相应的像素,生成片段着色器使用的片段,并裁切吊超过视图以外的像素
片段着色器:计算一个像素的最终颜色
测试与混合:检测片段对应的深度值,用来判断这个像素是其它物体的前面还是后面,决定是否丢弃
代码创建
首先,我们声明出来下面需要使用的着色器源码。
通过上述概念我们知道着色器,是运行在图像渲染管线上的小程序,代码风格类似c语言
// 顶点着色器源码
const char *vertexShaderSource =
"#version 330 core\n"
"layout (location = 0) in vec3 aPos;\n"
"void main(){\n"
"gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0f);\n"
"}\n\0"; //片段着色器源码
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"
"}\n\0";
下边我们使用上述两个着色器,构建一个可以渲染的渲染管线
//创建顶点着色器
int vertexShader = glCreateShader(GL_VERTEX_SHADER);
// 第一个参数着色器对象,第二个参数传递的源码字符串,第三个参数顶点着色器的源码
glShaderSource(vertexShader, , &vertexShaderSource, NULL);
glCompileShader(vertexShader);
// 判断编译是否成功
int sucess;
char infoLog[];
glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &sucess);
if(!sucess){
glGetShaderInfoLog(vertexShader, , NULL, infoLog);
std::cout<< "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n"<< infoLog << std::endl;
}
// 片段着色器,创建并编译
int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, , &fragmentShaderSource, NULL);
glCompileShader(fragmentShader);
glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, & sucess);
if(!sucess){
glGetShaderInfoLog(fragmentShader, , NULL, infoLog);
std::cout<<"ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n"<< infoLog<<std::endl;
}
两个着色器创建,且编译之后,需要进行连接
//链接两个着色器对象到用来渲染的着色器程序中
// 创建着色器程序
int shaderProgram = glCreateProgram();
//链接
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram);
glGetShaderiv(shaderProgram, GL_COMPILE_STATUS, & sucess);
if(!sucess){
glGetShaderInfoLog(shaderProgram, , NULL, infoLog);
std::cout<<"ERROR::SHADER::PROGRAM::COMPILATION_FAILED\n"<< infoLog<<std::endl;
}
链接两个着色器到我们刚创建的着色器程序中,且进行了编译,这个时候,我们上面的两个着色器,已经在shaderProgram中了。
//删除无用的顶点着色器以及片段着色器
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
我们知道,渲染的几个阶段,那么现在我们只需要创建顶点数据,然后使用上述着色器程序进行渲染
// 定义三个顶点
float vertices[] = {
-0.5f, -0.5f, 0.0f,
0.5f, -0.5f, 0.0f,
0.0f, 0.5f, 0.0f
};
对于顶点的管理,我们使用顶点缓冲对象以及顶点数组对象进行管理。文章开头已经讲解了基本的概念
// 使用顶点缓冲对象进行缓存
unsigned int VBO, VAO;
glGenVertexArrays(, &VAO);
glGenBuffers(, &VBO);
// 缓冲对象进行绑定
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
缓冲对象的使用
// 将定义的顶点数据复制到缓冲内存中
// 第一个参数,缓冲类型,第二个参数数据大小,第三个参数发送的数据,第四个参数,显卡如何管理给定的数据
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); // 解析顶点数据
glVertexAttribPointer(, , GL_FLOAT, GL_FALSE, * sizeof(float), (void*));
// 启动顶点数据
glEnableVertexAttribArray(); glBindBuffer(GL_ARRAY_BUFFER, );
glBindVertexArray(); glDrawArrays(GL_TRIANGLES,,);
现在我们的顶点数组对象已经在内存中了
下面就是调用着色器程序进行启动
//启用三角形
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
// 激活程序
glUseProgram(shaderProgram);
glBindVertexArray(VAO);
glDrawArrays(GL_TRIANGLES, , );
glfwSwapBuffers(window);
最后,不要忘了清楚我们使用的vao以及vbo对象
//清除对象
glDeleteVertexArrays(, &VAO);
glDeleteBuffers(, &VBO);
总结
通过上述的流程,应该是可以跑起来一个三角形的。OpenGL之路漫漫长,我们继续加油