創建OpenGL Context(WGL)


創建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

 


免責聲明!

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



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