參考官方文檔:https://learnopengl-cn.github.io/
一直對OpenGL很感興趣,但每次找到網上各種教程,跟着教程做總是不知道哪裏出錯了總是運行不出來,可能是自己有某一步出錯了,也可能是這些教程都漏了什麼細節沒交代。這裏終於根據官方教程結合自己的探究總算成功完成了第一步:創建一個窗口。爲此,必須把整個步驟詳細交代以下,方便作爲以後的參考。
知識儲備:這裏是需要熟悉C++,雖然其他語言如C#也是可以通過安裝OpenTK來進行OpenGL編程的,但OpenTK的相關資料太少了,所以感覺研究難度很大,要入門還是C++合適。然後是基本的數學知識:線性代數、幾何等。不會這些的話,玩不來這些自然就被勸退了。
構建GLFW
-
首先是從官方網站下載最新版本的源代碼包並解壓。https://www.glfw.org/download.html。
-
然後是用CMake來構建。CMake最好是最新版的,它的版本是和VS的版本相關的。啓動CMake,需要選擇兩個目錄,源代碼目錄選定GLFW的源代碼目錄,新建一個
build
目錄作爲目標目錄。 -
點擊
Configure
按鈕,選擇對應的VS版本確定,然後點Generate
按鈕。
-
在文件管理器中找到第二步創建的
build
文件夾,其中有個文件GLFW.sln
,雙擊用VS打開,在VS中的菜單欄的生成
欄的生成解決方案
生成成功後就行了。順便其中包含了大量的Examples
和Tests
,其中有很多的小項目可以在後面出粗的時候作爲一個參考。 -
上一步完成後,在
..\build\src\Debug\
下能找到文件glfw3.lib
。這個文件很重要。另外還有..\glfw-master\include
下的GLFW
文件夾也是很重要的。可以額外在任意的路徑下新建一個文件夾比如命名爲relation
,將GLFW
文件夾和glfw3.lib
文件都放進去。 -
配置GLAD。打開網址https://glad.dav1d.de/,如圖選擇
gl
欄和Profile
欄,最後點擊GENERATE
。然後下載glad.zip
文件解壓就行了。
-
接下來是容易出問題的。打開VS,新建一個C++的空白應用。首先確認
Debug
和x64
,通常默認的Debug
和x86
一定要改爲x64
,正是這裏總是導致出錯。右擊項目選擇屬性,要確認是Debug
和活動(x64)
。在VC++目錄
下修改包含目錄和庫目錄。
-
然後在
連接器
|輸入
下的添加依賴項下添加兩項:opengl32.lib
和glfw3.lib
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-JX6wfPao-1590500907788)(C:\Users\xhh\Pictures\QQ瀏覽器截圖\opentk_9.png)]
- 在解決方案資源管理下右擊
源文件
添加現有項選擇..\glad\src
下的glad.c
文件。然後新建C++源文件main.cpp
,輸入代碼:
#include<iostream>
#include<glad/glad.h>
#include<GLFW/glfw3.h>
int main(void) {
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_COMPAT_PROFILE);
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);
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {
std::cout << "Failed to initialize GlAD" << std::endl;
return -1;
}
glViewport(0, 0, 800, 600);
void framebuffer_size_callback(GLFWwindow * window, int width, int height);
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
while (!glfwWindowShouldClose(window))
{
glfwSwapBuffers(window);
glfwPollEvents();
}
glfwTerminate();
return 0;
}
void framebuffer_size_callback(GLFWwindow* window, int width, int height) {
glViewport(0, 0, width, height);
}
如果上面的所有步驟都沒問題的話最後運行會出現一個控制檯窗口和一個額外的窗口。就說明成功運行了。如果失敗的話再回過頭看一下哪一步有問題。
必要的說明:
- 先是調用
glfwInit
函數來初始化GLFW,然後用glfwWindowHint
函數來配置GLFW。
int main() {
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_COMPAT_PROFILE);
}
- 接下來創建一個窗口對象,它存放了所有和窗口相關的數據,而且會被GLFW的其他函數頻繁用到。
glfwCreateWindow
函數的前三個參數分別是寬、高和標題。這個函數會返回一個GLFWwindow
對象。創建完窗口我們就可以通知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);
- GLAD是用來管理OpenGL的函數指針的,所以我們需要先初始化GLAD
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {
std::cout << "Failed to initialize GlAD" << std::endl;
return -1;
}
- 開始渲染之前我們必須告訴OpenGL渲染窗口的尺寸大小。
glViewport(0, 0, 800, 600);
- 但當用戶改變窗口的大小時,視口也需要被調整。我們可以對窗口註冊應給回調函數,他會在每次窗口大小被調整時被調用。我們可以先在這裏聲明,在main函數後面再給出完整的定義。這樣就不會有語法錯誤了。也可以在main函數之前給出函數的定義。
int main{
...;
void framebuffer_size_callback(GLFWwindow * window, int width, int height);
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
...;
}
void framebuffer_size_callback(GLFWwindow* window, int width, int height) {
glViewport(0, 0, width, height);
}
- 然後我們需要在程序中添加一個while循環,通常稱爲渲染循環,它可以在我們讓GLFW退出前保持運行。
while (!glfwWindowShouldClose(window))
{
glfwSwapBuffers(window);//交換顏色緩衝,它在每一次迭代中被用來繪製,並作爲輸出顯示在屏幕上
glfwPollEvents();//檢測有沒有觸發什麼事件、更新窗口狀態,並調用對應的回調函數
}
- 最後,渲染循環結束後我們需要正確的釋放和刪除之前分配的所有資源。
glfwTerminate();
return 0;
- 我們可以添加一些輸入控制,如讓它響應鍵盤輸入,在main函數前定義一個函數,然後在渲染循環的每一次迭代中調用這個函數:
void processInput(GLFWwindow* window) {
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
glfwSetWindowShouldClose(window, true);
}
int main(){
...;
while (!glfwWindowShouldClose(window))
{
processInput(window);
glfwSwapBuffers(window);//交換顏色緩衝,它在每一次迭代中被用來繪製,並作爲輸出顯示在屏幕上
glfwPollEvents();//檢測有沒有觸發什麼事件、更新窗口狀態,並調用對應的回調函數
}
glfwTerminate();
return 0;
}
- 我們可以在渲染循環中放入一些渲染操作。如用一個自定義的顏色清空屏幕。可以調用
glClear
函數來清空屏幕的顏色緩衝,它接受一個緩衝位來指定要清空的緩衝,可選的緩衝位有:GL_COLOR_BUFFER_BIT
、GL_DEPTH_BUFFER_BIT
和GL_STENCLL_BUFFER_BIT
。我們這裏選擇顏色緩衝。
while (!glfwWindowShouldClose(window))
{
processInput(window);
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glfwSwapBuffers(window);//交換顏色緩衝,它在每一次迭代中被用來繪製,並作爲輸出顯示在屏幕上
glfwPollEvents();//檢測有沒有觸發什麼事件、更新窗口狀態,並調用對應的回調函數
}
這樣我們運行以後窗口的屏幕變爲我們設置的顏色