首页  编辑  

OpenGL演示

Tags: /超级猛料/Picture.图形图像编程/DirectX_OpenGL等/   Date Created:

专栏:Delphi技术 文章编号:977 文章类别: 原创 作者: 品雪 加贴时间:00-6-25 0:10:03

做一个OpenGL控件

关键词:Delphi - 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/)。

2000.6.23