在畫出出色的效果以前,首先要作的就是建立一個 OpenGL 上下文和一個用於顯式的窗口。
一些函數庫已經提供了這樣的功能,能夠提供給開發者一個窗口和上下文來渲染。
比較流行的有 GLUT, SDL, SFML, GLFW, 此處咱們使用 GLFW.html
GLAD 一般和 GLFW 配合使用; GLEW 一般和 Freeglut 配合使用。c++
簡介
GLFW 是一個專門針對 OpenGL 的 C 語言庫,它提供了一些渲染物體所須要的最低限度的接口。它容許用戶建立 OpenGL 上下文,定義窗口參數以及處理用戶輸入。git
官網下載
GLFW 須要從 GLFW 官網進行下載。
github
能夠下載源文件而後配合 CMake 工具生成對應的二進制版本和頭文件,具體的作法可參見連接:LearnOpenGL CN。
也能夠直接下載了官網編譯好的二進制版本,建議下載 32 位版本。緩存
百度雲下載
https://pan.baidu.com/s/1CCtc0sy1WpkScIkP3GZD8A 提取碼 9hjy函數
因爲 OpenGL 驅動版本衆多,它大多數函數的位置都沒法在編譯時肯定下來,須要在運行時查詢。因此任務就落在了開發者身上,開發者須要在運行時獲取函數地址並將其保存在一個函數指針中供之後使用。然而這個過程既複雜有繁瑣,而 GLAD 庫能夠簡化此過程。工具
官網下載
打開 GLAD 的在線服務,選擇以下配置後進行生成,選擇壓縮文件 glad.zip 下載便可。spa
百度雲下載
https://pan.baidu.com/s/1LlBCLbRNDSUNUKz4kzw_ow 提取碼 54xk線程
將獲得的壓縮包進行解壓。
爲了方即可以將使用須要的文件放在指定的目錄中。此處我選擇將其放在文件夾 C:\Program Files\OpenGL 中。指針
移動後的文件結構以下:
OpenGL | include | glad |
GLFW | ||
KHR | ||
vc2019 | ... | |
src | glad.c |
新建一個源文件,要包含如下頭文件
#include <glad/glad.h> #include <GLFW/glfw3.h>
建立 main
函數,實例化 GLFW 窗口
int main() { // 初始化 GLFW glfwInit(); // 配置 GLFW // 第一個參數是 enum 類型表示選項名稱 // 第二個參數是 int 類型用於設置第一個參數的值 glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); // 設置主版本號 glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); // 設置次版本號 glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // 使用核心模式 // 僅 Mac OS X 系統須要下面的語句 //glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); return 0; }
建立一個窗口對象
// glfwCreateWindow 函數用於建立窗口對象 glfwCreateWindow // 第1、2、三個參數分別是窗口的寬度、高度、窗口的標題 // 後兩個參數暫時先忽略 // 返回一個 GLFWwindow 對象的指針 GLFWwindow* window = glfwCreateWindow(800, 600, "LearnOpenGL", NULL, NULL); if (window == NULL) { std::cout << "Failed to create GLFW window" << std::endl; glfwTerminate(); return -1; } // 窗口建立成功以後通知 GLFW 將窗口的上下文設置爲當前線程的主上下文 glfwMakeContextCurrent(window);
加載系統儲相關的 OpenGL 函數指針地址
GLAD 是用來管理 OpenGL 的函數指針的,因此任何調用 OpenGL 的函數以前都要初始化 GLAD。
// gladLoadGLLoader: GLAD 裝載 GL 裝載機 // glfwGetProcAddress: GLFW 根據編譯的系統定義正確的函數 if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) { std::cout << "Failed to initialize GLAD" << std::endl; return -1; }
視口
在開始渲染以前必需要告訴 OpenGL 渲染矩形區域的尺寸大小,即視口(Viewport)。
視口的大小能夠和窗口相同,也能夠大於或者小於窗口的大小。
只有繪製在視口區域的圖形才能顯式,
// glViewport: 設置視口的錨點和大小 // 第1、二個參數控制視口左下角的位置,窗口左下角爲(0, 0) // 第3、四個參數控制視口的大小(單位:像素) glViewport(0, 0, 800, 600);
當用戶改變窗口的大小時,視口也應該被調整。
能夠對窗口註冊一個回調函數,它會在每次窗口大小被調整時被調用。
// 回調函數第一個參數是窗口對象指針 // 回調函數的後兩個參數是窗口被改變以後的寬度和高度 void framebuffer_size_callback(GLFWwindow* window, int width, int height) { glViewport(0, 0, width, height); }
另外,還須要註冊這個回調函數,告訴 GLFW 開發者但願每當窗口調整大小的時候調用這個函數。
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
渲染循環
在程序中添加一個 while
循環用於在 GLFW 推出以前不斷的繪製圖像並接受用戶的輸入。
while(!glfwWindowShouldClose(window)) { glfwSwapBuffers(window); glfwPollEvents(); }
glfwWindowShouldClose
函數在每次循環開始時檢查一次 GLFW 是否被要求退出。glfwPollEvents
函數檢測是否觸發了什麼事件、更新窗口狀態,並調用對應的回調函數。glfwSwapBuffers
函數會交換顏色緩存,它在這一迭代中被用來繪製,而且做爲輸出顯示在屏幕上。雙緩衝(Double buffer)
應用程序使用單緩衝繪圖時會可出現圖像閃爍的問題,這是由於生成的圖像不是一會兒被繪製出來的,而是一步步生成的,這致使渲染的結果不真實。
爲了不這一問題,咱們採用雙緩衝渲窗口應用程序。其中,前緩衝中保存着最終輸出的圖像;後換衝用於繪製全部的渲染指令。當全部的渲染指令完成以後,交換先後緩衝,圖像就會當即顯示出來。
正確釋放/刪除以前的分配的全部資源
// 正確釋放/刪除以前的分配的全部資源 glfwTerminate();
以獲取一個按鍵輸入爲例:當按下 Escape 按鍵時關閉窗口。
// 建立一個 processInput 來處理輸入 void processInput(GLFWwindow *window) { // glfwGetKey 用於檢查按鍵 Escape 是否被按下(若是按下了返回 GLDFW_RELEASE) // glfwSetWindowShouldClose 用於 Escape 按下時將 WindowShouldClose 屬性設置爲 true if(glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) glfwSetWindowShouldClose(window, true); // 下一次循環就會關閉 GLFW } // 在循環渲染的每次迭代中調用 processInput while (!glfwWindowShouldClose(window)) { processInput(window); glfwSwapBuffers(window); glfwPollEvents(); }
咱們要把全部的渲染(Rendering)操做放到渲染循環中,從而保證渲染指令在每次渲染循環迭代的時候都能被執行。
// 渲染循環 while(!glfwWindowShouldClose(window)) { // 輸入 processInput(window); // 渲染指令 ... // 檢查並調用事件,交換緩衝 glfwPollEvents(); glfwSwapBuffers(window); }