opengl是一個由Khronos組織制定並維護的規範(Specification) 。是一系列的圖形軟件編程接口,和gdi相似。opengl有不少封裝的庫最有名的GLFW庫。接下來不少東西以GLFW 爲例子來講明一些api的使用問題,但這並不影響opengl自己的邏輯表述。ios
OpenGL自身是一個巨大的狀態機(State Machine):一系列的變量描述OpenGL此刻應當如何運行。OpenGL的狀態一般被稱爲OpenGL上下文(Context)。咱們一般使用以下途徑去更改OpenGL狀態:設置選項,操做緩衝。最後,咱們使用當前OpenGL上下文來渲染。編程
假設當咱們想告訴OpenGL去畫線段而不是三角形的時候,咱們經過改變一些上下文變量來改變OpenGL狀態,從而告訴OpenGL如何去繪圖。一旦咱們改變了OpenGL的狀態爲繪製線段,下一個繪製命令就會畫出線段而不是三角形。api
當使用OpenGL的時候,咱們會遇到一些狀態設置函數(State-changing Function),這類函數將會改變上下文。以及狀態使用函數(State-using Function),這類函數會根據當前OpenGL的狀態執行一些操做。只要你記住OpenGL本質上是個大狀態機,就能更容易理解它的大部分特性。函數
在OpenGL中一個對象是指一些選項的集合,它表明OpenGL狀態的一個子集 。oop
當咱們使用一個對象時,一般看起來像以下同樣:線程
// OpenGL的狀態 struct OpenGL_Context { ... object* object_Window_Target; ... }; // 建立對象 unsigned int objectId = 0; glGenObject(1, &objectId); // 綁定對象至上下文 glBindObject(GL_WINDOW_TARGET, objectId); // 設置當前綁定到 GL_WINDOW_TARGET 的對象的一些選項 glSetObjectOption(GL_WINDOW_TARGET, GL_OPTION_WINDOW_WIDTH, 800); glSetObjectOption(GL_WINDOW_TARGET, GL_OPTION_WINDOW_HEIGHT, 600); // 將上下文對象設回默認 glBindObject(GL_WINDOW_TARGET, 0);
這一段代碼展示了使用OpenGL時常見的工做流。咱們首先建立一個對象,而後用一個id保存它的引用(實際數據被儲存在後臺)。而後咱們將對象綁定至上下文的目標位置(例子中窗口對象目標的位置被定義成GL_WINDOW_TARGET)。接下來咱們設置窗口的選項。最後咱們將目標位置的對象id設回0,解綁這個對象。設置的選項將被保存在objectId所引用的對象中,一旦咱們從新綁定這個對象到GL_WINDOW_TARGET位置,這些選項就會從新生效。code
咱們要開始一個圖形渲染程序,首要是要選擇gl庫,由於要使用api.而後建立窗口、設置視口、設置窗口大小調整後的回調在回調中要處理視口、接着是渲染循環、還要處理處輸入等。對象
int main() { glfwInit(); //初始化glfw glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); //配置glfw glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); //glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); return 0; }
首先,咱們在main函數中調用glfwInit函數來初始化GLFW,而後咱們可使用glfwWindowHint函數來配置GLFW。glfwWindowHint函數的第一個參數表明選項的名稱,咱們能夠從不少以GLFW_開頭的枚舉值中選擇;第二個參數接受一個整形,用來設置這個選項的值接口
接下來咱們建立一個窗口對象,這個窗口對象存放了全部和窗口相關的數據,並且會被GLFW的其餘函數頻繁地用到。事件
GLFWwindow* window = glfwCreateWindow(800, 600, "LearnOpenGL", NULL, NULL); if (window == NULL) { glfwTerminate(); return -1; } glfwMakeContextCurrent(window);
glfwCreateWindow函數須要窗口的寬和高做爲它的前兩個參數。第三個參數表示這個窗口的名稱(標題),。這個函數將會返回一個GLFWwindow對象,建立完窗口咱們就能夠通知GLFW將咱們窗口的上下文設置爲當前線程的主上下文了。
必須告訴OpenGL渲染窗口的尺寸大小,即視口(Viewport),這樣OpenGL才只能知道怎樣根據窗口大小顯示數據和座標。咱們能夠經過調用glViewport函數來設置窗口的維度(Dimension):
glViewport(0, 0, 800, 600);
glViewport函數前兩個參數控制窗口左下角的位置。第三個和第四個參數控制渲染窗口的寬度和高度(像素)。
OpenGL幕後使用glViewport中定義的位置和寬高進行2D座標的轉換,將OpenGL中的位置座標轉換爲你的屏幕座標,處理過的OpenGL座標範圍只爲-1到1。
當窗口大小發生變換的時候須要設置視口的大小:
glfwSetFramebufferSizeCallback(window, [](GLFWwindow* window, int width, int height){ glViewport(0, 0, width, height); });
咱們一樣也但願可以在GLFW中實現一些輸入控制,這能夠經過使用GLFW的幾個輸入函數來完成。咱們將會使用GLFW的glfwGetKey函數,它須要一個窗口以及一個按鍵做爲輸入。這個函數將會返回這個按鍵是否正在被按下。咱們將建立一個processInput函數來讓全部的輸入代碼保持整潔。
void processInput(GLFWwindow *window) { if(glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) glfwSetWindowShouldClose(window, true); }
須要在程序中添加一個while循環,咱們能夠把它稱之爲渲染循環(Render Loop),它能在咱們讓GLFW退出前一直保持運行。下面幾行的代碼就實現了一個簡單的渲染循環:
// 渲染循環 while(!glfwWindowShouldClose(window)) { // 輸入 processInput(window); // 渲染指令 ... glClearColor(0.2f, 0.3f, 0.3f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); // 檢查並調用事件,交換緩衝 glfwPollEvents(); glfwSwapBuffers(window); } glfwTerminate();
true
而後渲染循環便結束了,以後爲咱們就能夠關閉應用程序了。應用程序使用單緩衝繪圖時可能會存在圖像閃爍的問題。 這是由於生成的圖像不是一會兒被繪製出來的,而是按照從左到右,由上而下逐像素地繪製而成的。最終圖像不是在瞬間顯示給用戶,而是經過一步一步生成的,這會致使渲染的結果很不真實。爲了規避這些問題,咱們應用雙緩衝渲染窗口應用程序。前緩衝保存着最終輸出的圖像,它會在屏幕上顯示;而全部的的渲染指令都會在後緩衝上繪製。當全部的渲染指令執行完畢後,咱們交換(Swap)前緩衝和後緩衝,這樣圖像就當即呈顯出來,以前提到的不真實感就消除了。
#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; int main() { //glfw初始化和設置 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) { glfwTerminate(); return -1; } //設置問當前窗口上下文 glfwMakeContextCurrent(window); //設置窗口大小改變的回調 處理視口變化 glfwSetFramebufferSizeCallback(window, framebuffer_size_callback); // render loop while (!glfwWindowShouldClose(window)) { // input processInput(window); // render glClearColor(0.2f, 0.3f, 0.3f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); // glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.) // ------------------------------------------------------------------------------- glfwSwapBuffers(window); glfwPollEvents(); } // glfw: terminate, clearing all previously allocated GLFW resources. 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); }