ITPub博客

首页 > Linux操作系统 > Linux操作系统 > NeHe的opengl教程delphi版(1)----基本框架 (转)

NeHe的opengl教程delphi版(1)----基本框架 (转)

原创 Linux操作系统 作者:gugu99 时间:2007-08-16 09:54:34 0 删除 编辑
NeHe的opengl教程delphi版(1)----基本框架 (转)[@more@]

将CKER翻译的NeHe的vc 的OPENGL框架转成了Delphi版,
希望对用Delphi学习OPENGL的兄弟有所帮助,
不知为什么,我的Delphi环境下无法直接运行,但是在别的机器上好像没问题
我的机器只能编译后运行EXE文件。
感谢NeHe提供的这么好的框架,感谢CKER翻译的VC的资料

 

Program Project1;

Uses
  opengl,
  windows,
  Messages;

Const
  WND_TITLE  = 'OPenGl 基本框架'; //标题
Var
  //===========================================================================
  // 每一个OpenGL都被连接到一个着色描述表上。着色描述表将所有的OpenGL调用命令连
  // 接到Device Context(设备描述表)上,将OpenGL的着色描述表定义为hRC ,要让程序能
  // 够绘制窗口的话,还需要创建一个设备描述表,Windows的设备描述表被定义为 hDC,
  // DC将窗口连接到GDI(Graphics Device Interface图形设备接口)。而RC将OpenGL连接
  // 到DC。
  //===========================================================================
  h_RC  : HGLRC;  // Rendering Context(着色描述表)。
  h_DC  : HDC;  // Device Context(设备描述表)
  h_Wnd  : HWND;  // 窗口句柄
  h_Instance  : HINST;  // 程序Instance(实例)。
  keys  : Array[0..255] Of Boolean; // 用于键盘例程的数组
  {$R *.res}

  //==============================================================================
  //重新设置OpenGL场景的大小,而不管窗口的大小是否已经改变(假定没有使用全屏模式)。
  //甚至无法改变窗口的大小时(例如在全屏模式下),它至少仍将运行一次————————
  //在程序开始时设置透视图。OpenGL场景的尺寸将被设置成它显示时所在窗口的大小。
  //==============================================================================

Procedure glResizeWnd(Width, Height: Integer); // 重置并初始化GL窗口大小
Begin
  If (Height = 0) Then  // 防止高度为0,产生除0异常
  Height := 1;
  glViewport(0, 0, Width, Height);  // 重置当前的视口(Viewport)

  //下面几行为透视图设置屏幕。意味着越远的东西看起来越小。这么做创建了一个现实
  //外观的场景。此处透视按照基于窗口宽度和高度的45度视角来计算。0.1f,100.0f是
  //我们在场景中所能绘制深度的起点和终点。
  //glMatrixMode(GL_PROJECTION)指明接下来的两行代码将影响projection matrix(投影矩阵)。
  //投影矩阵负责为我们的场景增加透视。
  //glLoadidentity()近似于重置。它将所选的矩阵状态恢复成其原始状态。
  //调用 glLoadIdentity()之后我们为场景设置透视图。

  glMatrixMode(GL_PROJECTION);  // 选择投影矩阵
  glLoadIdentity();  // 重置投影矩阵

  gluPerspective(45.0, Width / Height, 0.1, 100.0); // 计算窗口的外观比例

  //glMatrixMode(GL_MODELVIEW)指明任何新的变换将会影响 modelview matrix(模型观察矩阵)。
  //模型观察矩阵中存放了我们的物体讯息。

  glMatrixMode(GL_MODELVIEW);  // 选择模型观察矩阵
  glLoadIdentity();  // 重置模型观察矩阵

  //如果现在还不能理解这些术语的含义,请别着急。
  //只要知道如果想获得一个精彩的透视场景的话,必须这么做。
End;

//==============================================================================
// 对OpenGL进行所有的设置。将设置清除屏幕所用的颜色,打开深度缓存,
// 启用smooth shading(阴影平滑),等等。这个例程直到OpenGL窗口创建之后才会被调用。
// 此过程将有返回值。但此处的初始化没那么复杂,现在还用不着担心这个返回值。
//==============================================================================

