NeHe的OpenGL教程1(Bang翻譯Delphi版)-如何繪制OpenGL視窗
在這個教程裡,我将教你在Windows環境中建立OpenGL程式.它将顯示一個空的OpenGL視窗,可以在視窗和全屏模式下切換,按ESC退出.它是我們以後應用程式的架構。如下圖:

program lesson1a;
{
OpenGL DelphiXE
}
uses
Windows,
Messages,
// 引用OpenGL單元
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繪制代碼
//......
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.