NeHe的OpenGL教程5(Bang翻译Delphi版)-如何绘制立方体

NeHe的OpenGL教程5(Bang翻译Delphi版)-如何绘制立方体

在这一课里,我们把三角形变为立体的金子塔形状,把四边形变为立方体,如下图:

NeHe的OpenGL教程5(Bang翻译Delphi版)-如何绘制立方体

program lesson5a;

{
    OpenGL DelphiXE
    出处:根据NeHe代码翻译而来(
http://nehe.gamedev.net/)
    作者:帅宏军
shuaihj@163.com
}

uses
  Windows,
  Messages,
  OpenGL;

// 全局变量
var
  h_Rc: HGLRC;                    // 窗口着色描述表句柄
  h_Dc: HDC;                        // OpenGL渲染描述表句柄
  h_Wnd: HWND;                      // 保存我们的窗口句柄
  keys: array [0..255] of BOOL;     // 保存键盘按键的数组
  Active: bool = true;              // 窗口的活动标志,缺省为TRUE
  FullScreen:bool = true;           // P全屏标志缺省,缺省设定成全屏模式

  rtri: GLfloat;  // 用于三角形的角度
  rquad: GLfloat; // 用于四边形的角度


// 重置OpenGL窗口大小
procedure ReSizeGLScene(Width: GLsizei; Height: GLsizei);
begin
  if (Height=0) then                                    // 防止被零除
    Height:=1;                                            // 将Height设为1

  glViewport(0, 0, Width, Height);                        // 重置当前的视口(和窗口大小相当)

  glMatrixMode(GL_PROJECTION);                            // 指定"投影矩阵堆栈"是下一个矩阵操作的目标
  glLoadIdentity();                                       // 重置当前指定的矩阵为单位矩阵

  gluPerspective(45.0,Width/Height,0.1,100.0);            // 设置视口的大小

  glMatrixMode(GL_MODELVIEW);                             // 指定"模型视图矩阵堆栈"是下一个矩阵操作的目标
  glLoadIdentity;                                         // 重置当前指定的矩阵为单位矩阵
end;

// 初始化OpenGL所有设置
function InitGL:bool;
begin
  glShadeModel(GL_SMOOTH);                       // 采用光滑着色(绘制指定两点间其他点颜色时使用过渡色)

  glClearColor(0.0, 0.0, 0.0, 0.5);                // 指定清除颜色缓存时使用的颜色值(黑色,0.5透明)

  glClearDepth(1.0);                            // 指定清除深度缓存时使用的深度值
  glEnable(GL_DEPTH_TEST);                       // 启用深度测试,对深度缓存中符合"深度比较算法"要求的像素进行重绘
  glDepthFunc(GL_LEQUAL);                        // 指定"深度比较算法"

  glHint(GL_PERSPECTIVE_CORRECTION_HINT,GL_NICEST); // 告诉系统对透视进行最高质量修正

  Result:=true;
end;

// 绘制OpenGL场景(任何您所想在屏幕上显示的东东都将在此段代码中出现)
function DrawGLScene():bool;
begin
  glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);  // 根据glClearColor和glClearDepth指定的值清除颜色和深度缓存
  glLoadIdentity();                                     // 重置当前指定的矩阵为单位矩阵

  //你自己的OpengGL绘制代码
  //当您调用glLoadIdentity()之后,您实际上将当前点移到了屏幕中心,
  //X坐标轴从左至右,Y坐标轴从下至上,Z坐标轴从里至外。
  //OpenGL屏幕中心的坐标值是X和Y轴上的0.0f点。
  //中心左面的坐标值是负值,右面是正值。移向屏幕顶端是正值,移向屏幕底端是负值。移入屏幕深处是负值,移出屏幕则是正值。
  glTranslatef(-1.5,0.0,-6.0); // 左移 1.5 单位,并移入屏幕 6.0

  glRotatef(rtri,0.0,1.0,0.0); // 绕Y轴旋转金字塔

  glBegin(GL_TRIANGLES); // 开始绘制金字塔的各个面

  //下面的代码创建一个绕者其中心轴旋转的金字塔。金字塔的上顶点高出原点一个单位,底面中心低于原点一个单位。上顶点在底面的投影位于底面的中心。
  //注意所有的面-三角形都是逆时针次序绘制的。这点十分重要,在以后的课程中我会作出解释。现在,您只需明白要么都逆时针,要么都顺时针,但永远不要将两种次序混在一起,除非您有足够的理由必须这么做。
  //我们开始画金字塔的前侧面。因为所有的面都共享上顶点,我们将这点在所有的三角形中都设置为红色。底边上的两个顶点的颜色则是互斥的。前侧面的左下顶点是绿色的,右下顶点是蓝色的。这样相邻右侧面的左下顶点是蓝色的,右下顶点是绿色的。这样四边形的底面上的点的颜色都是间隔排列的。
  glColor3f(1.0,0.0,0.0); // 红色
  glVertex3f( 0.0, 1.0, 0.0); // 三角形的上顶点 (前侧面)
  glColor3f(0.0,1.0,0.0); // 绿色
  glVertex3f(-1.0,-1.0, 1.0); // 三角形的左下顶点 (前侧面)
  glColor3f(0.0,0.0,1.0); // 蓝色
  glVertex3f( 1.0,-1.0, 1.0); // 三角形的右下顶点 (前侧面)

  //现在绘制右侧面。注意其底边上的两个顶点的X坐标位于中心右侧的一个单位处。顶点则位于Y轴上的一单位处,且Z坐标正好处于底边的两顶点的Z坐标中心。右侧面从上顶点开始向外侧倾斜至底边上。
  //这次的左下顶点用蓝色绘制,以保持与前侧面的右下顶点的一致。蓝色将从这个角向金字塔的前侧面和右侧面扩展并与其他颜色混合。
  //还应注意到后面的三个侧面和前侧面处于同一个glBegin(GL_TRIANGLES) 和 glEnd()语句中间。因为我们是通过三角形来构造这个金字塔的。OpenGL知道每三个点构成一个三角形。当它画完一个三角形之后,如果还有余下的点出现,它就以为新的三角形要开始绘制了。OpenGL在这里并不会将四点画成一个四边形,而是假定新的三角形开始了。所以千万不要无意中增加任何多余的点。
  glColor3f(1.0,0.0,0.0); // 红色
  glVertex3f( 0.0, 1.0, 0.0); // 三角形的上顶点 (右侧面)
  glColor3f(0.0,0.0,1.0); // 蓝色
  glVertex3f( 1.0,-1.0, 1.0); // 三角形的左下顶点 (右侧面)
  glColor3f(0.0,1.0,0.0); // 绿色
  glVertex3f( 1.0,-1.0, -1.0); // 三角形的右下顶点 (右侧面)

  //现在是后侧面。再次切换颜色。左下顶点又回到绿色,因为后侧面与右侧面共享这个角。
  glColor3f(1.0,0.0,0.0); // 红色
  glVertex3f( 0.0, 1.0, 0.0); // 三角形的上顶点 (后侧面)
  glColor3f(0.0,1.0,0.0); // 绿色
  glVertex3f( 1.0,-1.0, -1.0); // 三角形的左下顶点 (后侧面)
  glColor3f(0.0,0.0,1.0); // 蓝色
  glVertex3f(-1.0,-1.0, -1.0); // 三角形的右下顶点 (后侧面)

  //最后画左侧面。又要切换颜色。左下顶点是蓝色,与后侧面的右下顶点相同。右下顶点是蓝色,与前侧面的左下顶点相同。
  //到这里金字塔就画完了。因为金字塔只绕着Y轴旋转,我们永远都看不见底面,因而没有必要添加底面。如果您觉得有经验了,尝试增加底面(正方形),并将金字塔绕X轴旋转来看看您是否作对了。确保底面四个顶点的颜色与侧面的颜色相匹配。
  glColor3f(1.0,0.0,0.0); // 红色
  glVertex3f( 0.0, 1.0, 0.0); // 三角形的上顶点 (左侧面)
  glColor3f(0.0,0.0,1.0); // 蓝色
  glVertex3f(-1.0,-1.0,-1.0); // 三角形的左下顶点 (左侧面)
  glColor3f(0.0,1.0,0.0); // 绿色
  glVertex3f(-1.0,-1.0, 1.0); // 三角形的右下顶点 (左侧面)
  glEnd(); // 金字塔绘制结束


  glLoadIdentity(); // 重置模型观察矩阵

  //注意到这次我们将立方体移地更远离屏幕了。因为立方体的大小要比金字塔大,同样移入6个单位时,立方体看起来要大的多。这是透视的缘故。越远的对象看起来越小。
  glTranslatef(1.5,0.0,-7.0); // 右移3单位

  glRotatef(rquad,1.0,0.0,0.0); // 绕X轴旋转立方体

  glBegin(GL_QUADS); // 开始绘制立方体

  //先画立方体的顶面。从中心上移一单位,注意Y坐标始终为一单位,表示这个四边形与Z轴平行。先画右上顶点,向右一单位,再屏幕向里一单位。然后左上顶点,向左一单位,再屏幕向里一单位。然后是靠近观察者的左下和右下顶点。就是屏幕往外一单位。
  glColor3f(0.0,1.0,0.0); // 颜色改为蓝色
  glVertex3f( 1.0, 1.0,-1.0); // 四边形的右上顶点 (顶面)
  glVertex3f(-1.0, 1.0,-1.0); // 四边形的左上顶点 (顶面)
  glVertex3f(-1.0, 1.0, 1.0); // 四边形的左下顶点 (顶面)
  glVertex3f( 1.0, 1.0, 1.0); // 四边形的右下顶点 (顶面)

  //底面的画法和顶面十分类似。只是Y坐标变成了-1。如果我们从立方体的下面来看立方体的话,您会注意到右上角离观察者最近,因此我们先画离观察者最近的顶点。然后是左上顶点最后才是屏幕里面的左下和右下顶点。
  //如果您真的不在乎绘制多边形的次序(顺时针或者逆时针)的话,您可以直接拷贝顶面的代码,将Y坐标从1改成 -1,也能够工作。但一旦您进入象纹理映射这样的领域时,忽略绘制次序会导致十分怪异的结果。
  glColor3f(1.0,0.5,0.0); // 颜色改成橙色
  glVertex3f( 1.0,-1.0, 1.0); // 四边形的右上顶点(底面)
  glVertex3f(-1.0,-1.0, 1.0); // 四边形的左上顶点(底面)
  glVertex3f(-1.0,-1.0,-1.0); // 四边形的左下顶点(底面)
  glVertex3f( 1.0,-1.0,-1.0); // 四边形的右下顶点(底面)

  //接着画立方体的前面。保持Z坐标为一单位,前面正对着我们。
  glColor3f(1.0,0.0,0.0); // 颜色改成红色
  glVertex3f( 1.0, 1.0, 1.0); // 四边形的右上顶点(前面)
  glVertex3f(-1.0, 1.0, 1.0); // 四边形的左上顶点(前面)
  glVertex3f(-1.0,-1.0, 1.0); // 四边形的左下顶点(前面)
  glVertex3f( 1.0,-1.0, 1.0); // 四边形的右下顶点(前面)

  //立方体后面的绘制方法与前面类似。只是位于屏幕的里面。注意Z坐标现在保持 -1 不变。
  glColor3f(1.0,1.0,0.0); // 颜色改成黄色
  glVertex3f( 1.0,-1.0,-1.0); // 四边形的右上顶点(后面)
  glVertex3f(-1.0,-1.0,-1.0); // 四边形的左上顶点(后面)
  glVertex3f(-1.0, 1.0,-1.0); // 四边形的左下顶点(后面)
  glVertex3f( 1.0, 1.0,-1.0); // 四边形的右下顶点(后面)

  //还剩两个面就完成了。您会注意到总有一个坐标保持不变。这一次换成了X坐标。因为我们在画左侧面。
  glColor3f(0.0,0.0,1.0); // 颜色改成蓝色
  glVertex3f(-1.0, 1.0, 1.0); // 四边形的右上顶点(左面)
  glVertex3f(-1.0, 1.0,-1.0); // 四边形的左上顶点(左面)
  glVertex3f(-1.0,-1.0,-1.0); // 四边形的左下顶点(左面)
  glVertex3f(-1.0,-1.0, 1.0); // 四边形的右下顶点(左面)

  //立方体的最后一个面了。X坐标保持为一单位。逆时针绘制。您愿意的话,留着这个面不画也可以,这样就是一个盒子:)
  //或者您要是有兴趣可以改变立方体所有顶点的色彩值,象金字塔那样混合颜色。您会看见一个非常漂亮的彩色立方体,各种颜色在它的各个表面流淌。
  glColor3f(1.0,0.0,1.0); // 颜色改成紫罗兰色
  glVertex3f( 1.0, 1.0,-1.0); // 四边形的右上顶点(右面)
  glVertex3f( 1.0, 1.0, 1.0); // 四边形的左上顶点(右面)
  glVertex3f( 1.0,-1.0, 1.0); // 四边形的左下顶点(右面)
  glVertex3f( 1.0,-1.0,-1.0); // 四边形的右下顶点(右面)
  glEnd(); // 立方体绘制结束


  rtri := rtri + 0.2; // 增加三角形的旋转变量
  rquad := rquad + 0.15; // 减少四边形的旋转变量


  Result := true;
