天天看點

NeHe的OpenGL教程3(Bang翻譯Delphi版)-如何給圖形着色

NeHe的OpenGL教程3(Bang翻譯Delphi版)-如何給圖形着色

作為第二課的擴充,我将教你如何使用顔色。你将了解兩種着色模式,在左圖中,三角形用的是光滑着色,四邊形用的是平面着色。如下圖:

NeHe的OpenGL教程3(Bang翻譯Delphi版)-如何給圖形着色

program lesson3a;

{

    OpenGL DelphiXE

}

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全屏标志預設,預設設定成全屏模式

// 重置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;

  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;

// 繪制OpenGL場景(任何您所想在螢幕上顯示的東東都将在此段代碼中出現)

function DrawGLScene():bool;

  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

  //因為每個頂點有不同的顔色,是以看起來顔色從每個角噴出,并剛好在三角形的中心彙合,

  //三種顔色互相混合。這就是平滑着色

  glBegin(GL_TRIANGLES); // 繪制三角形(逆時針畫出來的三角形才是正面朝着我們的)

    glColor3f(1.0,0.0,0.0);     // 設定目前色為紅色

    glVertex3f(-1.0,-1.0, 0.0); // 左下頂點(//glVertex 的第一個參數是X坐标,然後依次是Y坐标和Z坐标)

    glColor3f(0.0,1.0,0.0);     // 設定目前色為綠色

    glVertex3f( 1.0,-1.0, 0.0); // 右下頂點

    glColor3f(0.0,0.0,1.0);     // 設定目前色為藍色

    glVertex3f( 0.0, 1.0, 0.0); // 上頂點

  glEnd(); // 三角形繪制結束

  //在螢幕的左半部分畫完三角形後,我們要移到右半部分來畫正方形。

  //這次右移,是以X坐标值為正值。因為前面左移了1.5個機關,

  //這次要先向右移回螢幕中心(1.5個機關),再向右移動1.5個機關。總共要向右移3.0個機關。

  glTranslatef(3.0,0.0,0.0); // 右移3機關

  glColor3f(0.5,0.5,1.0); // 一次性将目前色設定為藍色

  glBegin(GL_QUADS);      // 繪制正方形(逆時針畫出來的正方形才是正面朝着我們的)

    glVertex3f(-1.0,-1.0, 0.0); // 左下

    glVertex3f( 1.0,-1.0, 0.0); // 右下

    glVertex3f( 1.0, 1.0, 0.0); // 右上

    glVertex3f(-1.0, 1.0, 0.0); // 左上

  glEnd(); // 正方形繪制結束

  Result := true;

//處理所有的視窗消息。當我們注冊好視窗類之後,程式跳轉到這部分代碼處理視窗消息

function WndProc(hWnd: HWND;                            // 視窗的句柄

                 message: UINT;                         // 視窗的消息

                 wParam: WPARAM;                        // 附加的消息内容

                 lParam: LPARAM):                       // 附加的消息内容

                                  LRESULT; stdcall;

  scrWidth,scrHeight: integer;

  rect: TRect;

  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;                                      // 傳回消息循環

    WM_CLOSE:                                           // 監視視窗關閉消息

      Begin

        PostQuitMessage(0);                             // 發出退出消息

        result:=0                                       // 傳回消息循環

    WM_KEYDOWN:                                         // 監視鍵盤有鍵按下消息

        keys[wParam] := TRUE;                           // 如果是,設為TRUE

        result:=0;                                      // 傳回消息循環

    WM_KEYUP:                                           // 監視鍵盤有鍵擡起消息

       keys[wParam] := FALSE;                          // 如果是,設為FALSE

    WM_SIZE:                                            // 監視視窗尺寸改變消息

       ReSizeGLScene(LOWORD(lParam),HIWORD(lParam));   // 重置OpenGL視窗大小

      end

    else

      // 其餘無關的消息被傳遞給DefWindowProc,讓Windows自行處理

       Result := DefWindowProc(hWnd, message, wParam, lParam);

//正常銷毀視窗(在程式退出之前調用,依次釋放着色描述表,裝置描述表和視窗句柄)

