- 作者:老汪软件技巧
- 发表时间:2024-09-08 21:02
- 浏览量:
最新大型开源项目-云游戏,云桌面系统,欢迎关注
GammaRay源码地址
本项目代码仓库
点击这里
1. 现实中我们如何精确的绘制一个有颜色的三角形?2. OpenGL的工作流程
首先看一张图:
这也可以称为简洁版的OpenGL的渲染管线(Graphics PipeLine),但也完全可以说明问题。
从图中可以看出,OpenGL的也是接受顶点,组成形状,染上颜色,最终输出到屏幕上。
从头开始看:
总结来说,途中蓝色的方框代表的操作,都是可以更改的。其中顶点着色,片段着色是必须的。很容易发现,顶点着色器的运行次数,要远小于片段着色器,简单来看,三角形的顶点着色执行三次,而组成一个三角形的像素有成百上千,甚至百万个,而每个像素都要执行片段着色。
3. 着色器长什么样?
#version 330 core
layout (location = 0) in vec3 aPos;
void main()
{
gl_Position = vec4(aPos, 1.0);
}
那么可以理解一下这个着色器代表的意义:我给你的点在什么位置,就给我输出什么位置。
#version 330 core
void main()
{
gl_FragColor = vec4(1.0f, 0.5f, 0.8f, 1.0f);
}
gl_FragColor 是内置变量,保存的是RGBA,来决定这个片段/像素的颜色,那么这个着色器的意思就是:给我染成粉色
4 着色器怎么用?
像C/C++一样,编译,链接后,就可以使用了,如下代码:
ShaderPtr ShaderLoader::LoadShader(const std::string& v_gl, const std::string& g_gl, const std::string& f_gl) {
auto result = std::make_shared();
GLint compile_result = GL_FALSE;
int log_len;
// Create the shaders
GLuint vertex_shader_id = GLFunc glCreateShader(GL_VERTEX_SHADER);
GLuint fragment_shader_id = GLFunc glCreateShader(GL_FRAGMENT_SHADER);
GLuint geometry_shader_id = GLFunc glCreateShader(GL_GEOMETRY_SHADER);
// Compile Vertex Shader
char const *vertex_source = v_gl.c_str();
GLFunc glShaderSource(vertex_shader_id, 1, &vertex_source, NULL);
GLFunc glCompileShader(vertex_shader_id);
// Check Vertex Shader
GLFunc glGetShaderiv(vertex_shader_id, GL_COMPILE_STATUS, &compile_result);
GLFunc glGetShaderiv(vertex_shader_id, GL_INFO_LOG_LENGTH, &log_len);
if (log_len > 0) {
std::vector<char> err_msg(log_len + 1);
GLFunc glGetShaderInfoLog(vertex_shader_id, log_len, NULL, &err_msg[0]);
printf("%s\n", &err_msg[0]);
}
if (!g_gl.empty()) {
// Compile Geometry Shader
char const *geometry_source = g_gl.c_str();
GLFunc glShaderSource(geometry_shader_id, 1, &geometry_source, NULL);
GLFunc glCompileShader(geometry_shader_id);
// Check Vertex Shader
GLFunc glGetShaderiv(geometry_shader_id, GL_COMPILE_STATUS, &compile_result);
GLFunc glGetShaderiv(geometry_shader_id, GL_INFO_LOG_LENGTH, &log_len);
if (log_len > 0) {
std::vector<char> err_msg(log_len + 1);
GLFunc glGetShaderInfoLog(geometry_shader_id, log_len, NULL, &err_msg[0]);
printf("%s\n", &err_msg[0]);
}
}
// Compile Fragment Shader
char const *fragment_source = f_gl.c_str();
GLFunc glShaderSource(fragment_shader_id, 1, &fragment_source, NULL);
GLFunc glCompileShader(fragment_shader_id);
// Check Fragment Shader
GLFunc glGetShaderiv(fragment_shader_id, GL_COMPILE_STATUS, &compile_result);
GLFunc glGetShaderiv(fragment_shader_id, GL_INFO_LOG_LENGTH, &log_len);
if (log_len > 0) {
std::vector<char> err_msg(log_len + 1);
GLFunc glGetShaderInfoLog(fragment_shader_id, log_len, NULL, &err_msg[0]);
printf("%s\n", &err_msg[0]);
}
// Link the program
GLuint program_id = GLFunc glCreateProgram();
GLFunc glAttachShader(program_id, vertex_shader_id);
if (!g_gl.empty()) {
GLFunc glAttachShader(program_id, geometry_shader_id);
}
GLFunc glAttachShader(program_id, fragment_shader_id);
GLFunc glLinkProgram(program_id);
// Check the program
GLFunc glGetProgramiv(program_id, GL_LINK_STATUS, &compile_result);
GLFunc glGetProgramiv(program_id, GL_INFO_LOG_LENGTH, &log_len);
if (log_len > 0) {
std::vector<char> err_msg(log_len + 1);
GLFunc glGetProgramInfoLog(program_id, log_len, NULL, &err_msg[0]);
printf("%s\n", &err_msg[0]);
}
GLFunc glDetachShader(program_id, vertex_shader_id);
GLFunc glDetachShader(program_id, fragment_shader_id);
GLFunc glDetachShader(program_id, geometry_shader_id);
GLFunc glDeleteShader(vertex_shader_id);
GLFunc glDeleteShader(fragment_shader_id);
GLFunc glDeleteShader(geometry_shader_id);
result->program_id = program_id;
return result;
}
这部分代码就是一个工具,固定的流程,实现一个方法就好。
5. 给我们的三角形设定坐标
从图中可以看出,中心为原点,范围从是 [-1, 1],那么我们的三角形也可以轻松的定义:
float vertices[] = {
-0.5f, -0.5f, 0.0f,
0.5f, -0.5f, 0.0f,
0.0f, 0.5f, 0.0f
};
6.如何将我们的数据给OpenGL?
GLuint VAO;
glGenVertexArrays(1, &VAO);
....其他OpenGL操作
glBindVertexArray(0);
在实践中,我们绘制多个图形,模型都必须使用VAO,例如我做的这个场景:
GLuint VAO;
GLuint VBO;
glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO);
生成一个VBO
glGenBuffers(1, &VBO);
绑定这个VBO
glBindBuffer(GL_ARRAY_BUFFER, VBO);
将数据传递到这个VBO
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
这里的0,对应的是我们的 顶点着色器的 aPos 指定的location,启用它,并告诉OpenGL如何使用我们的数据。
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, false, 0, (void*)0);
glBindVertexArray(0);
这个函数参数较多,逐一解释。glVertexAttribPointer(0, // 对应 aPos 的location
3, // 表示这个属性有几个数据
GL_FLOAT, // 每个数据的类型
false, //是否要归一化,因为我们是在[-1,1]范围内定义的,所以不需要。
0, //跨度(STRIDE),指的是在内存中,到达下一个相同的属性,跨过的字节数。这里是0,代表整个数据都是相同的。后面的教程会修改这个值。
(void*)0);//(OFFSET)在每个跨度中的起始位置
参看这个图:
7. 绑定VAO,执行绘制操作
glUseProgram(shaderProgram);
glBindVertexArray(VAO);
glDrawArrays(GL_TRIANGLES, 0, 3);