end;

//处理所有的窗口消息。当我们注册好窗口类之后,程序跳转到这部分代码处理窗口消息
function WndProc(hWnd: HWND;                            // 窗口的句柄
                 message: UINT;                         // 窗口的消息
                 wParam: WPARAM;                        // 附加的消息内容
                 lParam: LPARAM):                       // 附加的消息内容
                                  LRESULT; stdcall;
var
  scrWidth,scrHeight: integer;
  rect: TRect;
begin
  if message=WM_SYSCOMMAND then                         // 监视系统中断命令
    begin
      case wParam of
        SC_SCREENSAVE,SC_MONITORPOWER:                  // 屏保要运行或显示器要进入节电模式
          begin
            result:=0;                                  // 禁止命令执行
            exit;
          end;
      end;
    end;

  case message of
     WM_CREATE:                                       // 监视构建窗体消息
       begin
         //获得屏幕尺寸
         scrWidth := GetSystemMetrics(SM_CXSCREEN);
         scrHeight := GetSystemMetrics(SM_CYSCREEN);

         //获取窗体尺寸
         GetWindowRect(hWnd,&rect);
         rect.left := (scrWidth-rect.right) DIV 2;
         rect.top := (scrHeight-rect.bottom) DIV 2;

         //设置窗体位置(屏幕居中)
         SetWindowPos(hWnd,HWND_TOP,rect.left,rect.top,rect.right,rect.bottom,SWP_SHOWWINDOW);

         result:=0;                                     // 返回消息循环
       end;
    WM_ACTIVATE:                                        // 监视窗口激活消息
      begin
        if (Hiword(wParam)=0) then                      // 检查最小化状态
          active:=true                                  // 程序处于激活状态
        else
          active:=false;                                // 程序不再激活

        Result:=0;                                      // 返回消息循环
      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:                                            // 监视窗口尺寸改变消息
      begin
       ReSizeGLScene(LOWORD(lParam),HIWORD(lParam));   // 重置OpenGL窗口大小
        result:=0;                                      // 返回消息循环
      end
    else
      // 其余无关的消息被传递给DefWindowProc,让Windows自行处理
      begin
       Result := DefWindowProc(hWnd, message, wParam, lParam);
      end;
    end;