Procedure glInit();
Begin

  //设置清除屏幕时所用的颜色。如果对色彩的工作原理不清楚的话,快速解释一下。
  //色彩值的范围从0.0f到1.0f。0.0f代表最黑的情况,1.0f就是最亮的情况。
  //glClearColor 后的第一个参数是Red Intensity(红色分量),第二个是绿色,第三个是蓝色。
  //最大值也是1.0f,代表特定颜色分量的最亮情况。最后一个参数是Alpha值。
  //当它用来清除屏幕的时候,不用关心第四个数字。现在让它为0.0f。
  //通过混合三种原色(红、绿、蓝),可以得到不同的色彩
  //因此,使用glClearColor(0.0f,0.0f,1.0f,0.0f),您蓝色来清除屏幕。
  //如果用 glClearColor(0.5f,0.0f,0.0f,0.0f)的话,将使用中红色来清除屏幕。
  //不是最亮(1.0f),也不是最暗 (0.0f)。要得到白色背景,应该将所有的颜色设成最亮(1.0f)。
  //要黑色背景的话,该将所有的颜色设为最暗(0.0f)。

  glClearColor(0.0, 0.0, 0.0, 0.0);  // 黑色背景

  //阴影平滑通过多边形精细的混合色彩,并对外部光进行平滑。

  glShadeModel(GL_SMOOTH);  // 启用阴影平滑

  //接下来必须做的是关于depth buffer(深度缓存)的。将深度缓存设想为屏幕后面的层。
  //深度缓存不断的对物体进入屏幕内部有多深进行跟踪。本程序其实没有真正使用深度缓存,
  //但几乎所有在屏幕上显示3D场景OpenGL程序都使用深度缓存。它的排序决定那个物体先画。
  //这样您就不会将一个圆形后面的正方形画到圆形上来。深度缓存是OpenGL十分重要的部分。

  glClearDepth(1.0);  // 设置深度缓存
  glEnable(GL_DEPTH_TEST);  // 启用深度测试
  glDepthFunc(GL_LESS);  // 所作深度测试的类型

  //接着告诉OpenGL我们希望进行最好的透视修正。
  //这会十分轻微的影响性能。但使得透视图看起来好一点。

  glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); // 真正精细的透视修正

End;

//==============================================================================
//所有的绘图代码。任何您所想在屏幕上显示的东东都将在此段代码中出现。
//以后的每个程序会在此处增加新的代码。
//==============================================================================

Procedure glDraw();
Begin
  glClear(GL_COLOR_BUFFER_BIT Or GL_DEPTH_BUFFER_BIT); // 清除屏幕和深度缓存
  glLoadIdentity();  // 重置当前的模型观察矩阵
End;

Function WndProc(hWnd: HWND;  // 窗口的句柄
  Msg: UINT;  // 窗口的消息
  wParam: WPARAM;  // 附加的消息内容
  lParam: LPARAM  // 附加的消息内容
  ): LRESULT; stdcall;
Begin
  Result := 0;
  Case (Msg) Of  // 检查Windows消息
  WM_ACTIVATE:  // 监视窗口激活消息
  Begin
  End;
  WM_CREATE:  // 创建
  Begin
  End;
  WM_CLOSE:  // 关闭
  Begin
  PostQuitMessage(0);  // 发出退出消息
  Result := 0
  End;
  WM_KEYDOWN:  // 按键按下
  Begin
  keys[wParam] := True;  // 如果是,设为TRUE
  Result := 0;
  End;
  WM_KEYUP:  // 按键松开
  Begin
  keys[wParam] := False;  // 如果是,设为FALSE
  Result := 0;
  End;
  WM_SIZE:  //调整OpenGL窗口大小
  Begin
  glResizeWnd(LOword(lParam), HIWORD(lParam));  //LoWord=Width,HiWord=Height
  Result := 0;
  End;
  WM_TIMER:  //timers
  Begin
  End;
  Else  //其余的让Windows自行处理。
  Result := DefWindowProc(hWnd, Msg, wParam, lParam);  //向DefWindowProc传递所有未处理的消息。
  End;
