opengl像素格式和RC管理

http://hi.baidu.com/dem_simulation/item/7e45bc4086bb59a461d7b9c6

1.寫在前面

由於OpenGL被設計成獨立於硬件,獨立於窗口系統,因此OpenGL的用戶不需要考慮如何對計算機圖形設備中的顯示面(display surfaces)進行初始化和管理,這些工作交由操作系統幫助完成。雖然如此,任何想要使用好OpenGL的開發人員,都應該理解在Windows平臺下(如果你的程序運行於Windows平臺下的話),一副三維圖形在顯示面上呈現的具體原理。正所謂“連路都不會走,跑是必然要摔跟頭的”,所以沉下心來,一一搬開你路上的石頭。

這裏主要討論像素格式和渲染設備描述表(RC)的管理原理。能成功管理這些對象,就建立了硬件與圖形接口之間的橋樑。在Windows平臺下提供有兩種機制來完成硬件與OpenGL之間的接連——像素格式操作API和WGL API。其中後者用於管理RC。好,下面一一介紹。

2.軟件渲染模式與硬件加速模式

在Windows平臺上,OpenGL驅動可能有三種模式:純軟件、MCD和ICD。

   ***   純軟件模式是由微軟提供一個OpenGL的軟件實現,所有渲染操作均由CPU完成,速度很慢。如果安裝系統時使用Windows自帶的顯卡驅動程序,那麼OpenGL程序就會運行在軟件模式下。而且由於微軟有自己的Direct3D,所以對OpenGL的支持很消極,它的OpenGL純軟件實現只支持OpenGL1.1,而目前OpenGL的最新版本爲 1.4。

   ***   MCD(Mini Client Driver)是早期微軟在Windows NT上支持OpenGL時,爲了簡化驅動開發時使用的一個模型。MCD雖然可以簡化驅動開發,但是功能限制太大,現在市面上的3D加速卡均支持硬件變換和光照,MCD卻不能利用這一特性,看上去MCD已經沒有存在的價值。

   ***   ICD(Installable Client Driver)是一個完整的OpenGL驅動模型,比MCD複雜得多。硬件廠商要實現完整的OpenGL渲染管線,如變換、光照、光柵化等,因此只要硬件支持,ICD可以硬件加速整個OpenGL渲染管線。我們通常說的OpenGL硬件加速就是指的通過ICD模型獲得的硬件加速,而現在硬件廠商提供的OpenGL驅動程序也都是依照ICD模型開發的。(主要硬件廠商的ICD已經可以支持OpenGL的最新版1.4)

Windows怎麼實現OpenGL硬件加速呢?OpenGL32.dll是微軟的OpenGL 1.1純軟件實現,我們的程序都要動態鏈接到這個dll。如果安裝3D芯片廠商的驅動程序,會將一個不同名字的dll放到Windows系統目錄下,比如在Windows 2000下安裝nVIDIA GeForce2 MX的驅動程序,會在系統目錄下放置一個nvoglnt.dll(這就是nVIDIA的OpenGL驅動),並在註冊表中登記nvoglnt.dll,讓 Windows知道硬件加速OpenGL驅動的名字,以後運行OpenGL程序,OpenGL32.dll就會把OpenGL調用直接轉到 nvoglnt.dll進行處理。nvoglnt.dll則將OpenGL和 WGL指令打包發送給3D芯片廠商的驅動程序,這些驅動程序與支持繪圖渲染的基礎函數庫聯接,這樣以來,OpenGL程序可以最大限度地利用顯卡對OpenGL渲染的支持,從而極大地提高渲染速度。

在Windows平臺上,一個OpenGL程序是否使用硬件加速由三個因素決定,這三個因素缺一不可,否則程序都會運行於純軟件模式: 
   ***   是否有一塊3D加速卡 
   ***   是否安裝了顯卡廠商提供的最新的驅動程序,Windows自帶的顯卡驅動程序並不會提供OpenGL硬件加速能力 
‍ ***   指定的像素格式是否被顯卡硬件所支持

3.像素格式管理

圖形的整體顯示由單個的像素點所組成。每個像素在內存中都分配了一定字節數的內存空間。用於保存所有像素信息的內存就是緩存。緩存可以有多種形式,分別用於保存不同的信息,比如深度緩存保存每個像素的深度值等。計算機屏幕上所有像素的字節的某一位組成的矩陣是一個位面(bitplane),系統中所有的緩存統稱爲幀緩存。