end;

//正常销毁窗口(在程序退出之前调用,依次释放着色描述表,设备描述表和窗口句柄)
procedure KillGLWindow;
begin
  if FullScreen then                                    // 处于全屏模式吗?
    begin
      ChangeDisplaySettings(devmode(nil^),0);           // 回到原始桌面
      showcursor(true);                                 // 显示鼠标指针
    end;

  if h_rc<> 0 then                                      // 拥有OpenGL渲染描述表吗?
    begin
      if (not wglMakeCurrent(h_Dc,0)) then              // 释放DC和RC描述表
        MessageBox(0,'Release of DC and RC failed.',' 关闭错误',MB_OK or MB_ICONERROR);
      if (not wglDeleteContext(h_Rc)) then              // 删除RC
        begin
          MessageBox(0,'释放RC失败.',' 关闭错误',MB_OK or MB_ICONERROR);
          h_Rc:=0;                                      // 将RC设为 NULL
        end;
    end;

  if (h_Dc=1) and (releaseDC(h_Wnd,h_Dc)<>0) then       // 释放 DC
    begin
      MessageBox(0,'释放DC失败.',' S关闭错误',MB_OK or MB_ICONERROR);
      h_Dc:=0;                                          // 将 DC 设为 NULL
    end;

  if (h_Wnd<>0) and (not destroywindow(h_Wnd))then      // 销毁窗口
    begin
      MessageBox(0,'释放窗口句柄失败.',' 关闭错误',MB_OK or MB_ICONERROR);
      h_Wnd:=0;                                         // 将 hWnd 设为 NULL
    end;

  if (not UnregisterClass('OpenGL',hInstance)) then     // 注销类
    begin
      MessageBox(0,'不能注销窗口类.','关闭错误',MB_OK or MB_ICONINFORMATION);
    end;