End;
//==============================================================================
// 只在程序退出之前调用。作用是依次释放着色描述表,设备描述表和窗口句柄。
// 加入了许多错误检查。如果程序无法销毁窗口的任意部分,都会弹出带相应错误消息的
// 讯息窗口,
//==============================================================================

Procedure glKillWnd(Fullscreen: Boolean);
Begin

  //在KillGLWindow()中所作的第一件事是检查是否处于全屏模式。
  //如果是,要切换回桌面。本应在禁用全屏模式前先销毁窗口,
  //但在某些显卡上这么做可能会使得桌面崩溃。所以还是先禁用全屏模式。
  //这将防止桌面出现崩溃,并在Nvidia和3dfx显卡上都工作的很好!

  If Fullscreen Then  // 处于全屏模式吗?
  Begin
  //  使用ChangeDisplaySettings(NULL,0)回到原始桌面。
  //  将NULL作为第一个参数,
  //  0作为第二个参数传递强制Windows使用当前存放在注册表中的值
  //  (缺省的分辨率、色彩深度、刷新频率,等等)来有效的恢复我原始桌面。
  //  换回桌面后,还要使得鼠标指针重新可见。

  ChangeDisplaySettings(devmode(Nil^), 0); // 是的话,切换回桌面
  ShowCursor(True);  //显示鼠标
  End;

  //是否拥有着色描述表(hRC)。
  If h_RC > 0 Then
  //看我们能否释放它(将 hRC从hDC分开)。
  If (Not wglMakeCurrent(h_DC, 0)) Then
  MessageBox(0, 'DC和RC无法被释放!', '错误', MB_OK Or
  MB_ICONERROR);

  // 能否删除着色描述表
  If (Not wglDeleteContext(h_RC)) Then
  Begin
  MessageBox(0, '删除着色描述表失败!', '错误', MB_OK Or
  MB_ICONERROR);
  h_RC := 0;
  End;

  //是否存在设备描述表,如果有尝试释放它。
  If ((h_DC > 0) And (ReleaseDC(h_Wnd, h_DC) = 0)) Then
  Begin
  MessageBox(0, '释放设备描述表失败!', '错误', MB_OK Or
  MB_ICONERROR);
  h_DC := 0;
  End;

  //是否存在窗口句柄,调用 DestroyWindow( hWnd )来尝试销毁窗口
  If ((h_Wnd <> 0) And (Not DestroyWindow(h_Wnd))) Then
  Begin
  MessageBox(0, '无法销毁窗体!', '错误', MB_OK Or
  MB_ICONERROR);
  h_Wnd := 0;
  End;

  // 注销窗口类
  //这允许我们正常销毁窗口,接着在打开其他窗口时,
  //不会收到诸如"Windows Class already registered"(窗口类已注册)的错误消息。
  If (Not UnRegisterClass('OpenGL', hInstance)) Then
  Begin
  MessageBox(0, '无法注销窗口类!', '错误', MB_OK Or
  MB_ICONERROR);
  hInstance := 0;
  End;
End;

//==============================================================================
// 创建OpenGL窗口,
// 带有5个参数:窗口的标题栏,窗口的宽度,窗口的高度,色彩位数(16/24/32),
// 全屏标志(TRUE --全屏模式, FALSE--窗口模式 )。
// 返回的布尔值 窗口是否成功创建。
//==============================================================================

Function glCreateWnd(Width, Height: Integer; Fullscreen: Boolean; PixelDepth:
  Integer): Boolean;
Var
  wndClass  : TWndClass;  // 窗口类
  dwStyle  : DWORD;  // 窗口风格
  dwExStyle  : DWORD;  // 扩展窗口风格
  PixelFormat  : GLuint;  // 象素格式
  h_Instance  : HINST;  // 当前实例
  dmScreenSettings : DEVMODE;  // 设备模式
  pfd  : TPIXELFORMATDESCRIPTOR; //格式描述符

