文章出處

創建OpenGL Context(WGL)

創建OpenGL Context是初始化OpenGL的一部分。只有在此之后才能使用OpenGL。

關于platform的注意事項

創建OpenGL context之后才會存在OpenGL。這個創建過程不歸OpenGL Specification管,而是歸各個platform的API管。本文討論基于Windows的初始化過程。許多Windows上的初始化函數是以”wgl”開頭的。

本文假設讀者知道Win32 API的基礎知識。讀者應知道window handle(HWND)和device context(DC)是什么,以及如何創建他們。本文不是講解如何創建窗口的教程。

創建一個簡單的Context

這一節是創建Context的基礎知識。

窗口

創建HWND時,要確保它有CS_OWNDC設置。

像素格式

MS Windows里,每個窗口都有一個Device Context(DC)與之關聯。DC里存儲有像素格式PixelFormat。你創建的OpenGL Context里有個默認的framebuffer,PixelFormat是描述此framebuffer的屬性的數據結構。

設置PixelFormat的方式并不直觀。首先你創建一個你想要的pixelFormat,然后交給ChoosePixelFormat函數,此函數會查找能夠支持的PixelFormat列表,返回最接近pixelFormat的編號。然后你就可以用此編號指定DC的PixelFormat。

上面描述的數據結構就是PIXELFORMATDESCRIPTOR。

 1 PIXELFORMATDESCRIPTOR pfd =
 2 {
 3     sizeof(PIXELFORMATDESCRIPTOR),
 4     1,
 5     PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,    //Flags
 6     PFD_TYPE_RGBA,            //The kind of framebuffer. RGBA or palette.
 7     32,                        //Colordepth of the framebuffer.
 8     0, 0, 0, 0, 0, 0,
 9     0,
10     0,
11     0,
12     0, 0, 0, 0,
13     24,                        //Number of bits for the depthbuffer
14     8,                        //Number of bits for the stencilbuffer
15     0,                        //Number of Aux buffers in the framebuffer.
16     PFD_MAIN_PLANE,
17     0,
18     0, 0, 0
19 };

你看,很多field都是0。就這樣,沒問題。我們需要關心的field,你可能需要用的field,都用注釋標記了。關于PixelFormat的更多flags,請查詢Windows SDK文檔。

上文說到ChoosePixelFormat函數,它接收一個DC和一個PFD,返回一個編號。如果返回的是0,那就意味著找不到匹配的PixelFormat,或者PDF內容錯誤。

有了PixelFormat編號,就可以用SetPixelFormat指定給DC。這個函數接收DC、編號和PFD的指針。別激動,這個函數沒有讀取PFD里的任何重要信息。

創建Context

接下來創建context就簡單了。調用wglCreateContext。這個函數接收DC,返回OpenGL Context的句柄。

在使用OpenGL前,要用wglMakeCurrent 把context設置為current。如果已經有current context,這個函數會把舊context替換掉。后續的OpenGL函數調用會影響新context中的狀態。如果你傳入NULL,那么舊context會被移除,后續OpenGL函數調用會失敗(崩潰)。

current context是線程專用的。每個線程可以將一個不同的context設置為current。將同一個context設置為多個線程的current是危險的。

刪除Context

嚴格來說這不是創建Context的內容,但是你應當指定如何刪除Context。

首先要確定你想刪除的Context不是current。給wglMakeCurrent 傳入NULL參數。

現在可以調用wglDeleteContext 來刪除它了。

創建合適的Context

除非你只想做一個很簡單的程序,否則你不應當使用上述的簡單步驟創建的context。有一些功能強大的WGL擴展函數助你創建高級context,但是創建context 過程會復雜些。

創建一個傻帽Context

關鍵問題是這樣的:你用來獲取WGL擴展的函數,其本身就是一個OpenGL擴展。因此,首先要有一個OpenGL Context,然后才能使用WGL擴展。所以,為了能夠使用那些“創建context的函數”,我們首先要“創建一個context”。幸運的是,這個context用不著是我們最后的context。我們只需創建一個傻帽context來獲取函數指針,然后直接使用這些函數即可。

警告:不幸的是,Windows不允許用戶改變一個窗口的PixelFormat。你只能設置一次。因此,如果你想通過傻帽Context使用一個不同的PixelFormat,你必須在用完傻帽Context后徹底銷毀這個窗口并重建之。

對于傻帽Context,一個好的PixelFormat選擇是32位RGBA顏色緩存+24位深度緩存+8位模版緩存。我們上面就是這樣設置的PFD。這通常都能得到一個硬件加速的PixelFormat。

所以,這一步就是重復上文的代碼,創建一個傻帽Context,設置為current。

獲取WGL擴展

Main article: Load OpenGL Functions#Windows 2

如果你使用了加載擴展的庫,現在就可以調用任何你需要的函數。如果沒有,你就得自己手動加載

有不少擴展可以實施高端的context創建工作。其中大多數是圍繞PixelFormat的創建和一個Exception。

Pixel Format擴展

PFD是幫助創建Context的很好的方式,但是有個缺點:不可擴展。因此,產生了WGL_ARB_pixel_format擴展。這個擴展定義了一種新的獲取PixelFormat編號的機制,此機制的核心是由一個’屬性\值’的數組。

只有在定義了此擴展的機器上才能使用它。這個擴展已經存在很長時間了,即使很老的顯卡也支持它。所以你可以打賭認為你的機器環境是實現了WGL_ARB_pixel_format的。

此擴展提供了幾個新的函數,我們感興趣的是下面這個:

1 BOOL wglChoosePixelFormatARB(   HDC hdc,
2                                 const int *piAttribIList,
3                                 const FLOAT *pfAttribFList,
4                                 UINT nMaxFormats,
5                                 int *piFormats,
6                                 UINT *nNumFormats);

wglChoosePixelFormatARB 類似ChoosePixelFormat。他接收的不是固定的PFD結構體,而是一個’屬性\值’的數組。很多屬性都直接對應PFD里的字段,但有些屬性是新的。而且,此函數能夠返回多個符合要求的PixelFormat,并按照從最符合到最不符合的順序排序。“最符合”是由具體OpenGL實現來決定的。

總之,使用方法很簡單。piAttribIList 是整數屬性列表。每2個元素構成一個’屬性\值’對。屬性’0’表示列表結束,并且其后不需要值。你可以傳入NULL,此函數會當作你傳入一個空列表。

類似的,pfAttribFList 是浮點屬性列表。每2個元素構成一個’屬性\值’對。如何將整型的屬性放到float類型里?非常小心地放。你需要用static-cast(C++里)或者用其它技巧讓bit-pattern保持相同。

nMaxFormats 是將要保存到piFormats里的數量的最大值。因此piFormats 應當至少有那么多個元素。nNumFormats 是返回值,告訴你piFormats真正存儲了多少個元素。

如果函數返回FALSE,就意味著沒有找到合適的PixelFormat。此時piFormats 就是未定義的狀態(OpenGL實現可以隨意修改其內容)如果函數返回不是FALSE,那么就成功了,你得到了PixelFormat編號。

下面的示例代碼演示了如何使用此函數產生和上文近似的PixelFormat:

 1 const int attribList[] =
 2 {
 3     WGL_DRAW_TO_WINDOW_ARB, GL_TRUE,
 4     WGL_SUPPORT_OPENGL_ARB, GL_TRUE,
 5     WGL_DOUBLE_BUFFER_ARB, GL_TRUE,
 6     WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB,
 7     WGL_COLOR_BITS_ARB, 32,
 8     WGL_DEPTH_BITS_ARB, 24,
 9     WGL_STENCIL_BITS_ARB, 8,
10     0,        //End
11 };
12 
13 int pixelFormat;
14 UINT numFormats;
15 
16 wglChoosePixelFormatARB(hdc, attribList, NULL, 1, &pixelFormat, &numFormats);

有一些擴展,給這個函數增加了新的屬性。你可能想要用到的有:

得到了PixelFormat編號,你就可以用SetPixelFormat指定給DC。

Attributes創建Context

為了移除舊功能,OpenGL3.0及其以上版本創造了一個“不推薦\可移除”的模型。但是這帶來一點問題。在之前的OpenGL版本,新版OpenGL是舊版的超集(superset)。因此,如果你想要的是1.5版context,結果得到的是2.0版,那沒問題。你只是得到了額外的你用不到的功能。一旦出現了移除舊功能的可能性,這種超集關系就沒有了。

因此出現了WGL_ARB_create_context擴展。它提供了代替wglCreateContext的函數。類似wglChoosePixelFormatARB,它提供了一種擴展機制,使你能夠增加新的創建context所用的選項。

如果傻帽context沒有提供這個擴展,那么你就不能用它。你就只能用wglCreateContext 了。

如果它提供了這個擴展,那么會有一些平常得不到的選項供我們選用:

  • 保證獲取OpenGL3.0或者更高版本的Context。
  • 創建OpenGL3.2或者更高班的core context,且沒有兼容舊特性。
  • 不用窗口,創建context,用于離屏渲染。這可能會做不成。

遺留問題:(這里有一堆沒什么用的話,略過不譯)如果定義了WGL_ARB_create_context_profile,那就用上述方法。如果沒有,那就只能用wglCreateContext 直接創建GL3.0或更高版本context。

wglCreateContextAttribsARB 簽名如下:

1 HGLRC wglCreateContextAttribsARB(HDC hDC, HGLRC hshareContext, const int *attribList);

這里的attribList 與wglChoosePixelFormatARB里的類似。它是一系列’屬性\值’對,以單獨的0作為最后一個元素結尾。

你可以用WGL_CONTEXT_MAJOR_VERSION_ARB 和WGL_CONTEXT_MINOR_VERSION_ARB這2個屬性指定你想要哪個版本。

你請求一個版本,然后你得到哪個版本?這個規則比較復雜,簡單來說有兩條:

  1. 它總會返回一個等于或高于你要求的版本的OpenGL Context。
  2. 它永遠不會返回一個沒有實現你要求的版本里的core feature的OpenGL版本。

如果定義WGL_ARB_create_context_profile了,那么你可以用WGL_CONTEXT_PROFILE_MASK_ARB 屬性來選擇一個core配置(WGL_CONTEXT_CORE_PROFILE_BIT_ARB)或者一個兼容配置(WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB)。注意,這些都是bit組合,所以你可以同時要求他們倆(不過你只會得到兼容配置)。這里面的細節就值得深入討論了。

你也可以用WGL_CONTEXT_FLAGS_ARB屬性指定若干flag。你可以用它請求一個向前兼容的context(WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB)且(或)一個debug context(WGL_CONTEXT_DEBUG_BIT_ARB)。Debug context常常實現了ARB_debug_output,能夠提供加強了的error輸出。向前兼容的context必須徹底移除deprecated特性,實際上你永遠都不應該用這個選項

hshareContext 是個特殊的參數。如果你有2個GL Context,且你想讓他們共享對象,那你可以用wglShareLists函數。但是你必須在創建對象(在任意兩個context里)之前使用。wglCreateContextAttribsARB 直接配合這個功能。

 

See Also

References

 


文章列表


不含病毒。www.avast.com
arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()