end;

//创建我们的OpenGL窗口
function CreateGlWindow(title:Pchar; width,height,bits:integer;FullScreenflag:bool):boolean stdcall;
var
  Pixelformat: GLuint;            // 当我们要求Windows为我们寻找相匹配的象素格式时,Windows寻找结束后将模式值保存在变量PixelFormat中
  wc:TWndclass;                   // 窗口类结构
  dwExStyle:dword;                // 扩展窗口风格
  dwStyle:dword;                  // 窗口风格
  pfd: pixelformatdescriptor;     // 象素格式描述
  dmScreenSettings: Devmode;      // 设备模式
  h_Instance:hinst;               // 窗口的实例
  WindowRect: TRect;              // 取得矩形的左上角和右下角的坐标值
begin
  //取得矩形的左上角和右下角的坐标值。
  //我们将使用这些值来调整我们的窗口使得其上的绘图区的大小恰好是我们所需的分辨率的值。
  //通常如果我们创建一个640x480的窗口,窗口的边框会占掉一些分辨率的值。
  WindowRect.Left := 0;                               // 将Left 设为 0
  WindowRect.Top := 0;                                // 将Right 设为要求的宽度
  WindowRect.Right := width;                          // 将Top 设为 0
  WindowRect.Bottom := height;                        // 将Bottom 设为要求的高度

  FullScreen:=FullScreenflag;                         //  设置全局全屏标志

  //我们取得窗口的实例,然后定义窗口类
  h_instance:=GetModuleHandle(nil);                   //  取得我们窗口的实例
  with wc do
    begin
      style := CS_HREDRAW or CS_VREDRAW or CS_OWNDC;  // 移动时重画,并为窗口取得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:=nil;                              // 不需要菜单
      lpszClassName:='OpenGl';                        // 设定类名字
    end;

  //注册窗口类
  if  RegisterClass(wc)=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 := bits;                       // 每象素所选的色彩深度
          dmFields     := DM_BITSPERPEL or DM_PELSWIDTH or DM_PELSHEIGHT;
        end;

      //  尝试设置显示模式并返回结果。注: CDS_FULLSCREEN 移去了状态条。
      if (ChangeDisplaySettings(dmScreenSettings, CDS_FULLSCREEN))<>DISP_CHANGE_SUCCESSFUL THEN
        Begin
          // 若全屏模式失败,提供两个选项:退出或在窗口内运行
          if MessageBox(0,'全屏模式在当前显卡上设置失败!\n使用窗口模式?'
                                             ,'OpenGL',MB_YESNO or MB_ICONEXCLAMATION)= IDYES then
                FullScreen:=false                     // 选择窗口模式(Fullscreen=FALSE)
          else
            begin
              // 弹出一个对话框,告诉用户程序结束
              MessageBox(0,'程序将被关闭.','错误',MB_OK or MB_ICONERROR);
              Result:=false;                          // 退出并返回 FALSE
              exit;
            end;
          end;
    end;

  //由于全屏模式可能失败,用户可能决定在窗口下运行,
  //我们需要在设置屏幕/窗口之前,再次检查fullscreen的值是TRUE或FALSE
  if FullScreen then
    begin
      dwExStyle:=WS_EX_APPWINDOW;                              // 扩展窗体风格(窗体可见时处于最前面)
      dwStyle:=WS_POPUP or WS_CLIPSIBLINGS or WS_CLIPCHILDREN; // 窗体风格(没有边框)
      Showcursor(false);                                       // 隐藏鼠标指针
    end
  else
    begin
      dwExStyle:=WS_EX_APPWINDOW or WS_EX_WINDOWEDGE;                     // 扩展窗体风格(增强窗体的3D感观)
      dwStyle:=WS_OVERLAPPEDWINDOW or WS_CLIPSIBLINGS or WS_CLIPCHILDREN; // 窗体风格(带标题栏、可变大小的边框、菜单和最大化/最小化按钮的窗体)
    end;

  AdjustWindowRectEx(WindowRect,dwStyle,false,dwExStyle); //  调整窗口达到真正要求的大小

  // 开始创建窗口并检查窗口是否成功创建
  H_wnd:=CreateWindowEx(dwExStyle,                    // 扩展窗体风格
                               'OpenGl',              // 类名字
                               Title,                 // 窗口标题
                               dwStyle,               // 窗体风格
                               0,0,                   // 窗口位置
                               WindowRect.Right-WindowRect.Left,  // 计算调整好的窗口宽度
                               WindowRect.Bottom-WindowRect.Top,  // 计算调整好的窗口高度
                               0,                     // 无父窗口
                               0,                     // 无菜单
                               hinstance,             // 窗口实例
                               nil);                  // 不向WM_CREATE传递任何东东

  if h_Wnd=0 then                                     // 窗口是否正常创建
    begin
      KillGlWindow();                                 // 重置显示区
      MessageBox(0,'不能创建一个窗口设备描述表.','错误',MB_OK or MB_ICONEXCLAMATION);
      Result:=false;                                  // 返回 FALSE
      exit;
    end;

  //描述象素格式
  with pfd do
    begin
      nSize:= SizeOf( PIXELFORMATDESCRIPTOR );        // 象素描述符的大小
      nVersion:= 1;                                   // 版本号

      dwFlags:= PFD_DRAW_TO_WINDOW                    // 格式必须支持窗口
        or PFD_SUPPORT_OPENGL                         // 格式必须支持OpenGL
        or PFD_DOUBLEBUFFER;                          // 格式必须支持双缓冲

      iPixelType:= PFD_TYPE_RGBA;                     // 申请 RGBA 格式
      cColorBits:= bits;                              // 选定色彩深度

      cRedBits:= 0;                                   // 忽略的色彩位
      cRedShift:= 0;
      cGreenBits:= 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;                                  // Reserved

      dwLayerMask:= 0;                                // 忽略层遮罩
      dwVisibleMask:= 0;
      dwDamageMask:= 0;
    end;

  //尝试取得OpenGL设备描述表
  h_Dc := GetDC(h_Wnd);
  if h_Dc=0 then
    begin
      KillGLWindow();                                 // 重置显示区
      MessageBox(0,'不能创建一种相匹配的像素格式.','错误',MB_OK or MB_ICONEXCLAMATION);
      Result:=false;                                  // 返回 FALSE
      exit;
    end;

  //找到对应与此前我们选定的象素格式的象素格式
  PixelFormat := ChoosePixelFormat(h_Dc, @pfd);
  if (PixelFormat=0) then
    begin
      KillGLWindow();                                 // 重置显示区
      MessageBox(0,'不能找到像素格式.','错误',MB_OK or MB_ICONEXCLAMATION);
      Result:=false;                                  // 返回 FALSE
      exit;
    end;

  //尝试设置象素格式
  if (not SetPixelFormat(h_Dc,PixelFormat,@pfd)) then
    begin
      KillGLWindow();                                 // 重置显示区
      MessageBox(0,'不能设置像素格式.','错误',MB_OK or MB_ICONEXCLAMATION);
      Result:=false;                                  // 返回 FALSE
      exit;
    end;

  //尝试取得着色描述表
  h_Rc := wglCreateContext(h_Dc);                     // 尝试取得着色描述表
  if (h_Rc=0) then
    begin
      KillGLWindow();                                 // 重置显示区
      MessageBox(0,'不能创建OpenGL渲染描述表.','错误',MB_OK or MB_ICONEXCLAMATION);
      Result:=false;                                  // 返回 FALSE
      exit;
    end;

  //激活着色描述表
  if (not wglMakeCurrent(h_Dc, h_Rc)) then
    begin
      KillGLWindow();                                 // 重置显示区
      MessageBox(0,'不能激活当前的OpenGL渲然描述表.','错误',MB_OK or MB_ICONEXCLAMATION);
      Result:=false;                                  // 返回 FALSE
      exit;
    end;

  //显示创建完成的OpenGL窗口
  ShowWindow(h_Wnd,SW_SHOW);                          // 显示创建完成的OpenGL窗口
  SetForegroundWindow(h_Wnd);                         // 设为前端窗口(给它更高的优先级)
  SetFocus(h_Wnd);                                    // 并将焦点移至此窗口
  ReSizeGLScene(width,height);                        // 设置透视 GL 屏幕

  //初始化OpenGL(设置光照、纹理、等等任何需要设置的东东)
  if (not InitGl()) then                              // 初始化OpenGL
    begin
      KillGLWindow();                                 // 重置显示区
      MessageBox(0,'初始化失败.','错误',MB_OK or MB_ICONEXCLAMATION);
      Result:=false;                                  // 返回 FALSE
      exit;
    end;

  Result:=true;                                       // 成功
