1. 程式人生 > >小白的OpenGL3.3自學之路(2)OpenGL3.3之開啟一個視窗

小白的OpenGL3.3自學之路(2)OpenGL3.3之開啟一個視窗

還記得上一篇那段測試程式碼嗎?

那段程式碼就是最簡單的應用glfw開啟一個視窗,程式碼如下:

#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; 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; } while (!glfwWindowShouldClose(window)) { processInput(window); glfwPollEvents(); glfwSwapBuffers(window); } 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); }

下面本小白開始對這段程式碼進行簡單的瞭解:

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

這裡是標頭檔案的包含。要切記先包含GLAD的標頭檔案,再包含其他依賴於OpenGL的標頭檔案。

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;

framebuffer_size_callback函式是對視窗註冊一個回撥函式,在視窗大小被調整時呼叫。 processInput函式是在GLFW中實現一些輸入控制。 剩下的就是初始化視窗的寬度為800,高度為60。

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

glfwInit();->初始化GLFW glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR,3);->OpenGL主版本號(Major)為3 glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR,3);->OpenGL次版本號(Minor)為3 glfwWindowHint(GLFW_OPENGL_PROFILE,GLFW_OPENGLCORE_PROFILE);->OpenGL使用核心模式 以上告訴了我們要使用的OpenGL版本是3.3,並且是使用核心模式,也就是可程式設計管線模式。

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

第一句建立一個視窗物件,用來存放和視窗相關的資料,比如長,寬,視窗標題。 glfwMakeContextCurrent(window);在使用OpenGL API之前,要設定視窗物件作為當前的上下文。這個上下文將會一直被保持,直到你設定了另一個上下文或者擁有當前上下文的視窗被銷燬。 那麼問題來了。究竟什麼是上下文,為什麼每次渲染之前都要先設定上下文。能不能不要上下文?

OpenGL狀態機

要解決這個問題,需要知道為什麼說OpenGL是一個狀態機。 那什麼是狀態機呢?狀態機描述了一個物件在其生命週期內所經歷的各種狀態,狀態的改變,為什麼發生改變等。這麼說還是雲裡霧裡。那直接說狀態機的特點吧。 1.有記憶功能,能記住其當前的狀態 2.可以接收輸入,根據輸入的內容和自己的原先狀態,修改自己當前狀態,並且可以有對應輸出 3.當進入特殊狀態的時候,便不再接收輸入,停止工作。 那麼類比到OpenGL有如下理解: 1.OpenGL可以記錄自己的狀態。比如設定圖元型別為三角形,顏色分量,紋理座標等。 2.OpenGL可以通過呼叫函式來改變自己的狀態。 3.OpenGL中當glfwWindowShouldClose(window)返回值為true時,就不再繼續渲染,直接退出。 那麼理解了狀態機之後,這些狀態機裡面儲存的跟渲染有關的狀態,就是上下文,那麼很自然地想到我們要在渲染之前先設定上下文,這個上下文記錄了OpenGL渲染所需要的所有資訊,可以類比C語言裡面的結構體,這個結構體裡面儲存了當前繪製所使用的顏色,是否有光照計算,是否開啟光源等狀態屬性,從底層的角度看,上下文就是在實際渲染前,驅動為暫存器所做的配置,有了上下文,驅動下達命令,GPU管線根據之前記錄的狀態來處理資料。 那麼返回上面的程式碼:glfwMakeContextCurrent(window);這句話的意思是把之前建立的視窗設定為當前執行緒的上下文。那麼以後更改狀態後比如顏色等,也就會第一時間顯示到視窗。

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

GLAD是用來管理OpenGL的函式指標的,所以在呼叫任何OpenGL的函式之前我們需要初始化GLAD。

while (!glfwWindowShouldClose(window))
    {
        processInput(window);
        glfwPollEvents();
        glfwSwapBuffers(window);
    }
void processInput(GLFWwindow *window)
{
    if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
        glfwSetWindowShouldClose(window, true);
}

如果glfwWindowShouldClose函式返回值為false,那麼不會關閉視窗。就進入processInput函式,如果沒有按下Esc鍵,那麼glfwWindowShouldClose(window)繼續為false,意思就是繼續繪製。glfwPollEvents()這個函式檢查有沒有觸發什麼事件(比如鍵盤輸入、滑鼠移動等)。就到了glfwSwapBuffers(window);這個函式會交換顏色緩衝(它是一個儲存著GLFW視窗每一個畫素顏色值的大緩衝),它在這一迭代中被用來繪製,並且將會作為輸出顯示在螢幕上。說了一大堆。說白了就是應用雙緩衝交換顏色緩衝。那麼問題來了。什麼是雙緩衝技術? 這裡寫圖片描述 感覺有點像以前的雙排火槍手啊,第一排開槍,第二排把槍裝好彈然後遞給第一排 glfwTerminate()函式釋放/刪除之前分配的資源.

void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
    glViewport(0, 0, width, height);
}

glViewport函式前兩個引數控制視窗左下角的位置。第三個和第四個引數控制渲染視窗的寬度和高度(畫素)。