Begin
  h_Instance := GetModuleHandle(Nil);  // 取得窗口的实例
  ZeroMemory(@wndClass, SizeOf(wndClass)); // 初始化内存
  With wndClass Do  // 设置窗口类
  Begin
  style := CS_HREDRAW Or  // 如果长度变化,
  CS_VREDRAW Or  // 如果高度变化,就是只要变化就强制重画
  CS_OWNDC; //CS_OWNDC为窗口创建一个私有的DC。这意味着DC不能在程序间共享。
  lpfnWndProc := @WndProc;  // WndProc处理消息
  // cbClsExtra := 0;  // 无额外窗口数据
  // cbWndExtra := 0;  // 无额外窗口数据
  hInstance := h_Instance;  // 设置实例
  //hIcon := LoadIcon(0, IDI_WINLOGO); // 装入缺省图标
  hCursor := LoadCursor(0, IDC_ARROW); //载入鼠标指针
  //hbrBackground := 0;  // GL不需要背景
  //lpszMenuName := '';  // 不需要菜单
  lpszClassName := 'OpenGL';  //设定类名
  End;
  If (RegisterClass(wndClass) = 0) Then // 注册窗体类
  Begin
  MessageBox(0, '注册窗体类失败!', '错误', MB_OK Or
  MB_ICONERROR);
  Result := False;
  Exit
  End;

  // 如果需要全屏的话
  If Fullscreen Then
  Begin
  ZeroMemory(@dmScreenSettings, SizeOf(dmScreenSettings)); // 确保内存分配
  With dmScreenSettings Do
  Begin  // 设置屏幕设置的参数
  dmSize := SizeOf(dmScreenSettings); // Devmode 结构的大小
  dmPelsWidth := Width;  // 所选屏幕宽度
  dmPelsHeight := Height;  // 所选屏幕高度
  dmBitsPerPel := PixelDepth; // 每象素所选的色彩深度
  dmFields := DM_PELSWIDTH // 设置初始标志为dmPelsWidth
  Or DM_PELSHEIGHT  // dmPelsHeight 和
  Or DM_BITSPERPEL;  // dmBitsPerPel
  End;

  // 转换为全屏模式 ,
  //切换成与dmScreenSettings所匹配模式。
  //CDS_FULLSCREEN 移去了状态条。
  //并保证在来回切换时,没有移动或改变您在桌面上的窗口。
  // 转换为全屏模式
  If (ChangeDisplaySettings(dmScreenSettings, CDS_FULLSCREEN) =
  DISP_CHANGE_FAILED) Then  //转换失败
  Begin
  MessageBox(0, '不能转换为全屏模式!', '错误', MB_OK
  Or MB_ICONERROR);
  Fullscreen := False;
  End;
  End;

  If (Fullscreen) Then  // 仍在全屏模式下
  Begin
  dwStyle := WS_POPUP Or  // 没有边框
  WS_CLIPCHILDREN // 要让OpenGL正常运行,这两个属性是必须的。
  Or WS_CLIPSIBLINGS;  //他们阻止别的窗体在我们的窗体内/上绘图。
  dwExStyle := WS_EX_APPWINDOW;  // 窗体可见时处于最前面
  ShowCursor(False);  // 不显示鼠标
  End
  Else  //否则
  Begin
  dwStyle := WS_OVERLAPPEDWINDOW Or  //带标题栏、可变大小的边框、菜单和最大/小化按钮
  WS_CLIPCHILDREN Or // 要让OpenGL正常运行,这两个属性是必须的。
  WS_CLIPSIBLINGS;  //他们阻止别的窗体在我们的窗体内/上绘图。
  dwExStyle := WS_EX_APPWINDOW Or // 增强窗体的3D感观
  WS_EX_WINDOWEDGE;  // 边框为凸起
  ShowCursor(false);  // 不显示鼠标
  End;

  //创建一个窗体
  h_Wnd := CreateWindowEx(dwExStyle,  // 扩展窗体风格
  'OpenGL',  // 类名
  WND_TITLE,  // 标题
  dwStyle,  // 窗体属性
  0, 0,  // 窗体位置
  Width, Height,  // 窗体大小
  0,  // 没有父窗体
  0,  // 没有菜单
  h_Instance,  // 实例句柄
  Nil);  // 不向WM_CREATE传递任何东东

  If h_Wnd = 0 Then  // 创建失败,销毁窗体
  Begin
  glKillWnd(Fullscreen);
  MessageBox(0, '不能创建窗体!', '错误', MB_OK Or
  MB_ICONERROR);
  Result := False;
  Exit;
  End;

  // 描述象素格式
  //选择了通过RGBA(红、绿、蓝、alpha通道)支持OpenGL和双缓存的格式。
  //试图找到匹配选定的色彩深度(16位、24位、32位)的象素格式。
  //最后设置16位Z-缓存。
  //其余的参数要么未使用要么不重要
  //(stencil buffer模板缓存和accumulation buffer聚集缓存除外)。
  With pfd Do
  Begin
  nSize := SizeOf(TPIXELFORMATDESCRIPTOR); // 格式描述符大小
  nVersion := 1;  // 版本号
  dwFlags := PFD_DRAW_TO_WINDOW  // 格式必须支持窗口
  Or PFD_SUPPORT_OPENGL  // 格式必须支持OpenGL
  Or PFD_DOUBLEBUFFER;  // 必须支持双缓冲
  iPixelType := PFD_TYPE_RGBA;  // 申请 RGBA 格式
  cColorBits := PixelDepth;  // 选定色彩深度
  cRedbits := 0;  // 忽略的色彩位
  cRedShift := 0;  // 忽略的色彩位
  cGreenBits := 0;  // 忽略的色彩位
  cGreenShift := 0;  // 忽略的色彩位
  cBlueBits := 0;  // 忽略的色彩位
  cBlueShift := 0;  // 忽略的色彩位
  cAlphaBits := 0;  // 无Alpha缓存
  cAlphaShift := 0;  // 忽略Shift Bit
  cAccumBits := 0;  // 无聚集缓存
  cAccumRedBits := 0;  // 忽略聚集位
  cAccumGreenBits := 0;  // 忽略聚集位
  cAccumBlueBits := 0;  // 忽略聚集位
  cAccumAlphaBits := 0;  // 忽略聚集位
  cDepthBits := 16;  // 16位 Z-缓存 (深度缓存)
  cStencilBits := 0;  // 无模板缓存
  cAuxBuffers := 0;  // 无辅助缓存
  iLayerType := PFD_MAIN_PLANE;  // 主绘图层
  bReserved := 0;  // 保留
  dwLayerMask := 0;  // 忽略层遮罩
  dwVisibleMask := 0;  // 忽略层遮罩
  dwDamageMask := 0;  // 忽略层遮罩
  End;
  //得到设备场景描述
  h_DC := GetDC(h_Wnd);
  If (h_DC = 0) Then
  Begin
  glKillWnd(Fullscreen);  // 创建失败,销毁窗体
  MessageBox(0, '不能得到设备场景!', '错误', MB_OK Or
  MB_ICONERROR);
  Result := False;
  Exit;
  End;

  //找到相应的象素格式
  PixelFormat := ChoosePixelFormat(h_DC, @pfd);
  If (PixelFormat = 0) Then
  Begin
  glKillWnd(Fullscreen);
  MessageBox(0, '找不到合适的格式', '错误', MB_OK
  Or MB_ICONERROR);
  Result := False;
  Exit;
  End;

  //设置像素格式.
  If (Not SetPixelFormat(h_DC, PixelFormat, @pfd)) Then
  Begin
  glKillWnd(Fullscreen);
  MessageBox(0, '无法创建渲染格式', '错误', MB_OK Or
  MB_ICONERROR);
  Result := False;
  Exit;
  End;

  //取得着色描述表
  h_RC := wglCreateContext(h_DC);
  If (h_RC = 0) Then
  Begin
  glKillWnd(Fullscreen);
  MessageBox(0, '无法创建OpenGL 绘制描述表', '错误',
  MB_OK Or MB_ICONERROR);
  Result := False;
  Exit;
  End;

  //已经取得了设备描述表和着色描述表。
  //激活着色描述表
  If (Not wglMakeCurrent(h_DC, h_RC)) Then
  Begin
  glKillWnd(Fullscreen);
  MessageBox(0, 'Unable to activate OpenGL rendering context', 'Error',
  MB_OK Or MB_ICONERROR);
  Result := False;
  Exit;
  End;
  //OpenGL窗口已经创建完成

  // 显示窗体,置于最前
  ShowWindow(h_Wnd, SW_SHOW);  // 显示窗口
  SetForegroundWindow(h_Wnd);  // 略略提高优先级
  SetFocus(h_Wnd);  // 设置键盘的焦点至此窗口

  glResizeWnd(Width, Height);  // 设置透视 GL 屏幕
  glInit();  // 初始化新建的GL窗口

  Result := True;