end;

//Windows程序的入口(调用窗口创建例程,处理窗口消息,并监视人机交互)
function WinMain(hInstance: HINST;                    // 当前窗口实例
   hPrevInstance: HINST;                            // 前一个窗口实例
   lpCmdLine: PChar;                                // 命令行参数
   nCmdShow: integer):                              // 窗口显示状态
                        integer; stdcall;
var
  msg: TMsg;                                          // 用来检查是否有消息等待处理
  done: Bool;                                         // 用来检查否完程序完成运行
begin
  done:=false;  //用来退出循环的Bool 变量

  // 选择窗口是否全屏
  if MessageBox(0,'你想在全屏模式下运行么?','全屏',
                             MB_YESNO or MB_ICONQUESTION)=IDNO then
    FullScreen:=false
  else
    FullScreen:=true;

  // 创建OpenGL窗口
  if not CreateGLWindow('我的OpenGL 框架',640,480,16,FullScreen) then
    begin
      Result := 0;                                    // 失败退出
      exit;
    end;

  // 下面是消息循环的开始。只要done保持FALSE,循环一直进行
  while not done do
    begin
      if (PeekMessage(msg, 0, 0, 0, PM_REMOVE)) then  // 检查是否有消息在等待
        begin
          if msg.message=WM_QUIT then                 // 收到退出消息?
            done:=true
          else                                        // 如果不是退出消息,我们翻译消息,然后发送消息(使得WndProc() 或 Windows能够处理他们)
            begin
           TranslateMessage(msg);                    // 翻译消息
           DispatchMessage(msg);                     // 发送消息
         end;
        end
      else
        begin
          // J如果没有消息,绘制我们的OpenGL场景失败,或者ESC 发出退出信号直接退出程序
          if (active and not(DrawGLScene()) or keys[VK_ESCAPE]) then
            done:=true
          else
            SwapBuffers(h_Dc);                        // 交换缓存 (双缓存)

          if keys[VK_F1] then                         // 允许用户按下F1键在全屏模式和窗口模式间切换
            begin
              Keys[VK_F1] := false;                     // 若是,使对应的Key数组中的值为 FALSE
              KillGLWindow();                           // 销毁当前的窗口
              FullScreen := not FullScreen;             // 切换 全屏 / 窗口 模式

              // 重建 OpenGL 窗口
              CreateGLWindow('我的OpenGL 框架',640,480,16,fullscreen);
            end;
        end;
    end;

  // 关闭程序
  killGLwindow();                                     // 销毁窗口
  result:=msg.wParam;                                 // 退出程序
end;

begin
  WinMain(hInstance, HPrevInst, CmdLine, CmdShow);   // 程序开始运行
end.

 

上一篇:《OpenGL编程指南》一2.5 着色器的编译


下一篇:《OpenGL编程指南(原书第9版)》——第2章 2.0着色器基础