<form id="hz9zz"></form>
  • <form id="hz9zz"></form>

      <nobr id="hz9zz"></nobr>

      <form id="hz9zz"></form>

    1. 明輝手游網中心:是一個免費提供流行視頻軟件教程、在線學習分享的學習平臺!

      用Delphi做一個OpenGL控件

      [摘要]OpenGL是一個獨立于窗口的圖形庫,而圖形最終是在窗口系統里繪制出來的,那么OpenGL的繪圖命令是怎么在窗口里生成輸出的呢?   這就是各個系統上的OpenGL實現者需要做的工作了。在Windo...
      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網站 峁┑畝嗍菔境絳蚨夾枰褂盟。同书]馓卓庖丫灰浦駁蕉嘀制教ㄉ,所以要矢[胗眉虻サ姆椒ǹ⒃趙indows/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/)。

        end

        else

        刪除注冊表項....................... end;初始化擴展是通過IShellExtInit實現的,當外殼調用IShellExtInit.Initialize時,它傳遞一個數據對象包含來文件對應的目錄的PIDL標識符。Initialize方法需要從數據對象中提取文件名,并把文件名和PIDL標識符保存起來為了以后使用。

        

        function TCXPropSheet.SEIInitialize(pidlFolder: PItemIDList;

          lpdobj: IDataObject; hKeyProgID: HKEY): HResult;

        var

          StgMedium: TStgMedium;

          FormatEtc: TFormatEtc;

          szFile: array[0..MAX_PATH+1]of Char;

          filecount: integer;begin

          Result:=E_FAIL;

        if(lpdobj=nil)then

        begin

          Result:=E_INVALIDARG;

          messagebox(0, ’1’, ’錯誤’, mb_ok);

          Exit;

        end;

        with FormatEtc do

        begin

          cfFormat:=CF_HDROP;

          ptd:=nil;

          dwAspect:=DVASPECT_CONTENT;

          lindex:=-1;

          tymed:=TYMED_HGLOBAL;

        end;

        Result:=lpdobj.GetData(FormatEtc, StgMedium);

        if Failed(Result)then

        Exit;

        //如果只有一個文件被選中,獲得文件名并保存。

        filecount:=DragQueryFile(stgmedium.hGlobal, $FFFFFFFF, nil, 0);

        if filecount=1 then

        begin

          Result:=NOERROR;

          DragQueryFile(stgmedium.hGlobal, 0, szFile, SizeOf(szFile));

          FFilename:=strpas(szFile);

        end;

        ReleaseStgMedium(StgMedium);end;添加頁面的操作是通過IShellPropSheetExt接口來實現的。如果屬性頁是和文件相關聯,外殼會調用IShellPropSheetExt.AddPages給屬性頁添加一個頁面。如果屬性頁同控制面板程序相關聯,外殼調用IShellPropSheetExt.ReplacePage來替換頁面。

        IShellPropSheetExt.AddPages方法有兩個參數,lpfnAddPage是一個指向AddPropSheetPageProc回調函數的指針,回調函數用來提供要添加的頁面信息給外殼。lParam是一個用戶自定義的值,這里我們用它來返回給回調函數對象。

        一般的IShellPropSheetExt.AddPages方法實現步驟是:

        給PROPSHEETPAGE結構設定正確的值,特別是:

        把擴展的對象引用記數變量付值給pcRefParent成員,這可以防止頁面還在顯示時,擴展對象被卸載。

        實現PropSheetPageProc回調函數來處理頁面創建和銷毀的情況。

        調用CreatePropertySheetPage函數來創建頁面。

        調用lpfnAddPage指向的函數來來添加創建好的頁面。

        function TCXPropSheet.AddPages(lpfnAddPage: TFNADDPROPSHEETPAGE;

        lParam: LPARAM): HResult;var

        PSP: TPropSheetPage;

        HPSP: HPropSheetPage;begin

        result:=E_FAIL;

        try

        psp.dwSize:=SizeOf(psp);

        psp.dwFlags:=PSP_USEREFPARENT or PSP_USETITLE or PSP_USECALLBACK;

        psp.hInstance:=hInstance;

        //這里我們使用了事先儲存在wave.res中的對話框模板,模板是用delphi5自帶的

        //resource workshop編輯的,使用delphi5\bin\brcc32.exe編譯的。

        psp.pszTemplate:=MakeIntResource(100);

        //標題名

        psp.pszTitle:=’波文件信息’;

        //設定回調函數

        psp.pfnDlgProc:=@DialogProc;

        psp.pfnCallBack:=@PropCallback;

        //設定對象引用記數變量

        psp.pcRefParent:=@comserver.objectcount;

        //用lParam向回調函數傳遞對象

        psp.lParam:=integer(self);

        HPSP:=CreatePropertySheetPage(psp);

        if HPSP$#@60;$#@62;nil then begin

        if not lpfnAddPage(HPSP, lParam)then begin

        DestroyPropertySheetPage(HPSP);

        end else begin

        _addref;//增加引用記數,否則一脫離這個方法的作用域,delphi自動釋放對象。

        result:=S_OK;

        end

        end

        except

        on e: exception do begin

        e.message:=’添加頁面’+e.message;

        messagebox(0, pchar(e.message), ’錯誤’, mb_ok);

        end;

        end;end;

        function TCXPropSheet.ReplacePage(uPageID: UINT;

        lpfnReplaceWith: TFNADDPROPSHEETPAGE; lParam: LPARAM): HResult;begin

        Result:=E_NOTIMPL;//同文件關聯時,外殼不調用ReplacePage,所以不用實現end;回調函數處理屬性頁的消息,主要要響應WM_INITDIALOG消息來初始化頁面顯示信息,響應WM_COMMAND消息來處理用戶交互,響應WM_NOTIFY消息來處理頁面切換或關閉后處理操作結果。

        

        function DialogProc(hwndDlg: HWnd; Msg: UINT; wParam: wParam;

        lParam: LPARAM): Bool; stdcall;

        var

          PageObj: TCXPropSheet;

          filename: string;

          displayName : string;

          SheetHWnd: HWnd;

        begin

          result:=false;

          try

          if Msg=WM_INITDIALOG then begin//初始化界面

        //獲得lparam傳遞過來的對象

          pageObj:=TCXPropSheet(PPropSheetPage(lParam)^.lParam);

        //保存對象信息

          SetWindowLong(hwndDlg, DWL_USER, integer(pageObj));

        //設置界面顯示波文件信息

          SetDlgItemText(hwndDlg, 100, PChar(ExtractFileName(PageObj.FFileName)));

          OpenMedia(PageObj.FFileName);

        SetDlgItemText(hwndDlg, 101, PChar(IntToStr(GetWavStatus(MCI_WAVE_STATUS_AVGBYTESPERSEC))));

        SetDlgItemText(hwndDlg, 102, PChar(IntToStr(GetWavStatus(MCI_WAVE_STATUS_BITSPERSAMPLE))));

        SetDlgItemText(hwndDlg, 103, PChar(IntToStr(GetWavStatus(MCI_WAVE_STATUS_CHANNELS))));

        CloseMedia;

          SetWindowLong(hwndDlg, DWL_MSGRESULT, 0);

          Result:=TRUE;

        end

        else if(Msg=WM_COMMAND)then begin

        if Lo(wParam)=110 then//用戶點擊了關于按鈕(id=110)

          MessageBox(0,’作者:hubdog’+#13#10+’email:hubdog@263.net’,’關于...’,MB_OK);

        end else if(msg=WM_NOTIFY)then begin

          sheetHwnd:=getparent(hwndDlg);//獲得屬性頁的窗口句柄

          case PNMHdr(lparam)^.code of

        //頁面失去焦點

          PSN_KILLACTIVE:

        begin

          SetWindowLong(hwndDlg, DWL_MSGRESULT, 0);

          Result:=TRUE;

        end;

        end;

        end;

          except

          on e: exception do begin

          e.message:=’回調處理’+e.message;

          messagebox(0, pchar(e.message), ’錯誤’, mb_ok);

        end;

        end;

        end;

        

        建立同驅動器相關聯的屬性頁擴展用

        同上面講的有兩點不同:

        IShellExtInit.Initialize方法傳遞過來的數據對象包含的驅動器路徑可能是CFSTR_MOUNTEDVOLUME格式而不是CF_HDROP格式的。標準驅動器是CF_HDROP格式的,而在NTFS文件系統中映射的遠程設備則是CFSTR_MOUNTEDVOLUME格式的。

        注冊表項是HKEY_CLASSES_ROOT\Drive\Shellex\PropertySheetHandlers子鍵。

        建立控制面板屬性頁擴展

        同上面講的有兩點不同:

        控制面板程序調用IShellPropSheetExt.ReplacePage方法來替換頁面,它不調用IShellPropSheetExt。AddPages方法。

        注冊方式:子鍵可以在不同位置創建,這依賴于擴展是針對用戶還是針對機器的。對用戶方式子鍵是HKEY_CURRENT_USER\REGSTR_PATH_CONTROLPANEL,否則子鍵是HKEY_LOCAL_MACHINE\REGSTR_PATH_CONTROLSFOLDER。

        本程序在Delphi5,Win NT 4.0,K6-233系統下調試成功。例子程序可以到http://chaozhi.com/lgc去下載




      日韩精品一区二区三区高清