procedure KillGLWindow;

  if FullScreen then                                    // 處于全屏模式嗎?

      ChangeDisplaySettings(devmode(nil^),0);           // 回到原始桌面

      showcursor(true);                                 // 顯示滑鼠指針

  if h_rc<> 0 then                                      // 擁有OpenGL渲染描述表嗎?

      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;

  if (h_Dc=1) and (releaseDC(h_Wnd,h_Dc)<>0) then       // 釋放 DC

      MessageBox(0,'釋放DC失敗.',' S關閉錯誤',MB_OK or MB_ICONERROR);

      h_Dc:=0;                                          // 将 DC 設為 NULL

  if (h_Wnd<>0) and (not destroywindow(h_Wnd))then      // 銷毀視窗

      MessageBox(0,'釋放視窗句柄失敗.',' 關閉錯誤',MB_OK or MB_ICONERROR);

      h_Wnd:=0;                                         // 将 hWnd 設為 NULL

  if (not UnregisterClass('OpenGL',hInstance)) then     // 登出類

      MessageBox(0,'不能登出視窗類.','關閉錯誤',MB_OK or MB_ICONINFORMATION);

//建立我們的OpenGL視窗

function CreateGlWindow(title:Pchar; width,height,bits:integer;FullScreenflag:bool):boolean stdcall;

  Pixelformat: GLuint;            // 當我們要求Windows為我們尋找相比對的象素格式時,Windows尋找結束後将模式值儲存在變量PixelFormat中

  wc:TWndclass;                   // 視窗類結構

  dwExStyle:dword;                // 擴充視窗風格

  dwStyle:dword;                  // 視窗風格

  pfd: pixelformatdescriptor;     // 象素格式描述

  dmScreenSettings: Devmode;      // 裝置模式

  h_Instance:hinst;               // 視窗的執行個體

  WindowRect: TRect;              // 取得矩形的左上角和右下角的坐标值

  //取得矩形的左上角和右下角的坐标值。

  //我們将使用這些值來調整我們的視窗使得其上的繪圖區的大小恰好是我們所需的分辨率的值。

  //通常如果我們建立一個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

      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';                        // 設定類名字

  //注冊視窗類

  if  RegisterClass(wc)=0 then                        // 注冊視窗類

      MessageBox(0,'注冊視窗失敗.','錯誤',MB_OK or MB_ICONERROR);

      Result:=false;

      exit;

  //嘗試全屏模式

  if FullScreen then

      //配置設定用于存儲視訊設定的空間;設定螢幕的寬,高,色彩深度

      ZeroMemory( @dmScreenSettings, sizeof(dmScreenSettings) );  // 初始化記憶體

      with dmScreensettings do                        //裝置模式

          dmSize := sizeof(dmScreenSettings);         // Devmode 結構的大小

          dmPelsWidth  := width;                     // 所選螢幕寬度

         dmPelsHeight := height;                     // 所選螢幕高度

          dmBitsPerPel := bits;                       // 每象素所選的色彩深度

          dmFields     := DM_BITSPERPEL or DM_PELSWIDTH or DM_PELSHEIGHT;

      //  嘗試設定顯示模式并傳回結果。注: 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;

  //由于全屏模式可能失敗,使用者可能決定在視窗下運作,

  //我們需要在設定螢幕/視窗之前,再次檢查fullscreen的值是TRUE或FALSE

      dwExStyle:=WS_EX_APPWINDOW;                              // 擴充窗體風格(窗體可見時處于最前面)

      dwStyle:=WS_POPUP or WS_CLIPSIBLINGS or WS_CLIPCHILDREN; // 窗體風格(沒有邊框)

      Showcursor(false);                                       // 隐藏滑鼠指針

    end

  else

      dwExStyle:=WS_EX_APPWINDOW or WS_EX_WINDOWEDGE;                     // 擴充窗體風格(增強窗體的3D感觀)

      dwStyle:=WS_OVERLAPPEDWINDOW or WS_CLIPSIBLINGS or WS_CLIPCHILDREN; // 窗體風格(帶标題欄、可變大小的邊框、菜單和最大化/最小化按鈕的窗體)

  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                                     // 視窗是否正常建立

      KillGlWindow();                                 // 重置顯示區

      MessageBox(0,'不能建立一個視窗裝置描述表.','錯誤',MB_OK or MB_ICONEXCLAMATION);

      Result:=false;                                  // 傳回 FALSE

  //描述象素格式

  with pfd do

      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;

  //嘗試取得OpenGL裝置描述表

  h_Dc := GetDC(h_Wnd);

  if h_Dc=0 then

      KillGLWindow();                                 // 重置顯示區

      MessageBox(0,'不能建立一種相比對的像素格式.','錯誤',MB_OK or MB_ICONEXCLAMATION);

  //找到對應與此前我們標明的象素格式的象素格式

  PixelFormat := ChoosePixelFormat(h_Dc, @pfd);

  if (PixelFormat=0) then

      MessageBox(0,'不能找到像素格式.','錯誤',MB_OK or MB_ICONEXCLAMATION);

  //嘗試設定象素格式

  if (not SetPixelFormat(h_Dc,PixelFormat,@pfd)) then

      MessageBox(0,'不能設定像素格式.','錯誤',MB_OK or MB_ICONEXCLAMATION);

  //嘗試取得着色描述表

  h_Rc := wglCreateContext(h_Dc);                     // 嘗試取得着色描述表

  if (h_Rc=0) then

      MessageBox(0,'不能建立OpenGL渲染描述表.','錯誤',MB_OK or MB_ICONEXCLAMATION);

  //激活着色描述表

  if (not wglMakeCurrent(h_Dc, h_Rc)) then

      MessageBox(0,'不能激活目前的OpenGL渲然描述表.','錯誤',MB_OK or MB_ICONEXCLAMATION);

  //顯示建立完成的OpenGL視窗

  ShowWindow(h_Wnd,SW_SHOW);                          // 顯示建立完成的OpenGL視窗

  SetForegroundWindow(h_Wnd);                         // 設為前端視窗(給它更高的優先級)

  SetFocus(h_Wnd);                                    // 并将焦點移至此視窗

  ReSizeGLScene(width,height);                        // 設定透視 GL 螢幕

  //初始化OpenGL(設定光照、紋理、等等任何需要設定的東東)

  if (not InitGl()) then                              // 初始化OpenGL

      MessageBox(0,'初始化失敗.','錯誤',MB_OK or MB_ICONEXCLAMATION);

  Result:=true;                                       // 成功