End;

Function WinMain(hInstance: HINST;  //实例
  hPrevInstance: HINST;  // 前一个实例
  lpCmdLine: PChar;  // 命令行参数
  nCmdShow: Integer  // 窗口显示状态
  ): Integer; stdcall;
Var
  msg  : TMsg;  // Windowsx消息结构
  finished  : Boolean;  // 用来退出循环的Bool 变量
Begin
  finished := False;

  //应用程序初始化
  //glCreateWnd,创建窗体为800*600
  If Not glCreateWnd(800, 600, false, 32) Then
  Begin
  Result := 0;
  Exit;
  End;

  While Not finished Do
  Begin
  //检查一个线程消息队列,将所选的范围保存到消息纪录中
  //BOOL PeekMessage(
  //  LPMSG lpMsg,  // 消息记录的指针
  //  HWND hWnd,  // 窗口句柄
  //  UINT wMsgFilteRmin,  // 第一个消息
  //  UINT wMsgFilterMax,  // 最后一个消息
  //  UINT wRemoveMsg    // 标志  Value   Meaning
  //  );  PM_NOREMOVE 处理后保留在消息队列中
  //  PM_REMOVE 处理后从消息队列中清除
  //要做的第一件事是检查是否有消息在等待。
  //使用PeekMessage()可以在不锁住我们的程序的前提下对消息进行检查。
  //许多程序使用GetMessage(),也可以很好的工作。
  //但使用GetMessage(),程序在收到paint消息或其他别的什么窗口消息之前不会做任何事。
  If (PeekMessage(msg, 0, 0, 0, PM_REMOVE)) Then //检查是否有消息
  // wMsgFilterMin,wMsgFilterMax 这两个参数都为0,返回所有可用的消息
  Begin
  If (msg.message = WM_QUIT) Then // 如果是退出消息
  finished := True  //改变循环条件,退出
  Else
  Begin  // 否则处理消息
  // 翻译消息,然后发送消息,使得WndProc() 或 Windows能够处理他们。
  TranslateMessage(msg); //翻译消息
  DispatchMessage(msg); //发送消息
  End;
  End
  Else  //如果没有消息,绘制我们的OpenGL场景。
  Begin
  glDraw();  // 重画屏幕


  SwapBuffers(h_DC);  // 交换缓存 (双缓存)

  If (keys[VK_ESCAPE]) Then // 如果按下了ESC键
  finished := True
  End;
  End;
  glKillWnd(FALSE);  // 释放窗体
  Result := msg.wParam;  // 退出程序
End;

Begin
  WinMain(hInstance, hPrevInst, CmdLine, CmdShow);
End.

 

这段代码的功能是设置一个 OpenGL窗口。它可以只是一个窗口或是全屏幕的、可以任意 大小、任意色彩深度。此处的代码很稳定且很强大,您可以在您所有的OpenGL项目中使用。我会尽量在学习NeHe教程的同时将他翻译成Delphi版的。


来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/10748419/viewspace-960873/ ,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论
  • 博文量
    3121
  • 访问量
    2644389