姑且不談?wù)揙penGL的名貴背景和光明前途,單憑其實(shí)用性和有效性就足以使其成為我們圖形輸出編程的首選。但是在實(shí)際工程應(yīng)用中程序員沒必要仔細(xì)地深究OpenGL的運(yùn)作機(jī)制,也往往不需要掌握各種高級的效果制作,真正需要的是最簡捷地利用這個(gè)得力的工具實(shí)現(xiàn)輸出數(shù)據(jù)的可視化,如波形、譜圖、立體統(tǒng)計(jì)圖表等的顯示。有鑒于此,本文總結(jié)出了在C++Builder中OpenGL編程的實(shí)用框架。筆者經(jīng)過實(shí)踐,對于一般的圖形輸出的應(yīng)用,此框架足以應(yīng)付。對于復(fù)雜些的程序可以在本框架的基礎(chǔ)上進(jìn)行擴(kuò)充。
在Windows環(huán)境下用OpenGL編程至少要了解如下預(yù)備知識:
OpenGL本身:對于一般的應(yīng)用,我們可以認(rèn)為OpenGL就是一套與窗口系統(tǒng)和操作系統(tǒng)無關(guān)的三維圖形函數(shù)庫。
DC和RC:每個(gè)Win32應(yīng)用程序都有一個(gè)設(shè)備描述表(Device Context)簡稱DC,在這個(gè)設(shè)備描述表中包含了圖形怎樣顯示在窗口的設(shè)置( GDI)信息。調(diào)用OpenGL函數(shù)必須使用設(shè)備描述表才能正確地在窗口輸出。圖形操作描述表(Rendering Context)簡稱RC,是一種設(shè)備描述表的形式,在圖形操作描述表中存放一些OpenGL和操作系統(tǒng)相聯(lián)系的信息。圖形操作描述表是傳遞所有OpenGL命令的端口。
像素格式:像素格式定義了OpenGL繪圖的屬性,創(chuàng)建圖形操作描述表首先要定義象素格式。它由這樣一個(gè)數(shù)據(jù)結(jié)構(gòu)實(shí)現(xiàn)
typedef struct tagPIXELFORMATDESCRIPTOR
{
WORD nSize; //結(jié)構(gòu)大小
WORD nVersion; //版本
DWORD dwFlags; //象素緩沖的位標(biāo)志
BYTE iPixelType; //RGBA模式或顏色索引模式
BYTE cColorBits; //顏色位數(shù)
BYTE cRedBits; //RGBA模式下R所占位數(shù)
BYTE cRedShift; //RGBA模式下R位數(shù)偏移
BYTE cGreenBits; //RGBA模式下G所占位數(shù)
BYTE cGreenShift; //RGBA模式下G位數(shù)偏移
BYTE cBlueBits; //RGBA模式下B所占位數(shù)
BYTE cBlueShift; //RGBA模式下B位數(shù)偏移
BYTE cAlphaBits; //RGBA模式下Alpha所占位數(shù)
BYTE cAlphaShift; //RGBA模式下Alpha位數(shù)偏移
BYTE cAccumBits; //累計(jì)緩沖區(qū)位面總數(shù)
BYTE cAccumRedBits; //累計(jì)緩沖區(qū)R位面總數(shù)
BYTE cAccumGreenBits; //累計(jì)緩沖區(qū)G位面總數(shù)
BYTE cAccumBlueBits; //累計(jì)緩沖區(qū)B位面總數(shù)
BYTE cAccumAlphaBits; //累計(jì)緩沖區(qū)Alpaha位面總數(shù)
BYTE cDepthBits; //深度緩沖位數(shù)
BYTE cStencilBits; //模板緩沖位數(shù)
BYTE cAuxBuffers; //Win32 下不支持
BYTE iLayerType; //不再使用
BYTE bReserved; //0
DWORD dwLayerMask; //不再使用
DWORD dwVisibleMask; //0
DWORD dwDamageMask; //不再使用
} PIXELFORMATDESCRIPTOR;
雙緩沖技術(shù):OpenGL支持一個(gè)顯示緩沖和一個(gè)非顯示緩沖。缺省的情況是所有的OpenGL繪制命令在非顯示緩沖中繪制,繪制完成后再將其內(nèi)容拷貝到顯示緩沖區(qū)中(使用SwapBuffers函數(shù))。雙緩沖使圖象轉(zhuǎn)換更平滑,這就是在快速動畫(如波形等的實(shí)時(shí)輸出)時(shí)沒有屏幕閃爍的奧妙所在。
反走樣技術(shù):實(shí)際中需要畫出的往往是曲線,由于計(jì)算機(jī)以離散點(diǎn)生成圖形,曲線上會有鋸齒,這就是一種走樣現(xiàn)象。在用一般語言畫圖時(shí),這一現(xiàn)象是難以避免的。OpenGL中利用混合技術(shù),把原來邊界的鋸齒部分用低飽和度的點(diǎn)補(bǔ)上從而實(shí)現(xiàn)反走樣,達(dá)到平滑的邊界效果。
好,我們現(xiàn)在可以啟用下面的程序框架了。
在*.h文件的類聲明中添加private成員:
private:
HGLRC hRC;
HDC hDC;
以下是相應(yīng)*.cpp文件
首先加上兩個(gè)包含文件:
#include <glgl.h> //程序使用OpenGL的核心函數(shù)
#include <glglu.h> //程序使用實(shí)用庫中的函數(shù)
一、在FormCreate()函數(shù)中完成OpenGL的初始化
使用OpenGL必須首先進(jìn)行一些初始化工作,具體包含以下步驟:
1、創(chuàng)建DC
hDC=GetDC(Handle);
此句獲取一個(gè)設(shè)備描述表,TForm1->Handle中保存有Form的窗口句柄;很多情況下我們希望在一個(gè)Panel中輸出圖形,那么可以用Panel1->Handle作為此函數(shù)的參數(shù)。
2、創(chuàng)建RC
(1)定義像素格式
static PIXELFORMATDESCRIPTOR pfd={
sizeof(PIXELFORMATDESCRIPTOR), //此結(jié)構(gòu)的大小
1, //此結(jié)構(gòu)的版本
PFD_DRAW_TO_WINDOW| //在窗口上繪圖(而不是在位圖上)
PFD_SUPPORT_OPENGL| //在窗口中支持使用OpenGL
PFD_DOUBLEBUFFER, //使用雙緩沖模式
PFD_TYPE_RGBA, //使用RGBA色彩模式
24, //存儲顏色數(shù)據(jù)的位數(shù)
0,0,0,0,0,0,
0,0,0,0,0,0,0,
32, //深度緩沖區(qū)大小
0,0,
PFD_MAIN_PLANE, //在主平面上繪圖
0,
0,0,0
};
(2)選擇最佳像素格式
int iPixelFormat=ChoosePixelFormat(hDC,&pfd);
選擇一最適合上述pfd結(jié)構(gòu)的像素格式,并把保存索引號。
SetPixelFormat(hDC,iPixelFormat,&pfd);
按選擇的索引號設(shè)置設(shè)備描述表的像素格式。
(3)用DC創(chuàng)建RC
hRC=wglCreateContext(hDC);
用指定的設(shè)備描述表產(chǎn)生一個(gè)圖形操作描述表,使它在該設(shè)備描述表上繪圖,并且有與此設(shè)備描述表相同的像素格式。
3、指定當(dāng)前的DC、RC
wglMakeCurrent(hDC,hRC);
把產(chǎn)生的圖形操作描述表置為當(dāng)前的,程序此后的所有OpenGL函數(shù)都通過此圖形操作描述表執(zhí)行,并將圖形繪制在設(shè)備描述表引用的設(shè)備上。
到此就完成了初始化工作,這些步驟基本上是固定的(像素格式的參數(shù)設(shè)置也是如此),對于一般的應(yīng)用可以直接使用上述語句。
二、在FormDestroy()中作清理工作以釋放資源
1、清屏
glClearColor(0.0,0.0,0.0,1.0);
設(shè)置背景色為黑色。
glClear(GL_COLOR_BUFFER_BIT);
清屏以防止對以后窗口操作的影響。
2、當(dāng)前DC、RC置空
wglMakeCurrent(NULL,NULL);
使不再有當(dāng)前的圖形操作描述表。
3、刪除DC、RC
wglDeleteContext(hRC);
刪除該圖形操作描述表。
DeleteObject(hDC);
刪除該設(shè)備描述表。
如果在同一個(gè)程序里對多個(gè)窗體用繪圖必須嚴(yán)格進(jìn)行清理,否則輸出會出現(xiàn)混亂。
三、在FormPaint()中實(shí)施繪圖的相關(guān)操作
每當(dāng)窗體重畫時(shí)進(jìn)行繪圖的動作。OnPaint事件可能由系統(tǒng)觸發(fā)例如置為當(dāng)前窗口;也可以由程序觸發(fā)即在需要改變繪圖時(shí)調(diào)用TForm->FormPaint(Sender)。把所有繪圖操作統(tǒng)一歸入FormPaint()事件的響應(yīng)函數(shù)中使我們很容易控制繪圖的時(shí)機(jī),程序變得很有條理。
1、繪圖準(zhǔn)備
(1)指定DC
HDC hDC;
hDC=wglGetCurrentDC();
在多個(gè)設(shè)備描述表如多個(gè)繪圖面板時(shí)有必要指定此后OpenGL命令輸出的目標(biāo)。
(2)清屏
動態(tài)的圖形輸出必須有清屏的操作將上此繪圖的結(jié)果以背景色覆蓋掉,以便畫新的圖形。
glClearColor(0.0,0.0,0.0,1.0);
背景色以黑色為例。
glClear(GL_COLOR_BUFFER_BIT);
清屏命令。
(3)啟動反走樣功能(可選)
glEnable(GL_BLEND);
啟動混合。
glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
指定混合的屬性。
glEnable(GL_LINE_SMOOTH);
啟動線反走樣。
glHint(GL_LINE_SMOOTH_HINT,GL_NICEST);
指定為線反走樣且為最好質(zhì)量。
注意在繪圖結(jié)束后要有相應(yīng)的關(guān)閉反走樣的操作,以防止影響程序以后的操作,語句如下
glDisable(GL_LINE_SMOOTH);
glDisable(GL_BLEND);
2、繪圖
調(diào)用自定義的繪圖函數(shù)RenderSence()。
3、緩沖操作
glFlush();
強(qiáng)制完成繪圖工作。
SwapBuffers(hDC);
完成兩個(gè)圖形緩沖區(qū)的交換,把畫完的非顯示緩沖圖形顯示出來。
四、編寫繪圖函數(shù)
即定義RenderSence()函數(shù),完成真正的繪圖操作。實(shí)際上是把工程計(jì)算的結(jié)果作為參數(shù)調(diào)用OpenGL的各種繪圖庫函數(shù)。具體函數(shù)與畫法請參閱有關(guān)書籍。我們把單純的繪圖和效果設(shè)定單列出來作為一個(gè)函數(shù)供FormPaint()調(diào)用,這樣的結(jié)構(gòu)使畫圖編程變得非常靈活。更復(fù)雜的情況可以寫多個(gè)自定義繪圖函數(shù),也可以引入?yún)?shù)列表。
五、視口變換
OpenGL中有多種圖形變換,其中視口變換是比較簡單而且常用的變換方式,所以也歸為本框架的一部分。事實(shí)上只需要使用一個(gè)函數(shù)
glViewport(0,0, ClientWidth, ClientHeight);
此函數(shù)可以指定全部圖形最后投影的一個(gè)矩形區(qū)域,上句以整個(gè)窗體的客戶區(qū)為例。在FormResize()事件響應(yīng)函數(shù)中調(diào)用這個(gè)函數(shù)可以使圖形在窗體大小形狀變化時(shí)保持相同比例的縮放。