• 作者:老汪软件技巧
  • 发表时间: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);