做一個OpenGL控制項 |
|
jackkcg
站務副站長 發表:891 回覆:1050 積分:848 註冊:2002-03-23 發送簡訊給我 |
此為轉貼資料 做一個OpenGL控制項 OpenGL是一個獨立於視窗的圖形庫,而圖形最終是在視窗系統裏繪製出來的,那?OpenGL的繪圖命令是怎?在窗口裏生成輸出的呢?
這就是各個系統上的OpenGL實現者需要做的工作了。在Windows裏是通過wgl庫完成的,在X-Windows裏是通過glx伺服器來完成的,至於這些OpenGL實現具體是怎?工作的,請參考sgi發佈的sample implement源碼,不過那個代碼是用C寫的。
在MS-Windows裏,wgl庫負責將OpenGL的繪製設備RenderContext與GDI的DeviceContext聯繫起來,使得發到OpenGL的RC裏的命令生成的點陣圖能夠在GDI DC裏繪製出來,你可以把它想象成OpenGL在RC裏有一個FrameBuffer,記錄著生成的圖案,而wgl則負責把FrameBuffer的內容BitBlt到DC上。當然,這並不是它實際的工作方法,如果想瞭解更多請參考SGI發佈的SDK資料或聯繫MS公司。
?了使GDI DC能夠接受OpenGL RC的輸出,必須?DC選定特別的圖元格式,然後建立RC,再用wglMakeCurrent把當前要使用的RC和DC聯繫起來。此後我們就可以用OpenGL命令正常工作了。在一個程式裏可以創建多個RC和多個DC,程式中的OpenGL命令會發到被wglMakeCurrent指定?當前的那一組合中。
我並不認?這個初始化過程是個很有意思的工作,這個世界上有很多聰明的程式師也這?想,所以他們發明了glaux庫和glut庫。glaux是在著名的OpenGL Programmer Guide裏提出的,這本書是OpenGL編程的官方文檔,因?它的封皮是紅色的,所以通常簡稱?RedBook。故名思意,glaux是一套輸助庫,它使得你無須關心在具體視窗系統裏初始化、消息回應的細節,而是使用傳統的c/dos程式風格編制OpenGL程式。
int main(int argc, char** argv)
{ auxInitDisplayMode( AUX_SINGLE|AUX_RGB|AUX_DEPTH16);//使用單緩衝、RGB彩色模式、16位元濃度
auxInitPosition(0,0,250,250);
auxInitWindow("Title");//以上兩行在(0,0)片建立了一個大小?250X250的窗口,其標題?"Title"。
myinit();//建立OpenGL透視投影環境
auxReshapeFunc(myReshape);//指定視窗大小變化的回應函數
auxMainLoop(display);//指定繪製函數
return 0; } 由於glaux是?教學目的開發的,所以實用價值很限,所以又有程式師開發了glut,這套庫被廣泛使用,它的工作方式與glaux極?類似,但功能完善得多,特別是對交互、全屏等的支援要理想得多,所以許多的OpenGL演示程式使用它,比如SGI網站上提供的多數演示程式都需要使用它。同時這套庫已經被移植到多種平臺上,所以要是想用簡單的方法開發在windows/macos/os2/xwindows等系統上都能使用的程式,那?應該選擇這套庫。
我並不認?一個Delphi程式師會喜歡glaux或glut,因?那意味著你不能利用Delphi的可視開發能力,另外任何真正實用的Delphi程式想直接在其他作業系統上編譯運行好象也不現實,即glut的跨平臺能力也沒有什?吸引力。我們應該開發一個VCL控制項,把初始化工作封裝起來。
我認?從TCustomPanel派生一個子類比較方便,讓我們稱它?TGLPanel吧。初始化過程要在WMCreate裏完成,之所以要放在這裏是因?這個時候Window Handle已經建立,但還沒?用。
在WMCreate中會?調用initDC完成DC調整工作,initDC會以本窗口使用的DC調用PreparePixelFormat,而PixelFormat則真正完成圖元格式調整,?然後WMCreate會調用InitGL完成OpenGL透視投影環境的設定,?最後調用OnInit給用戶一個調整透視投影環境的機會。
注意,如果要在MDI環境中的子表單中使用OpenGL,還有些附加工作要做,這就是給窗口類的Params.Style加上WS_CLIPCHILDREN和WS_CLIPSIBLINGS屬性,這得在Window Handle建立之前就完成,因此要寫在CreateParams裏。由於SDI應用並不需要該代碼,所以應該定義OnPreInit事件,讓用戶在需要的時候自己加上,在Create裏調用OnPreInit。以下代碼定義了OnPreInit,但並沒有定義CreateParams,如果需要自己加上吧。
在TGLPanel類中實際所做工作的詳細說明(按成員可見性組織):
私有
1、加入DC/RC/Pal私有變數
2、定義初始化DC/RC的私有方法 保護:
3、加入FOnPaint,FOnResize,FOnInit,FOnPreInit四個事件回應變數。
4、繼承/重載虛方法CreateParams,Paint,Resize
5、回應以下消息
WM_CREATE, TWMCreate, WMCreate
WM_DESTROY, TWMDestroy, WMDestroy
WM_PALETTECHANGED, TWMPaletteChanged, WMPaletteChanged
WM_QUERYNEWPALETTE, TWMQueryNewPalette, WMQueryNewPalette
WM_ERASEBKGND, TWMEraseBkgnd, WMEraseBkgnd 公開:
6、定義建構與析構方法
7、定義必要的其他方法以提供各種特性 發佈:
8、以下繼承來的屬性
__property Alignment;
__property Align;
__property DragCursor;
__property DragMode;
__property Enabled;
__property ParentFont;
__property ParentShowHint;
__property PopupMenu;
__property ShowHint;
__property TabOrder;
__property TabStop;
__property Visible;
9、以下繼承來的方法
__property OnClick;
__property OnDblClick;
__property OnDragDrop;
__property OnDragOver;
__property OnEndDrag;
__property OnEnter;
__property OnExit;
__property OnMouseDown;
__property OnMouseMove;
__property OnMouseUp;
__property OnStartDrag;
10、加入以下事件
//初始化OpenGL狀態
__property TNotifyEvent OnInit = {read=FOnInit,write=FOnInit};
//專用於修改顯示BPP模式
__property TNotifyEvent OnPreInit = {read=FOnPreInit,write=FOnPreInit};
11、重載以下事件
__property TNotifyEvent OnResize = {read=FOnResize,write=FOnResize};
__property TNotifyEvent OnPaint = {read=FOnPaint,write=FOnPaint};
12、將消息與其回應函數連接起來(Delphi中這一步是在定義函數時指定的)
源代碼
unit GLPanel; interface uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
ExtCtrls,OpenGL; type
TGLPanel = class(TCustomPanel)
private
{ Private declarations }
DC: HDC;
RC: HGLRC;
procedure initDC;
procedure initGL;
procedure PreparePixelFormat(var DC: HDC); protected
{ Protected declarations }
FOnPaint:TNotifyEvent;
FOnInit:TNotifyEvent;
FOnPreInit:TNotifyEvent;
FOnResize:TNotifyEvent; procedure Paint;override;
procedure Resize;override;
procedure WMDestroy(var Msg: TWMDestroy);message WM_DESTROY;
procedure WMCreate(var Msg:TWMCreate); message WM_CREATE; public
{ Public declarations }
constructor Create(Owner:TComponent);override; published
{ Published declarations } property Alignment;
property Align;
property DragCursor;
property DragMode;
property Enabled;
property ParentFont;
property ParentShowHint;
property PopupMenu;
property ShowHint;
property TabOrder;
property TabStop;
property Visible; property OnClick;
property OnDblClick;
property OnDragDrop;
property OnDragOver;
property OnEndDrag;
property OnEnter;
property OnExit;
property OnMouseDown;
property OnMouseMove;
property OnMouseUp;
property OnStartDrag; property OnInit:TNotifyEvent read FOnInit write FOnInit;
property OnPreInit:TNotifyEvent read FOnPreInit write FOnPreInit; property OnResize:TNotifyEvent read FOnResize write FOnResize;
property OnPaint:TNotifyEvent read FOnPaint write FOnPaint; end; procedure Register; implementation procedure Register;
begin
RegisterComponents('Samples', [TGLPanel]);
end;
//---------------------------------------------
constructor TGLPanel.Create;
begin
inherited;
end;
//---------------------------------------------
procedure TGLPanel.WMDestroy(var Msg: TWMDestroy);
begin
wglMakeCurrent(0, 0);
wglDeleteContext(RC);
ReleaseDC(Handle, DC);
end;
//---------------------------------------------------
procedure TGLPanel.initDC;
begin
DC := GetDC(Handle);
PreparePixelFormat(DC);
end;
//---------------------------------------------------
procedure TGLPanel.initGL;
begin
glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_PROJECTION);
glLoadIdentity;
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glOrtho(-1, 1, -1, 1, -1, 50);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity;
glEnable(GL_DEPTH_TEST);
//注意下面這一行是?了做練習程式時可以直接用glColor指定材質而加的,
// 可能使得光照或表面材質發生意想不到的變化,
// 如果不需要可以去掉或在程式中用glDisable(GL_COLOR_MATERIAL);關閉
glEnable(GL_COLOR_MATERIAL);
glShadeModel(GL_SMOOTH);
gluLookAt(2, 4, 6, 0, 0, 0, 0, 1, 0);
SwapBuffers(DC);
end;
//---------------------------------------------
procedure TGLPanel.PreparePixelFormat(var DC: HDC);
var
PFD : TPixelFormatDescriptor;
ChosenPixelFormat : Integer;
begin
FillChar(PFD, SizeOf(TPixelFormatDescriptor), 0); with PFD do
begin
nSize := SizeOf(TPixelFormatDescriptor);
nVersion := 1;
dwFlags := PFD_DRAW_TO_WINDOW or
PFD_SUPPORT_OPENGL or
PFD_DOUBLEBUFFER;
iPixelType := PFD_TYPE_RGBA;
cColorBits := 16; // 16位?色
cDepthBits := 32; // 32位深度緩衝
iLayerType := PFD_MAIN_PLANE;
{ Should be 24, but we must allow for the clunky WKU boxes }
end; ChosenPixelFormat := ChoosePixelFormat(DC, @PFD);
if ChosenPixelFormat = 0 then
Raise Exception.Create('ChoosePixelFormat failed!');
SetPixelFormat(DC, ChosenPixelFormat, @PFD);
end; procedure TGLPanel.WMCreate(var Msg:TWMCreate);
begin
//在這裏做初始化工作
//修改DC的象素格式,使之支援OpenGL繪製
initDC;
RC := wglCreateContext(DC);
wglMakeCurrent(DC, RC);
//初始化GL繪製系統
initGL;
if Assigned(FOnInit) then
begin
if (wglMakeCurrent(DC,RC)=false) then
ShowMessage('wglMakeCurrent:' IntToStr(GetLastError));
FOnInit(self);
end;
end;
//---------------------------------------------------------------------------
procedure TGLPanel.Paint;
begin
//TCustomPanel::Paint();
if Assigned(FOnPaint) then
begin
wglMakeCurrent(DC,RC);
FOnPaint(self);
SwapBuffers(DC);
end;
end;
//---------------------------------------------------------------------------
procedure TGLPanel.Resize;
begin
inherited;
if Assigned(FOnResize) then
begin
wglMakeCurrent(DC,RC);
glViewport(0,0,ClientWidth,ClientHeight);
FOnResize(self);
end;
end;
end. 以上代碼僅用來說明原理及建立一個基本的練習環境,您可以自由使用,轉載請注明出處。如果使用從本人主頁下載的TGLPanel請遵守內附使用說明的版權申明。如果實際做東西,建議使用Mike Lischke的GLScene控制項組(http://www.lischke-online.de/)。
------
********************************************************** 哈哈&兵燹 最會的2大絕招 這個不會與那個也不會 哈哈哈 粉好 Delphi K.Top的K.Top分兩個字解釋Top代表尖端的意思,希望本討論區能提供Delphi的尖端新知 K.表Knowlege 知識,就是本站的標語:Open our mind |
本站聲明 |
1. 本論壇為無營利行為之開放平台,所有文章都是由網友自行張貼,如牽涉到法律糾紛一切與本站無關。 2. 假如網友發表之內容涉及侵權,而損及您的利益,請立即通知版主刪除。 3. 請勿批評中華民國元首及政府或批評各政黨,是藍是綠本站無權干涉,但這裡不是政治性論壇! |