幀緩存有四種,一種是顏色緩存,其內包含了該像素的顏色信息,數據可以是顏色索引值(color index),也可以是顏色的RGBA值;一種是深度緩存,其內包含了每個像素的深度信息,如果兩個像素佔用同一個位置,那麼較低深度值的像素用覆蓋較高深度值的像素;一種是模板緩存,利用它可以限制屏幕上繪圖的區域;最後一種是累積緩存,它主要用於保存各像素的RGBA值,用於反走樣等複雜的效果。

在Windows對OpenGL的軟件實現中,包含了這些緩存區的設置。軟件實現支持單緩存和雙緩存模式,支持深度緩存、模板緩存和積累緩存,但層級緩存還不支持。爲了有效地利用這些緩存,必須設置好像素格式。每個OpenGL程序窗口都有一套像素格式,下面討論結構、功能和使用方法。

3.1像素格式結構體

PIXELFORMATDESCRIPTOR是像素格式結構體,用以描述Windows平臺下的像素格式的各類屬性。某些硬件廠商可能會爲加強OpenGL的渲染能力而支持某此軟件加速並不支持的像素格式屬性。結構體定義如下:

typedef struct tagPIXELFORMATDESCRIPTOR
{
WORD nSize;           //定義數據結構體的大小,可以這樣給值:pfd.nsize = sizeof(PIXELFORMATDESCRIPTOR)
WORD nVersion;                            //直接設置爲1
DWORD dwFlags;                            //一系列位標識符,可以相互取或聯接,具體有哪些功能標識符,查看MSDN
BYTE iPixelType;      //定義像素數據的類型,是索引值(‍PFD_TYPE_COLORINDEX)還是RGBA值(PFD_TYPE_RGBA)
BYTE cColorBits;      //每個顏色緩存中的位面數,如果是RGBA模式,除去alpha位面後的位面數就是該值
BYTE cRedBits;        //顏色緩存中,紅色信息所佔用的位面數
BYTE cRedShift;       //紅色位面數在緩存區中的移位數,即移多少位可以找到紅色的RGB信息   
BYTE cGreenBits;      //顏色緩存中,綠色信息所佔用的位面數
BYTE cGreenShift;     //綠色位面數在緩存區中的移位數,即移多少位可以找到綠色的RGB信息
BYTE cBlueBits;       //顏色緩存中,藍色信息所佔用的位面數
BYTE cBlueShift;      //藍色位面數在緩存區中的移位數,即移多少位可以找到藍色的RGB信息
BYTE cAlphaBits;      //軟件加速不支持, 顏色緩存中,alpha信息的位面數
BYTE cAlphaShift;     //軟件加速不支持, 顏色緩存中,alpha信息移位數
BYTE cAccumBits;       //積累緩存中的總位面數
BYTE cAccumRedBits;    //積累緩存中的紅色信息位面數
BYTE cAccumGreenBits;    //積累緩存中的綠色信息位面數
BYTE cAccumBlueBits;     //積累緩存中的藍色信息位面數
BYTE cAccumAlphaBits;      //積累緩存中的alpha信息的位面數
BYTE cDepthBits;          //深度緩存保存的深度值
BYTE cStencilBits;        //模板緩存中保存的深度值   
BYTE cAuxBuffers;          //軟件加速並不使用該項,輔助緩存區的數目
BYTE iLayerType;          //1.0版本的OpenGL只能置爲PFD_MAIN_PLANE
BYTE bReserved;            //必須置爲0
DWORD dwLayerMask;
DDWOR dwVisibleMask;
DWORD dwDamageMask;
} PIXELFORMATDESCRIPTOR;
;

其中以每一個像素的顏色緩存區爲例子說明一下各參數的意義,如下圖所示。


圖:像素格式結構體中每一像素的顏色緩存區(RGBA模式)

3.2像素格式的種類

Windows對OpenGL的軟件實現中支持24種不同的像素格式,雖然每一種都由唯一的標識(1-24)所區別,但並非這麼簡單。某一種像素格式,主要還是由其內所具有的屬性來歸類,如下圖:



圖:像素格式的主要屬性