//Windows程式的入口(調用視窗建立例程,處理視窗消息,并監視人機互動)

function WinMain(hInstance: HINST;                    // 目前視窗執行個體

   hPrevInstance: HINST;                            // 前一個視窗執行個體

   lpCmdLine: PChar;                                // 指令行參數

   nCmdShow: integer):                              // 視窗顯示狀态

                        integer; stdcall;

  msg: TMsg;                                          // 用來檢查是否有消息等待處理

  done: Bool;                                         // 用來檢查否完程式完成運作

  done:=false;  //用來退出循環的Bool 變量

  // 選擇視窗是否全屏

  if MessageBox(0,'你想在全屏模式下運作麼?','全屏',

                             MB_YESNO or MB_ICONQUESTION)=IDNO then

    FullScreen:=false

    FullScreen:=true;

  // 建立OpenGL視窗

  if not CreateGLWindow('我的OpenGL 架構',640,480,16,FullScreen) then

      Result := 0;                                    // 失敗退出

  // 下面是消息循環的開始。隻要done保持FALSE,循環一直進行

  while not done do

      if (PeekMessage(msg, 0, 0, 0, PM_REMOVE)) then  // 檢查是否有消息在等待

          if msg.message=WM_QUIT then                 // 收到退出消息?

            done:=true

          else                                        // 如果不是退出消息,我們翻譯消息,然後發送消息(使得WndProc() 或 Windows能夠處理他們)

           TranslateMessage(msg);                    // 翻譯消息

           DispatchMessage(msg);                     // 發送消息

         end;

        end

      else

          // J如果沒有消息,繪制我們的OpenGL場景失敗,或者ESC 發出退出信号直接退出程式

          if (active and not(DrawGLScene()) or keys[VK_ESCAPE]) then

            SwapBuffers(h_Dc);                        // 交換緩存 (雙緩存)

          if keys[VK_F1] then                         // 允許使用者按下F1鍵在全屏模式和視窗模式間切換

              Keys[VK_F1] := false;                     // 若是,使對應的Key數組中的值為 FALSE

              KillGLWindow();                           // 銷毀目前的視窗

              FullScreen := not FullScreen;             // 切換 全屏 / 視窗 模式

              // 重建 OpenGL 視窗

              CreateGLWindow('我的OpenGL 架構',640,480,16,fullscreen);

  // 關閉程式

  killGLwindow();                                     // 銷毀視窗

  result:=msg.wParam;                                 // 退出程式

  WinMain(hInstance, HPrevInst, CmdLine, CmdShow);   // 程式開始運作

end.

繼續閱讀