創建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);
有一些擴展,給這個函數增加了新的屬性。你可能想要用到的有:
- WGL_ARB_pixel_format_float:支持浮點framebuffer。
- WGL_ARB_framebuffer_sRGB: 支持sRGB格式的colorbuffer。
- WGL_ARB_multisample: 支持多重采樣的framebuffer。
得到了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個屬性指定你想要哪個版本。
你請求一個版本,然后你得到哪個版本?這個規則比較復雜,簡單來說有兩條:
- 它總會返回一個等于或高于你要求的版本的OpenGL Context。
- 它永遠不會返回一個沒有實現你要求的版本里的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
- Core And Compatibility in Contexts
- Tutorial: OpenGL 3.0 Context Creation (GLX)
- Tutorial: OpenGL 3.1 The First Triangle (C++/Win)
References
文章列表