OpenGL教程(2)——第一個窗口


注:本文可轉載,轉載請注明出處:http://www.cnblogs.com/collectionne/p/6618419.html

 

OpenGL環境終於配置好了,現在我們可以開始學習OpenGL了。

 

首先,創建一個.cpp文件,然后打上幾行#include指令:

 

#include <iostream>
using std::cout;
using std::endl;

#include <GL/glew.h>
#include <GLFW/glfw3.h>

 

先從<iostream>說起。其實<iostream>不是必須的,這里包含它,是因為后面可能需要在控制台上打印信息,提醒用戶。glew.h和glfw3.h是我們上一篇教程里所下載的頭文件,注意一定要先包含glew.h,再包含glfw3.h(因為glfw3.h會包含gl.h,而這是glew.h不允許的)。最后,如果你使用的是靜態版的GLEW,需要在包含glew.h前加上下面的#define指令:

 

#define GLEW_STATIC

 

然后我們進入main()函數:

 

int main()
{

 

GLFW

 

首先我們需要初始化GLFW:

 

if (!glfwInit())
{
    cout << "Failed to initialize GLFW!\n";     
    return -1;
}

 

glfwInit()函數用於初始化GLFW,在調用大部分其它GLFW函數前,都需要初始化GLFW。glfwInit()如果成功,將會返回GLFW_TRUE,否則返回GLFW_FALSE(GLFW_TRUE和GLFW_FALSE是GLFW定義的常量,被定義為於1和0)。也就是說,如果返回GLFW_FALSE,就說明初始化失敗,這時我們將打印一條信息告知用戶GLFW初始化失敗,並且返回(注意,你可能無法看到打印的信息,這時可以在return -1前加上一句cin.get()或system("pause")暫停程序)。不過,檢查返回值也不是必須的。

 

然后我們需要使用glfwWindowHint()函數設置hint:

 

glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);

 

Hint直接翻譯成中文是“線索”,這里大致指“選項”。使用glfwWindowHint()可以設置一些關於窗口的選項。glfwWindowHint()接受兩個參數,第一個是我們要設置的hint的名字,使用GLFW常量(以GLFW_開頭)指定;第二個是我們要把該hint設置成的值,該值隨要設置的hint而異。

 

Hint也有很多種,這里我們只設置了4種,分別是OpenGL主版本號(GLFW_CONTEXT_VERSION_MAJOR)、OpenGL副版本號(GLFW_CONTEXT_VERSION_MINOR)、OpenGL模式(GLFW_OPENGL_PROFILE)、窗口是否可調整大小(GLFW_RESIZABLE)。這4個hint,我們分別設置為3、3、GLFW_CORE_PROFILE、GLFW_FALSE。

 

因為我們要使用OpenGL 3.3,所以我們把GLFW_CONTEXT_VERSION_MAJOR和GLFW_CONTEXT_VERSION_MINOR對應的hint都設置為3。因為我們要使用OpenGL核心模式(這個后面會提到更多),所以我們把GLFW_OPENGL_PROFILE對應的hint設置為GLFW_OPENGL_CORE_PROFILE,表示使用OpenGL核心模式。最后,把GLFW_RESIZABLE對應的hint設置為GLFW_FALSE,表示窗口不允許用戶調整大小。之所以這樣做是因為如果允許用戶調整大小,大小發生變化后,窗口的繪制區域默認不變(依然是原來窗口的區域),也就是說窗口上繪制的圖像的大小、位置不會發生改變。為了避免這種現象發生,我們就簡單地不讓用戶調整窗口大小(當然也有更好的方法,就是用GLFW設置一個窗口大小的回調函數,但這樣比較簡單)。

 

注意,如果你使用Mac OS X,你需要再加上下面這句調用:

 

glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE);

 

然后就該創建窗口了:

 

GLFWwindow * window = glfwCreateWindow(800, 600, "First window", nullptr, nullptr);
if (window == nullptr)
{
    cout << "Failed to create window using GLFW!\n";
    return -1;
}

 

glfwCreateWindow()接受5個參數。第一個、第二個是窗口的寬和高,以像素為單位,這里分別是800和600;第三個是窗口標題,這里是"First window";第四個和第五個參數可以忽略,直接傳入nullptr。

 

glfwCreateWindow()將會使用前面glfwWindowHint()所設置的hint創建窗口,返回一個GLFWwindow指針,我們把這個指針叫做窗口句柄(window handle)。簡單說來,就是從此以后我們都用它來代表我們的窗口,后面會需要對窗口進行設置、詢問一些關於窗口的信息,就需要傳入這個指針。如果創建窗口出現問題,將返回NULL(C++里就是nullptr),因此如果window為nullptr,說明創建窗口失敗,打印錯誤信息,返回-1。

 

創建完畢之后,需要讓當前窗口的環境在當前線程上成為當前環境(說法有點復雜,不太嚴謹地說,就是接下來的畫圖都會畫在我們剛剛創建的窗口上):

 

glfwMakeContextCurrent(window);

 

GLEW

 

GLFW設置完畢后,還需要初始化GLEW:

 

glewExperimental = GL_TRUE;
if (glewInit())
{
    cout << "Failed to init GLEW!\n";
    return -1;
}

 

首先我們把glewExperimental設置為GL_TRUE,這樣GLEW就會使用更加新的方法管理OpenGL,減少錯誤。然后調用glewInit()初始化GLEW。注意glewInit()成功返回0,失敗返回1,與GLFW相反。如果失敗則輸出錯誤信息,並返回-1。

 

游戲循環

 

和控制台程序不同,我們希望這個程序可以一直運行,直到用戶關閉窗口。這樣我們就需要創建一個循環,叫做游戲循環(game loop)。

 

