美文网首页
Self-learningOpenGL系列——你好,三角形

Self-learningOpenGL系列——你好,三角形

作者: 吃菜小怪兽 | 来源:发表于2019-07-17 15:51 被阅读0次
    • 顶点缓冲对象(VBO): Vertex Buffer Object
    • 顶点数组对象(VAO): Vertex Array Object
    • 索引缓冲对象(EBO、IBO): Element Buffer Object、Index Buffer Object

    图形渲染管线

    可以被划分为两个主要部分:

    1. 3D坐标转换为2D坐标
    2. 2D坐标转变为实际有颜色的像素

    可以被划分为几个阶段:

    1. 顶点着色器
    2. 形状(图元)装配
    3. 几何着色器
    4. 光栅化
    5. 片段着色器
    6. 测试与混合
    顶点着色器

    它把一个单独的顶点作为输入
    主要目的是把3D坐标转换为另一种3D坐标,同时对顶点属性进行一些基本处理

    图元装配

    将顶点着色器输出的所有顶点作为输入
    所有的点装配成指定图元形状

    几何着色器

    把图元形式的一系列顶点的集合作为输入
    可以通过产生新顶点构造出新的图元来生成其他形状

    光栅化、裁切

    几何着色器的输出作为输入
    把图元映射为最终屏幕上相应的像素,生成供片段着色器使用的片段
    在片段着色器运行之前会执行裁切,丢弃超出视图以外的所有像素,提升执行效率

    片段着色器

    光栅化生成的片段作为输入
    主要目的是计算一个像素的最终颜色

    测试与混合

    片段着色器的输出作为输入
    检测片段的对应的深度值,判断这个像素是其它物体的前面还是后面,决定是否应该丢弃;检查Alpha值并对物体进行混合

    数据输入

    定义一个float数组:

    //以标准化设备坐标形式
    float vertices[] = {
        -0.5f, -0.5f, 0.0f,
         0.5f, -0.5f, 0.0f,
         0.0f,  0.5f, 0.0f
    };
    

    通过VBO管理这个内存,代码会像这样:

    //0:生成顶点缓冲对象
    unsigned int VBO;
    glGenBuffers(1, &VBO);
    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    //1:复制数据到缓冲内存中
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
    

    着色器

    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";
    

    2)编译源码,代码会像这样:

    unsigned int vertexShader;
    vertexShader = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
    glCompileShader(vertexShader);
    

    GL_VERTEX_SHADER表示顶点着色器类型
    GL_FRAGMENT_SHADER表示片段着色器类型

    检测编译是否成功:

    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;
    }
    

    3)链接着色器程序,代码会像这样:

    unsigned int shaderProgram;
    shaderProgram = glCreateProgram();
    glAttachShader(shaderProgram, vertexShader);
    glAttachShader(shaderProgram, fragmentShader);
    glLinkProgram(shaderProgram);
    

    检测链接是否成功:

    int  success;
    char infoLog[512];
    glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
    
    if(!success)
    {
        glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog);
        std::cout << "ERROR::SHADER::VERTEX::LINK_FAILED\n" << infoLog << std::endl;
    }
    

    4)激活、使用程序
    glUseProgram(shaderProgram);

    把着色器对象链接到程序对象以后,记得删除着色器对象:

    glDeleteShader(vertexShader);
    glDeleteShader(fragmentShader);
    

    我们已经把输入顶点数据发送给了GPU,并指示了GPU如何在顶点和片段着色器中处理它。但是,OpenGL还不知道它该如何解释内存中的顶点数据,以及它该如何将顶点数据链接到顶点着色器的属性上。

    链接顶点属性

    使用glVertexAttribPointer函数解析顶点数据;
    使用glEnableVertexAttribArray启动顶点属性,默认是禁用的;

    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
    glEnableVertexAttribArray(0);
    

    顶点数组对象

    存储内容:

    • glEnableVertexAttribArrayglDisableVertexAttribArray的调用
    • 通过glVertexAttribPointer设置的顶点属性配置
    • 通过glVertexAttribPointer调用与顶点属性关联的顶点缓冲对象

    代码会像这样:

    // ..:: 初始化代码(只运行一次 (除非你的物体频繁改变)) :: ..
    // 1. 绑定VAO
    glBindVertexArray(VAO);
    // 2. 把顶点数组复制到缓冲中供OpenGL使用
    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
    // 3. 设置顶点属性指针
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
    glEnableVertexAttribArray(0);
    
    [...]
    
    // ..:: 绘制代码(渲染循环中) :: ..
    // 4. 绘制物体
    glUseProgram(shaderProgram);
    glBindVertexArray(VAO);
    someOpenGLFunctionThatDrawsOurTriangle();
    

    绘制三角形

    代码会像这样:

    glUseProgram(shaderProgram);
    glBindVertexArray(VAO);
    glDrawArrays(GL_TRIANGLES, 0, 3);
    

    完整程序

    #include <glad/glad.h>
    #include <GLFW/glfw3.h>
    
    #include <iostream>
    
    void framebuffer_size_callback(GLFWwindow* window, int width, int height);
    void processInput(GLFWwindow *window);
    
    // settings
    const unsigned int SCR_WIDTH = 800;
    const unsigned int SCR_HEIGHT = 600;
    
    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";
    
    int main()
    {
        // glfw: 初始化和配置
        // ------------------------------
        glfwInit();
        glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); // 主版本号
        glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); // 次版本号
        glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // 核心模式
        
    #ifdef ____
        glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // macOS中需要加上这行代码,配置才能生效
    #endif
        
        // glfw: 创建窗口对象
        // --------------------
        GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);
        if (window == NULL)
        {
            std::cout << "Failed to create GLFW window" << std::endl;
            glfwTerminate();
            return -1;
        }
        glfwMakeContextCurrent(window); // 将窗口的上下文设置为当前线程的主上下文
        glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
        
        // glad: 初始化,加载所有OpenGL函数指针
        // ---------------------------------------
        if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
        {
            std::cout << "Failed to initialize GLAD" << std::endl;
            return -1;
        }
        
        // 构建和编译着色器程序
        // ------------------------------------
        // 顶点着色器
        int vertexShader = glCreateShader(GL_VERTEX_SHADER);
        glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
        glCompileShader(vertexShader);
        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;
        }
        // 片段着色器
        int 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;
        }
        // 链接着色器
        int shaderProgram = glCreateProgram();
        glAttachShader(shaderProgram, vertexShader);
        glAttachShader(shaderProgram, fragmentShader);
        glLinkProgram(shaderProgram);
        glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
        if (!success) {
            glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog);
            std::cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog << std::endl;
        }
        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(1, &VAO);
        glGenBuffers(1, &VBO);
        // 首先绑定顶点数组对象,然后绑定和设置顶点缓冲区,然后配置顶点属性。
        glBindVertexArray(VAO);
        
        glBindBuffer(GL_ARRAY_BUFFER, VBO);
        glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
        
        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
        glEnableVertexAttribArray(0);
        
        //解绑
        glBindBuffer(GL_ARRAY_BUFFER, 0);
        glBindVertexArray(0);
        
        // 渲染循环
        // -----------
        while (!glfwWindowShouldClose(window))
        {
            // 输入
            // -----
            processInput(window);
            
            // 渲染
            // ------
            glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
            glClear(GL_COLOR_BUFFER_BIT);
            
            // 画出第一个三角形
            glUseProgram(shaderProgram);
            glBindVertexArray(VAO);
            glDrawArrays(GL_TRIANGLES, 0, 3);
            
            // 交换缓冲区和轮询IO事件(按下/释放键,移动鼠标等)
            // -------------------------------------------------------------------------------
            glfwSwapBuffers(window);
            glfwPollEvents();
        }
        
        // 正确释放/删除之前的分配的所有资源
        // ------------------------------------------------------------------
        glfwTerminate();
        return 0;
    }
    
    // 处理所有输入:查询GLFW是否按下/释放了此帧的相关键,并做出相应的反应
    // ---------------------------------------------------------------------------------------------------------
    void processInput(GLFWwindow *window)
    {
        if(glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
            glfwSetWindowShouldClose(window, true);
    }
    
    // 在每次窗口大小被调整的时候被调用
    // ---------------------------------------------------------------------------------------------
    void framebuffer_size_callback(GLFWwindow* window, int width, int height)
    {
        glViewport(0, 0, width, height);
    }
    
    屏幕快照

    索引缓冲对象

    和顶点缓冲对象一样,EBO也是一个缓冲,它专门储存索引
    代码会像这样:

    // ..:: 初始化代码 :: ..
    // 1. 绑定顶点数组对象
    glBindVertexArray(VAO);
    // 2. 把我们的顶点数组复制到一个顶点缓冲中,供OpenGL使用
    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
    // 3. 复制我们的索引数组到一个索引缓冲中,供OpenGL使用
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
    // 4. 设定顶点属性指针
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
    glEnableVertexAttribArray(0);
    
    [...]
    
    // ..:: 绘制代码(渲染循环中) :: ..
    glUseProgram(shaderProgram);
    glBindVertexArray(VAO);
    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0)
    glBindVertexArray(0);
    
    屏幕快照

    GitHub

    Self-learningOpenGL

    相关文章

      网友评论

          本文标题:Self-learningOpenGL系列——你好,三角形

          本文链接:https://www.haomeiwen.com/subject/wnzqlctx.html