【OpenGL】二、三角形

流程分析

  1. VAO

    1. GLFW_OPENGL_CORE_PROFILE:必须设置VAO

  1. VBO(顶点缓冲对象)

    1. 生成
    2. 绑定
    3. 写入数据(GL_ARRAY_BUFFER
    4. 数据布局(glVertexAttribPointer

  1. IBO(元素缓冲对象)

    1. 生成
    2. 绑定
    3. 写入数据(GL_ELEMENT_ARRAY_BUFFER

  1. Shader

    1. 从外部读取
    2. 生成程序
    3. 生成和编译Shader
      1. glCreateShader
      2. glShaderSource
      3. glCompileShader
      4. 检测是否编译成功并返回message
    4. 将编译的着色器附加到程序对象上
    5. 链接一个program对象
    6. 验证程序对象
    7. 清除shader对象

  1. Render

    1. 清除缓冲
    2. 激活着色器
    3. 绑定VAO
    4. ebo 使用glDrawElements绘制

Vertex Array Object(VAO)

记住一点,Bind操作类似于PS的选中图层,Bind之后就可以对齐进行操作

Vertex Buffer Object(VBO)

简而言之,本质是一个内存字节数组,数据存入VBO(CPU执行),但最终的传入要在GPU VRAM上。显卡执行DrawCall时运行的代码就是Shader
创建三角形首先要设置一个顶点缓冲对象,存入数据。

简要分析

生成VBO

unsigned int VBO,
//可以一次生成多个缓冲对象
//第一个参数指定生成的个数,第二个参数为无符号整形
//由于该函数无返回值,我们需要传入一个指针,其会为该无符号整形赋值为Id。
 glGenBuffers(1, &VBO);

由于OpenGL是一个状态机工作,glGenBuffers分配了唯一标识符,它可以代表缓冲区,着色器,纹理等等。用该标识符去代表你所使用的对象。


//绑定缓冲区
glBindBuffer(GL_ARRAY_BUFFER, VBO);

一旦创建的VBO,就要绑定缓冲区。
下面是红宝书解释,但我还是喜欢用PS选择图层去理解
【红宝书】绑定对象的过程就像设置铁路的道岔开关,每一个缓冲类型(比如GL_ARRAY_BUFFER)中的各个缓冲对象(比如生成了多个缓冲对象,ID为:123,322,111)就像不同的轨道一样,我们将开关设置为其中一个的状态(比如绑定123为GL_ARRAY_BUFFER),那么之后的列车(针对GL_ARRAY_BUFFER的改动)都会驶入这条轨道(123缓冲对象)。


//指定数据
//第二个参数:分配大小
//第三个是参数:初始数据
//第四个参数:STREAM:修改一次只能使用几次STATIC:修改一次,能用很多次DYNAMIC:反复修改多次使用
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

解析顶点数据

//第一个参数:第一个属性
//第二个参数指定顶点属性的大小
//第三个参数指定数据的类型
//第四个参数:步长:顶点数据长度
//第五个数据:偏移:处理第某个数据时使用
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), (void*)0);
//启动顶点数据
//顶点属性位置值
glEnableVertexAttribArray(0);

简而言之就是向OpenGL解释如何使用顶点数据


Shader (着色器)

本质:GPU如何处理数据


代码

Application.cpp

#include <glad/glad.h>
#include <GLFW/glfw3.h>

#include <iostream>
#include<fstream>
#include<string>
#include<sstream>

//着色器结构体 (顶点/片元)
struct ShaderProgramSources
{
    std::string  VertexSource;
    std::string  FramentSource;
};

/// <summary>
/// 读取OpenShader并转成代码
/// </summary>
/// <param name="filepath"></param>
/// <returns></returns>
static ShaderProgramSources ParseShader(const std::string& filepath)
{
    std::ifstream stream(filepath);

    enum class ShaderType
    {
        NONE = -1,
        VERTEX = 0,
        FRSGMENT = 1,
    };

    std::string line;
    std::stringstream ss[2];
    ShaderType type = ShaderType::NONE;

    while (getline(stream, line))
    {
        if (line.find("#shader") != std::string::npos)
        {
            if (line.find("vertex") != std::string::npos)
                type = ShaderType::VERTEX;
            else if (line.find("fragment") != std::string::npos)
                type = ShaderType::FRSGMENT;
        }
        else
        {
            ss[(int)type] << line << '\n';
        }
    }

    return { ss[0].str(),ss[1].str() };
}



static unsigned int CompileShader(unsigned int type, const std::string& source)
{
    //1.创建一个着色器对象,返回ID
    unsigned int id = glCreateShader(type);
    //2.返回指向数据的指针
    const char* src = source.c_str();
    //3.着色器源码附加到着色器对象上;最后一个参数:这是一个指向包含字符串的每个相应元素的字符串长度的数组,如果length为NULL,则认为每个字符串都以null结尾,如果length不是NULL,则它指向包含字符串的每个相应元素的字符串长度的数组。
    glShaderSource(id, 1, &src, nullptr);
    glCompileShader(id);

    int result;
    glGetShaderiv(id, GL_COMPILE_STATUS, &result);
    if (result == GL_FALSE)
    {
        int length;
        glGetShaderiv(id, GL_INFO_LOG_LENGTH, &length);
        char* message = (char*)alloca(length * sizeof(char));
        glGetShaderInfoLog(id, length, &length, message);

        std::cout << "编译失败" << (type == GL_VERTEX_SHADER ? "Vertex" : "Fragment") << std::endl;

        std::cout << message << std::endl;

        //编译失败则释放内存
        glDeleteShader(id);
    }


    return id;
}


static int CreateShader(const std::string& vertexShader, const std::string& FragmentShader)
{
    //创建一个程序
    unsigned int program = glCreateProgram();
    unsigned int vs = CompileShader(GL_VERTEX_SHADER, vertexShader);
    unsigned int fs = CompileShader(GL_FRAGMENT_SHADER, FragmentShader);
    //将编译的着色器附加到程序对象上
    glAttachShader(program, vs);
    glAttachShader(program, fs);
    //用于连接(或链接)一个program对象。
    glLinkProgram(program);
    //用于验证程序对象是否可以在当前的OpenGL状态下执行。
    glValidateProgram(program);

    //可清除可不清除
    glDeleteShader(vs);
    glDeleteShader(fs);

    return program;
}


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;



int main()
{
    // glfw: initialize and configure
    // ------------------------------
    glfwInit();
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

#ifdef __APPLE__
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#endif

    // glfw window creation
    // --------------------
    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: load all OpenGL function pointers
    // ---------------------------------------
    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
    {
        std::cout << "Failed to initialize GLAD" << std::endl;
        return -1;
    }

    ShaderProgramSources shaderProgramSources = ParseShader("res/shaders/Basic.shader");

    std::cout << shaderProgramSources.VertexSource << std::endl;
    std::cout << shaderProgramSources.FramentSource << std::endl;

    unsigned int shaderProgram = CreateShader(shaderProgramSources.VertexSource, shaderProgramSources.FramentSource);
    glUseProgram(shaderProgram);


    // set up vertex data (and buffer(s)) and configure vertex attributes
    // ------------------------------------------------------------------
    float vertices[] = {
        0.5f, 0.5f, 0.0f,   // 右上角
        0.5f, -0.5f, 0.0f,  // 右下角
        -0.5f, -0.5f, 0.0f, // 左下角
        -0.5f, 0.5f, 0.0f   // 左上角
    };

    unsigned int indices[] = {
        // 注意索引从0开始! 
        // 此例的索引(0,1,2,3)就是顶点数组vertices的下标,
        // 这样可以由下标代表顶点组合成矩形

        0, 1, 3, // 第一个三角形
        1, 2, 3  // 第二个三角形
    };

    unsigned int VBO, VAO, EBO;
    glGenVertexArrays(1, &VAO);
    glGenBuffers(1, &VBO);
    glGenBuffers(1, &EBO);
    // bind the Vertex Array Object first, then bind and set vertex buffer(s), and then configure vertex attributes(s).
    glBindVertexArray(VAO);

    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    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);

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

    // note that this is allowed, the call to glVertexAttribPointer registered VBO as the vertex attribute's bound vertex buffer object so afterwards we can safely unbind
    glBindBuffer(GL_ARRAY_BUFFER, 0);

    // remember: do NOT unbind the EBO while a VAO is active as the bound element buffer object IS stored in the VAO; keep the EBO bound.
    //glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);

    // You can unbind the VAO afterwards so other VAO calls won't accidentally modify this VAO, but this rarely happens. Modifying other
    // VAOs requires a call to glBindVertexArray anyways so we generally don't unbind VAOs (nor VBOs) when it's not directly necessary.
    //glBindVertexArray(0);         此处不能解绑,不然就需要在Loop里重新绑定


    // uncomment this call to draw in wireframe polygons.
    //glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);

    // render loop
    // -----------
    while (!glfwWindowShouldClose(window))
    {
        // input
        // -----
        processInput(window);

        // render
        // ------
        glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);

        // draw our first triangle
        glUseProgram(shaderProgram);
        //glBindVertexArray(VAO);
        glDrawElements(GL_TRIANGLES, sizeof(indices) / sizeof(indices[0]), GL_UNSIGNED_INT, 0);
        // glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.)
        // -------------------------------------------------------------------------------
        glfwSwapBuffers(window);
        glfwPollEvents();
    }

    // optional: de-allocate all resources once they've outlived their purpose:
    // ------------------------------------------------------------------------
    glDeleteVertexArrays(1, &VAO);
    glDeleteBuffers(1, &VBO);
    glDeleteProgram(shaderProgram);

    // glfw: terminate, clearing all previously allocated GLFW resources.
    // ------------------------------------------------------------------
    glfwTerminate();
    return 0;
}

// process all input: query GLFW whether relevant keys are pressed/released this frame and react accordingly
// ---------------------------------------------------------------------------------------------------------
void processInput(GLFWwindow* window)
{
    if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
        glfwSetWindowShouldClose(window, true);
}

// glfw: whenever the window size changed (by OS or user resize) this callback function executes
// ---------------------------------------------------------------------------------------------
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
    // make sure the viewport matches the new window dimensions; note that width and 
    // height will be significantly larger than specified on retina displays.
    glViewport(0, 0, width, height);
}

Basic.shader

#shader vertex
#version 330 core

layout(location = 0) in vec3 aPos;
void main()
{
	gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);
};

#shader fragment
#version 330 core
layout(location = 0) out vec4 FragColor;
void main()
{
	FragColor = vec4(1.0f, 0.0f, 0.0f, 0.5f);
};

参考链接

TheCherno
LearnOpenGL
Zeehoy

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值