while (!glfwWindowShouldClose(window))
{
glfwSwapBuffers(window); glfwPollEvents(); }

 

glfwWindowShouldClose()檢查窗口是否需要關閉。如果是,游戲循環就結束了,接下來我們將會清理資源,結束程序。

 

glfwSwapBuffers()用來交換窗口的兩個顏色緩沖(color buffer)。這個概念叫做雙緩沖(double buffer)。如果不使用雙緩沖,就可能會出現閃屏現象,因為繪制一般不是一下子就繪制完畢的,而是從左到右、從上到下地繪制。為了避免這個問題,一般會使用雙緩沖,前緩沖(front buffer)是最終的圖像,而程序會在后緩沖(back buffer)上繪制。后緩沖繪制完畢后,就交換兩個緩沖,這樣就不會有閃屏的問題了。

 

glfwPollEvents()用來檢查是否有事件被觸發,例如點擊關閉按鈕、點擊鼠標、按下鍵盤,等等。如果有,GLFW將會對這些事件進行處理。更嚴謹地說(以鍵盤、鼠標鍵為例),GLFW自己會記錄每個鍵、鼠標鍵的狀態(按下/沒有按下),但當某個按鍵松開或被按下時,GLFW不會自動更新狀態,必須調用glfwPollEvents()才能更新。調用glfwPollEvents()時會檢查狀態是否有變化(如按下的是否松開,沒有按下的是否被按下),如果有就會更新該狀態。如果設置了回調函數(這個將在以后講),還會調用相應的回調函數。如果不調用這個函數,不僅無法檢測輸入(后文會需要這樣),我們在點擊窗口右上角的X時,GLFW也不會知道需要關閉窗口。所以必須在每一輪游戲循環中調用這個函數。

 

最后,當循環執行完畢后,我們需要釋放前面所申請的資源:

 

glfwTerminate();
return 0;
}

 

glfwTerminate()將會釋放所有GLFW資源,關閉當前窗口。然后程序就成功退出了。

 

現在運行你的程序,如果你得到了一個黑色窗口,恭喜你,成功了!如果沒有,請參考結尾的代碼,對照自己的代碼。

 

額外的東西

 

現在我們已經成功創建窗口,並且在用戶點擊右上角的X時退出。但是還不夠——你可能會問,可不可以讓用戶按ESC鍵時退出,或者換一個窗口背景顏色?這里我們就來實現這兩項。

 

關於第一個功能,可以這樣寫(在游戲循環里):

 

if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
    glfwSetWindowShouldClose(window, GLFW_TRUE);

 

glfwGetKey()用來判斷一個鍵是否按下。第一個參數是GLFW窗口句柄,第二個參數是一個GLFW常量,代表一個鍵。GLFW_KEY_ESCAPE表示Esc鍵。如果Esc鍵按下了,glfwGetKey將返回GLFW_PRESS(值為1),否則返回GLFW_RELEASE(值為0)。

 

如果Esc被按下了,我們就會調用glfwSetWindowShouldClose()函數,為窗口設置關閉標志。第一個參數是窗口句柄,第二個參數表示是否關閉,這里為GLFW_TRUE,表示關閉該窗口。注意,這時窗口不會立即被關閉,但是glfwWindowShouldClose()將返回GLFW_TRUE,到了glfwTerminate()就會關閉窗口。

 

關於第二個功能,可以在進入游戲循環之前,加一句:

 

glClearColor(0.2f, 0.3f, 0.3f, 1.0f);

 

與其它glfw開頭的函數(這些函數由GLFW提供)不同,這個函數是以gl開頭的,是OpenGL的函數。glClearColor用來設置窗口被清除時的顏色,也就是背景顏色。前3個參數分別是背景顏色的R、G、B分量,范圍是0~1;第4個是alpha值表示透明度,這里只是設為1.0f,表示不透明。(0.2, 0.3, 0.3)這一顏色大致是較暗的藍綠色。

 

然后在游戲循環中加一句:

 

glClear(GL_COLOR_BUFFER_BIT);

 

這將會清除當前窗口,把所有像素的顏色都設置為前面所設置的清除顏色。GL_COLOR_BUFFER_BIT是OpenGL定義的常量,表示清除顏色緩存。后面會學到,還有GL_DEPTH_BUFFER_BIT和GL_STENCIL_BUFFER_BIT。

 

現在運行程序,你應該會得到下面的畫面,並且按Esc時會關閉窗口:

 

 

最終代碼

 

#include <iostream>
using std::cout;
using std::endl;

#include <GL/glew.h>
#include <GLFW/glfw3.h>

int main()
{ 
    // init GLFW
    if (!glfwInit())
    {
        cout << "Failed to init GLFW!\n";
        return -1;
    }

    // set window hints
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); 
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); 
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
// create the window GLFWwindow * window = glfwCreateWindow(800, 600, "First window", nullptr, nullptr); if (window == nullptr) { cout << "Failed to create window using GLFW!\n"; return -1; } glfwMakeContextCurrent(window); // init GLEW glewExperimental = GL_TRUE; if (glewInit()) { cout << "Failed to init GLEW!\n"; return -1; } // set clear color glClearColor(0.2f, 0.3f, 0.3f, 1.0f); // game loop while (!glfwWindowShouldClose(window)) { // if Esc is pressed, close the window if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) glfwSetWindowShouldClose(window, GLFW_TRUE); // clear the window glClear(GL_COLOR_BUFFER_BIT); // poll and swap glfwSwapBuffers(window);
glfwPollEvents(); }
// cleanup glfwTerminate(); return 0; }

 

好啦,你已經學會了創建窗口的基本技巧,下一課,我們將會學習如何畫我們的第一個三角形!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM