天天看點

OPENGL學習——繪制平面三角形

OPENGL是一個底層的圖形庫,可以跨平台,我們這裡學習的是他的可程式設計渲染管線。我會對完整代碼的每一句做講解。請仔細讀一讀。

推薦一個學習網站https://learnopengl-cn.github.io/

一、建立一個視窗

完整代碼

說明:這一部分主要作為了解,建立視窗的過程是固定的,沒有什麼知識可言。

#include <glad/glad.h>
#include <GLFW/glfw3.h>
//導入的兩個OPENGL庫,注意這倆是有順序的
#include <iostream>

void framebuffer_size_callback(GLFWwindow* window, int width, int height);。
void processInput(GLFWwindow *window);
//這兩個函數是回調函數,第一個是調節視窗大小的,第二個是退出視窗的。


int main()
{

    glfwInit();//初始化GLFW
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);//主版本号
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);//附版本号
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    //設定為可程式設計渲染模式

    GLFWwindow* window = glfwCreateWindow(800, 600, "LearnOpenGL", NULL, NULL);
    //建立一個GLFW視窗,前邊兩個是長寬,第三個是視窗标題,後邊兩個NULL不用管,寫NULL就行。
    if (window == NULL)//如果視窗建立失敗了
    {
        std::cout << "Failed to create GLFW window" << std::endl;
        glfwTerminate();//銷毀視窗
        return -1;//退出main
    }
    glfwMakeContextCurrent(window);//這個也是必須寫的,沒有為啥
    glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
//設定這個回調

    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
    {
    //這個glad是給OPENGL做函數指針用的,它也需要建立,如果建立失敗了就退出,和上邊那個GLFW的一樣。
        std::cout << "Failed to initialize GLAD" << std::endl;
        return -1;
    }    

    while (!glfwWindowShouldClose(window))
    {
   //這塊叫做渲染循環,因為你要繪制的東西肯定是動态的,咱這是3D,是以東西必須得是循環繪制的,很好了解。

        processInput(window);//這個自己看去

        glfwSwapBuffers(window);//這個是繪制,告訴OPENGL需要繪制。
        glfwPollEvents();//檢測觸發事件,我需要檢測是否觸發事件。在這裡是按ESC退出,實作這個在processInput裡邊。
    }


    glfwTerminate();//銷毀視窗,上邊出現過。
    return 0;
}


void processInput(GLFWwindow *window)
{
    if(glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)//如果點選ESC
        glfwSetWindowShouldClose(window, true);//正常退出
}

void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{

    glViewport(0, 0, width, height);
    //讓視口,也就是渲染區域大小跟着視窗大小變,比如我全屏了,那麼渲染的區域也得是全屏。
}
           

二、建立三角形

基本概念的認識:

1.圖元:組成圖形的最小機關,我們這裡是三角形,這節課畫的也是三角形。這個怎麼了解呢,你去找一個OBJ模型,然後放大了看,你會發現這個OBJ的3D模型是由一個一個的三角形組成的(有的是四邊形),這個三角形(或是四邊形)就叫圖元。

2.光栅化:我們的顯示屏叫光栅顯示屏,也就是圖形顯示都是用像素塊顯示的,這個大家都知道。把圖形轉化成像素塊的過程就叫光栅化。

3.着色器(Shader):運作在GPU上的程式,OPENGL裡分為兩個,頂點着色器和片段着色器,前者是繪制頂點的,你看不到,後者是讓GPU往你電腦屏上顯示東西的,點連成面。還有一個着色器叫幾何着色器,我們不咋用。當然也可以做計算,看這個程式你怎麼寫了。

4.渲染:就是顯示東西的意思。

5.VBO,EBO,VAO:VBO叫頂點緩沖對象,VAO叫頂點數組對象,EBO叫索引緩沖對象。啥意思呢,就是說VBO是存頂點的,EBO是存頂點繪制順序的,最後這倆都要放進VAO裡,友善管理。這個要在程式裡體會。

完整代碼

#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <iostream>
void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void processInput(GLFWwindow *window);
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"//要從0号通道接收資料,定義的頂點資料。vec3是個向量。
    "void main()\n"
    "{\n"
    "   gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n"
    //這個就是告訴着色器是頂點了gl_Position是自帶的一個變量。
    "}\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"
    //繪圖了,FragColor 也是自帶的,前邊是RGB值。
    "}\n\0";

int main()
{

    glfwInit();
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

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

    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
    {
        std::cout << "Failed to initialize GLAD" << std::endl;
        return -1;
    }


    //建立shader
    unsigned 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;
    }
    // fragment shader//和上邊一樣
    unsigned int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
    glCompileShader(fragmentShader);
    // check for shader compile errors
    glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
    if (!success)
    {
        glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);
        std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << std::endl;
    }
    // 連接配接shaders
    unsigned 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;//建立VAO,VBO對象
    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);//1、0号location錄入資料2、三個資料3、浮點類型4、不轉化為标準空間,一般都是FALSE 5、步長 6、位置在數組的開頭,是以是0
    glEnableVertexAttribArray(0);//這個也是說0号location


    glBindBuffer(GL_ARRAY_BUFFER, 0); //綁定VAO
    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);//畫三角形

        glfwSwapBuffers(window);
        glfwPollEvents();
    }

    glDeleteVertexArrays(1, &VAO);
    glDeleteBuffers(1, &VBO);
    glDeleteProgram(shaderProgram);


    glfwTerminate();
    return 0;
}


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

繼續閱讀