我們試試能不能讓GLFW正常工作。首先,新建一個.cpp文件,然后把下面的代碼粘貼到該文件的最前面。
#include <glad/glad.h>
#include <GLFW/glfw3.h>
請確認是在包含GLFW的頭文件之前包含了GLAD的頭文件。GLAD的頭文件包含了正確的OpenGL頭文件(例如GL/gl.h),所以需要在其它依賴于OpenGL的頭文件之前包含GLAD。接下來我們創建main函數,在這個函數中我們將會實例化GLFW窗口:
int main()
{
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
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’s window handling 這篇文檔中找到。如果你現在編譯你的cpp文件會得到大量的 undefined reference (未定義的引用)錯誤,也就是說你并未順利地鏈接GLFW庫。
由于本站的教程都是基于OpenGL 3.3版本展開討論的,所以我們需要告訴GLFW我們要使用的OpenGL版本是3.3,這樣GLFW會在創建OpenGL上下文時做出適當的調整。這也可以確保用戶在沒有適當的OpenGL版本支持的情況下無法運行。我們將主版本號(Major)和次版本號(Minor)都設為3。我們同樣明確告訴GLFW我們使用的是核心模式(Core-profile)。明確告訴GLFW我們需要使用核心模式意味著我們只能使用OpenGL功能的一個子集(沒有我們已不再需要的向后兼容特性)。如果使用的是Mac OS X系統,你還需要加下面這行代碼到你的初始化代碼中這些配置才能起作用(將上面的代碼解除注釋):
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
請確認您的系統支持OpenGL3.3或更高版本,否則此應用有可能會崩潰或者出現不可預知的錯誤。如果想要查看OpenGL版本的話,在Linux上運行glxinfo,或者在Windows上使用其它的工具(例如OpenGL Extension Viewer)。如果你的OpenGL版本低于3.3,檢查一下顯卡是否支持OpenGL 3.3+(不支持的話你的顯卡真的太老了),并更新你的驅動程序,有必要的話請更新顯卡。
接下來我們創建一個窗口對象,這個窗口對象存放了所有和窗口相關的數據,而且會被GLFW的其他函數頻繁地用到。
GLFWwindow* window=glfwCreateWindow(800, 600, "LearnOpenGL", NULL, NULL);
if (window==NULL)
{
std::cout << "Failed to create GLFW window" << std::endl;
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
glfwCreateWindow函數需要窗口的寬和高作為它的前兩個參數。第三個參數表示這個窗口的名稱(標題),這里我們使用"LearnOpenGL",當然你也可以使用你喜歡的名稱。最后兩個參數我們暫時忽略。這個函數將會返回一個GLFWwindow對象,我們會在其它的GLFW操作中使用到。創建完窗口我們就可以通知GLFW將我們窗口的上下文設置為當前線程的主上下文了。
在之前的教程中已經提到過,GLAD是用來管理OpenGL的函數指針的,所以在調用任何OpenGL的函數之前我們需要初始化GLAD。
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
std::cout << "Failed to initialize GLAD" << std::endl;
return -1;
}
我們給GLAD傳入了用來加載系統相關的OpenGL函數指針地址的函數。GLFW給我們的是glfwGetProcAddress,它根據我們編譯的系統定義了正確的函數。
在我們開始渲染之前還有一件重要的事情要做,我們必須告訴OpenGL渲染窗口的尺寸大小,即視口(Viewport),這樣OpenGL才只能知道怎樣根據窗口大小顯示數據和坐標。我們可以通過調用glViewport函數來設置窗口的維度(Dimension):
glViewport(0, 0, 800, 600);
glViewport函數前兩個參數控制窗口左下角的位置。第三個和第四個參數控制渲染窗口的寬度和高度(像素)。
我們實際上也可以將視口的維度設置為比GLFW的維度小,這樣子之后所有的OpenGL渲染將會在一個更小的窗口中顯示,這樣子的話我們也可以將一些其它元素顯示在OpenGL視口之外。
OpenGL幕后使用glViewport中定義的位置和寬高進行2D坐標的轉換,將OpenGL中的位置坐標轉換為你的屏幕坐標。例如,OpenGL中的坐標(-0.5, 0.5)有可能(最終)被映射為屏幕中的坐標(200,450)。注意,處理過的OpenGL坐標范圍只為-1到1,因此我們事實上將(-1到1)范圍內的坐標映射到(0, 800)和(0, 600)。
然而,當用戶改變窗口的大小的時候,視口也應該被調整。我們可以對窗口注冊一個回調函數(Callback Function),它會在每次窗口大小被調整的時候被調用。這個回調函數的原型如下:
void framebuffer_size_callback(GLFWwindow* window, int width, int height);
這個幀緩沖大小函數需要一個GLFWwindow作為它的第一個參數,以及兩個整數表示窗口的新維度。每當窗口改變大小,GLFW會調用這個函數并填充相應的參數供你處理。
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
glViewport(0, 0, width, height);
}
我們還需要注冊這個函數,告訴GLFW我們希望每當窗口調整大小的時候調用這個函數:
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
當窗口被第一次顯示的時候framebuffer_size_callback也會被調用。對于視網膜(Retina)顯示屏,width和height都會明顯比原輸入值更高一點。
我們還可以將我們的函數注冊到其它很多的回調函數中。比如說,我們可以創建一個回調函數來處理手柄輸入變化,處理錯誤消息等。我們會在創建窗口之后,渲染循環初始化之前注冊這些回調函數。
我們可不希望只繪制一個圖像之后我們的應用程序就立即退出并關閉窗口。我們希望程序在我們主動關閉它之前不斷繪制圖像并能夠接受用戶輸入。因此,我們需要在程序中添加一個while循環,我們可以把它稱之為渲染循環(Render Loop),它能在我們讓GLFW退出前一直保持運行。下面幾行的代碼就實現了一個簡單的渲染循環:
while(!glfwWindowShouldClose(window))
{
glfwSwapBuffers(window);
glfwPollEvents();
}
雙緩沖(Double Buffer)
應用程序使用單緩沖繪圖時可能會存在圖像閃爍的問題。 這是因為生成的圖像不是一下子被繪制出來的,而是按照從左到右,由上而下逐像素地繪制而成的。最終圖像不是在瞬間顯示給用戶,而是通過一步一步生成的,這會導致渲染的結果很不真實。為了規避這些問題,我們應用雙緩沖渲染窗口應用程序。前緩沖保存著最終輸出的圖像,它會在屏幕上顯示;而所有的的渲染指令都會在后緩沖上繪制。當所有的渲染指令執行完畢后,我們交換(Swap)前緩沖和后緩沖,這樣圖像就立即呈顯出來,之前提到的不真實感就消除了。
當渲染循環結束后我們需要正確釋放/刪除之前的分配的所有資源。我們可以在main函數的最后調用glfwTerminate函數來完成。
glfwTerminate();
return 0;
這樣便能清理所有的資源并正確地退出應用程序。現在你可以嘗試編譯并運行你的應用程序了,如果沒做錯的話,你將會看到如下的輸出:
如果你看見了一個非常無聊的黑色窗口,那么就對了!
我們同樣也希望能夠在GLFW中實現一些輸入控制,這可以通過使用GLFW的幾個輸入函數來完成。我們將會使用GLFW的glfwGetKey函數,它需要一個窗口以及一個按鍵作為輸入。這個函數將會返回這個按鍵是否正在被按下。我們將創建一個processInput函數來讓所有的輸入代碼保持整潔。
void processInput(GLFWwindow *window)
{
if(glfwGetKey(window, GLFW_KEY_ESCAPE)==GLFW_PRESS)
glfwSetWindowShouldClose(window, true);
}
這里我們檢查用戶是否按下了返回鍵(Esc)(如果沒有按下,glfwGetKey將會返回GLFW_RELEASE。如果用戶的確按下了返回鍵,我們將通過glfwSetwindowShouldClose使用把WindowShouldClose屬性設置為 true的方法關閉GLFW。下一次while循環的條件檢測將會失敗,程序將會關閉。
我們接下來在渲染循環的每一個迭代中調用processInput:
while (!glfwWindowShouldClose(window))
{
processInput(window);
glfwSwapBuffers(window);
glfwPollEvents();
}
這就給我們一個非常簡單的方式來檢測特定的鍵是否被按下,并在每一幀做出處理。
我們要把所有的渲染(Rendering)操作放到渲染循環中,因為我們想讓這些渲染指令在每次渲染循環迭代的時候都能被執行。代碼將會是這樣的:
// 渲染循環
while(!glfwWindowShouldClose(window))
{
// 輸入
processInput(window);
// 渲染指令
...
// 檢查并調用事件,交換緩沖
glfwPollEvents();
glfwSwapBuffers(window);
}
為了測試一切都正常工作,我們使用一個自定義的顏色清空屏幕。在每個新的渲染迭代開始的時候我們總是希望清屏,否則我們仍能看見上一次迭代的渲染結果(這可能是你想要的效果,但通常這不是)。我們可以通過調用glClear函數來清空屏幕的顏色緩沖,它接受一個緩沖位(Buffer Bit)來指定要清空的緩沖,可能的緩沖位有GL_COLOR_BUFFER_BIT,GL_DEPTH_BUFFER_BIT和GL_STENCIL_BUFFER_BIT。由于現在我們只關心顏色值,所以我們只清空顏色緩沖。
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
注意,除了glClear之外,我們還調用了glClearColor來設置清空屏幕所用的顏色。當調用glClear函數,清除顏色緩沖之后,整個顏色緩沖都會被填充為glClearColor里所設置的顏色。在這里,我們將屏幕設置為了類似黑板的深藍綠色。
你應該能夠回憶起來我們在 OpenGL 這節教程的內容,glClearColor函數是一個狀態設置函數,而glClear函數則是一個狀態使用的函數,它使用了當前的狀態來獲取應該清除為的顏色。
好了,現在我們已經做好開始在渲染循環中添加許多渲染調用的準備了,但這是下一節教程了,這一節的內容已經太多了。
IT之家2月18日消息 昨天,微軟公布了關于Windows 10 on ARM可運行的應用的更多細節。該系統的局限性大概有:
只支持ARM64驅動程序;
X64應用程序不受支持;
某些依賴OpenGL或專有DRM的游戲無法運行;
自定義Windows體驗(并擴展Shell)的應用程序可能無法正常工作;
假定運行在Windows 10 Mobile上的基于ARM及應用程序無法運行;
不支持ARM Hypervisor平臺。
除了列出Windows 10 on ARM的局限性之外,微軟還列出了有關Windows 10 on ARM運行應用程序的常見問題以及為開發人員如何解決這些問題提供建議。
如果你的應用程序依賴的驅動程序不是為ARM設計的,那么請將您X86的驅動程序重新編譯為ARM64的。
如果您的應用程序僅適用于X64,而且是為Microsoft Store開發的,那么請提交您的應用程序的ARM64版本。
如果您是Win32應用程序的開發者,那么請您分發您的應用程序的X86版本。您的應用程序所使用的OpenGL的版本須新于1.1,且支持DirectX9/10/11和12的硬件加速。
若您開發的X86應用程序無法在Windows 10 on ARM上正常工作,請使用ARM上應用程序兼容性疑難解答程序來排除故障。
若您開發的X86應用程序未檢測到它在ARM上運行,請使用IsWow64Process2來確定您的應用程序是否在ARM上運行。
若您開發的ARM32 UWP無法按預期工作,請參閱ARM應用程序故障排除指南來了解如何讓您開發的應用程序在ARM上正常工作。
從微軟關于Windows 10 on ARM細節上的透露來看,Windows 10 on ARM更像是Windows 10的一個精簡版,而非微軟一直所宣傳的“可提供完整體驗的Windows 10”。
想看到更多這類內容?去APP商店搜IT之家,天天都有小歡喜。