由上面的圖中可以看出,對一個像素格式進行歸類,主要看它的BPP,即每個像素的字節位數。這裏支持5種位面組成:32BPP、24BPP、16BPP、8BPP和4BPP。如果按照這種分類方法,24種定義的像素格式中,有8種是專門爲顯示硬件所設計的,顯示硬件驅動程序定義了每個像素應該具有的字節位數,這些像素格式被稱爲是與硬件相匹配的“本地像素格式”(native format),餘下的16種像素格式叫作“非本地像素格式”,這些像素格式支持對位圖的操作。

還可以按照像素數據類型來分,RGBA和顏色索引值。然後可以按單緩存或是雙緩存分類,最後可以按照深度緩存的深度分類(32或者16)。這樣算起來,像素格式的各類不下40種,但是16種非本地像素格式被限制使用,因爲它們對位圖的雙緩存區不起作用。各種像素格式的各類,具體可以參見MSDN。

3.3列舉像素格式

像素格式各類比較多,開發者應該爲自己的應用程序精心挑選一款適合自己具體情況的像素格式。可以寫一個程序來列舉所有的像素格式。代碼如下:

void CPixForm::OnClickedLastPfd()
{
COpenGL gl;
PIXELFORMATDESCRIPTOR pfd;
//
//得到視類窗口的句柄.
//
HWND hwndview = GetViewHwnd();
//
//得到與視類窗口相關聯的DC句柄
//
HDC   hdc   = ::GetDC(hwndview);
int nID = (m_nNextID > 1) ? m_nNextID-- : 1;
//
//得到像素格式的具體內容,如果不可用,那麼查看下一個,並更新對話框內容,否則什麼都不做
//
if (gl.DescribePixelFormat(hdc, nID, sizeof(PIXELFORMATDESCRIPTOR), &pfd))
    UpdateDlg(&pfd);
//
//釋放DC
//
::ReleaseDC(hwndview, hdc);
}

3.4管理像素格式的函數

有4個函數用於像素格式的管理。它們都是Win32函數。它們分別是:

ChoosePixelFormat
SetPixelFormat
GetPixelFormat
‍DescribePixelFormat

使用這些函數的一般方法由下圖給出:



圖:像素格式管理函數的使用

一個應用程序可以從上圖中的最上面的方塊(也就是PIXELFORMATDESCRIPTOR結構體中)知道一些基本的信息,比如說程序應用使用雙緩存、應該對窗口進行OpenGL渲染、可以支持GDI等等。應用程序要麼調用ChoosePixelFormat函數,要麼調用它自定義的像素格式匹配函數。其中前者將開發者要求的像素格式與軟件或者硬件支持的像素格式進行比較,並返回最匹配的某一款像素格式,下面的步驟具體描述了這種匹配的過程:

   ***   首先,ChoosePixelFormat( )函數查找那些滿足開發者定義的像素格式的屬性,如:
            ***   PFD_DRAW_TO_WINDOW
            *** PFD_DRAW_TO_BITMAP
            *** PFD_SUPPORT_GDI
            *** PFD_SUPPORT_OPENGL
            *** PFD_TYPE_RGBA
            *** PFD_TYPE_COLORINDEX
            *** PFD_DOUBLEBUFFER
            *** PFD_STEREO
   ***   然後,函數嘗試與下面的屬性值進行匹配:
            ***   cColorBits
            *** cAlphaBits
            *** cAccumBits
            *** cDepthBits
            *** cStencilBits
            *** cAuxBuffers
            *** iLayerType
   ***   最後,在返回的可匹配的像素格式中,如果既有硬件支持的又有軟件支持的,函數會優先選擇前者作爲最佳結果返回給開發者。

一旦你有了一款合適的像素格式,就調用SetPixelFormat( )函數,將窗口的像素格式設爲選擇結果。一旦窗口的像素格式設置成功,它就不能再被修改了。

3.5硬件支持的像素格式識別

要識別一款像素格式是否被硬件支持,可以通過下面的代碼實現,它主要是利用像素格式結構體中的dwFlags域來查看像素格式的相關信息。

BOOL COpenGL::IsDeviceIndex(HDC hdc, int idx)
{
ASSERT (hdc);
ASSERT (idx > 0);
BOOL bRet = FALSE;
PIXELFORMATDESCRIPTOR pfd;
int ipfdmax = DescribePixelFormat(hdc, idx, sizeof(PIXELFORMATDESCRIPTOR), 
                &pfd);
if (!(pfd.dwFlags & PFD_GENERIC_FORMAT))
    bRet = TRUE;
return (bRet);
}

如果‍PFD_GENERIC_FORMAT位被置1,那麼像素格式是軟件支持。否則爲硬件支持的。那麼查看一款像素格式是否爲本地像素格式,也可以用類似的方法完成:

BOOL COpenGL::IsNativeIndex(HDC hdc, int idx)
{
ASSERT (hdc);
ASSERT (idx > 0);

BOOL bRet = FALSE;
PIXELFORMATDESCRIPTOR pfd;
int ipfdmax = DescribePixelFormat(hdc, idx, sizeof(PIXELFORMATDESCRIPTOR), 
                &pfd);

if (pfd.dwFlags & PFD_DRAW_TO_WINDOW)
    bRet = TRUE;
return (bRet);
}

如果PFD_DRAW_TO_WINDOW被置1,那麼像素格式就是本地像素格式。否則,它是非本地像素格式,於是可用以支持位圖操作。

4.RC管理

4.1OpenGL與DC

在使用OpenGL與DC之前,有以下注意事項:

   ***   窗口的像素格式被設置後,就不能再修改;
   ***   DC可用以創建RC,它可以被釋放或者刪除,所有的DC依次被收回或者創建的時候,必須有正確的像素格式索引號與之對應。

爲了取得當前使用的像素格式的索引號,可以使用GetPixelFormat( )函數,下面的代碼顯示瞭如何應用這個函數:

int COpenGL::GetCurPFDIndex()
{
int icuridx = GetPixelFormat(wglGetCurrentDC());
return (icuridx);
}

得到的icuridx可以再傳入DescribePixelFormat( )函數,以便取得更多的信息。

4.2 OpenGL的RC

在使用RC之前,也有幾點注意事項:

   ***   在創建RC之前,必須設置好像素格式
   ***   在調用任何OpenGL命令之前,RC必須與DC相關聯
   ***   RC與DC相關聯期間,DC不得被釋放或者刪除(除非與該DC對應的窗口類的風格爲CS_OWNDC)

有5個函數用以管理RC:

wglCreateContext
wglMakeCurrent
wglGetCurrentContext
wglGetCurrentDC
wglDeleteContext

這些函數都很重要,但是你格外注意一下wglMakeCurrent( )函數,該函數能讓所有的繪圖在DC上完成。通常,應用程序調用wglCreateContext(),然後將調用wglMakeCurrent()將RC與繪圖面相關聯,然後OpenGL可以繪圖,然後仍然用‍wglMakeCurrent()將RC與繪圖面分離,最後RC由wglDeleteContext()刪除。

4.3兩種RC的管理機制

既然DC(包括像素格式)與RC如此緊密地聯繫,那麼如何將它們合理地利用並管理呢?首先,用DC創建一個RC,OpenGL利用這個RC在DC上,最終在繪圖面上進行繪製。有兩種方法利用DC。如下圖所示,DC在程序初始化時創建,在程序關閉時釋放。這種方法有些讓人不安,因爲我們並沒有在同一個作用域內完成DC的創建和釋放。其實這種作法是可行的,沒有錯誤。


圖:RC的管理機制一

另一種方法則比較傳統,如下圖所示。這種方法裏面,RC與兩個DC進行了關聯,同時要調用兩次wglMakeCurrent( )函數,這個函數是相當耗時的,所以這種方法在實時渲染和動畫設計中,不推崇使用。


圖:RC管理機制二

5.總結

Windows平臺爲OpenGL提供了軟件實現,如果沒有硬件驅動支持,所有的像素格式和RC的管理都交由GDI處理。如果有硬件驅動支持,那麼硬件設備會分擔很多管理任務,從而使OpenGL渲染運行地更快。在OpenGL可以繪圖之前,窗口、位圖或者硬件的像素格式必須初始化。然後創建RC,最後由程序開發人員在此基礎上盡情展示自己精彩的程序

參考文獻

[1] 劉梢月 圖形處理OpenGL硬件加速 ‍http://blog.csdn.net/liusaoyue/archive/2009/12/29/5093949.aspx
[2] Dennis Crain Windows NT OpenGL: Getting Started   Microsoft Developer NetWork Technology Group MSDN-2001

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章