NeHe的OpenGL教程7(Bang翻译Delphi版)-如何使用光源
在这一课里,我将教会你如何用光源照亮立方体的六个面,如下图:
将下图放在应用程序data目录下,起名NeHe.bmp
program lesson7a;
{
OpenGL DelphiXE
出处:根据NeHe代码翻译而来(http://nehe.gamedev.net/)
作者:帅宏军 shuaihj@163.com
在这一课里,我将教会你如何用光源照亮立方体的六个面
注:本单元用到了glaux.dll和glaux.pas,下载地址为:
http://download.csdn.net/detail/shuaihj/4043519
}
uses
Windows,
Messages,
SysUtils,
OpenGL,
glaux;
procedure glGenTextures(n: GLsizei; var textures: GLuint); stdcall; external opengl32;
procedure glBindTexture(target: GLenum; texture: GLuint); stdcall; external opengl32;
// 全局变量
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全屏标志缺省,缺省设定成全屏模式
rquadx, rquady, rquadz: GLfloat; // 用于立方体的三个方向上的角度
texture: Array[0..0] of GLuint; // 存储一个纹理
//设置用来创建光源的数组。我们将使用两种不同的光。
//第一种称为环境光。环境光来自于四面八方。所有场景中的对象都处于环境光的照射中。
//第二种类型的光源叫做漫射光。漫射光由特定的光源产生,并在您的场景中的对象表面上产生反射。
//处于漫射光直接照射下的任何对象表面都变得很亮,而几乎未被照射到的区域就显得要暗一些。
//这样在我们所创建的木板箱的棱边上就会产生的很不错的阴影效果。
//创建光源的过程和颜色的创建完全一致。前三个参数分别是RGB三色分量,最后一个是alpha通道参数。
//因此,下面的代码我们得到的是半亮(0.5f)的白色环境光。如果没有环境光,未被漫射光照到的地方会变得十分黑暗。
LightAmbient: array[0..3] of GLfloat = (0.5, 0.5, 0.5, 1.0); // 环境光参数
LightDiffuse: array[0..3] of GLfloat = (1.0, 1.0, 1.0, 1.0); // 环境光参数(所有的参数值都取成最大值1.0f。它将照在我们木板箱的前面,看起来挺好)
//最后我们保存光源的位置。前三个参数和glTranslate中的一样。依次分别是XYZ轴上的位移。
//由于我们想要光线直接照射在木箱的正面,所以XY轴上的位移都是0.0f。
//第三个值是Z轴上的位移。为了保证光线总在木箱的前面,所以我们将光源的位置朝着观察者(就是您哪。)挪出屏幕。
//我们通常将屏幕也就是显示器的屏幕玻璃所处的位置称作Z轴的0.0f点。所以Z轴上的位移最后定为2.0f。
//假如您能够看见光源的话,它就浮在您显示器的前方。当然,如果木箱不在显示器的屏幕玻璃后面的话,您也无法看见箱子。
//最后一个参数取为1.0f。这将告诉OpenGL这里指定的坐标就是光源的位置
LightPosition: array[0..3] of GLfloat = (0.0, 0.0, 2.0, 1.0); // 环境光参数
//载入位图
function LoadBMP(FileName: PAnsichar):PTAUX_RGBImageRec; // 加载位图
begin
if not FileExists(Filename) then // 位图是否存在
begin
Result := nil;
exit;
end;
Result := auxDIBImageLoadA(FileName); // 载入位图
end;
//加载纹理
function LoadGLTextures: Bool;
var
TextureImage: array [0..0] of PTAUX_RGBImageRec; // 纹理句柄
begin
Result := false;
ZeroMemory(@TextureImage,sizeof(TextureImage)); // 初始化内存
TextureImage[0] := LoadBMP('Data/NeHe.bmp'); // 加载纹理图片
if Assigned(TextureImage[0]) then
begin
Result := true;
glGenTextures(1,Texture[0]); // 创建纹理
glBindTexture(GL_TEXTURE_2D,texture[0]); // 使用来自位图数据生成的典型纹理
glTexImage2D(GL_TEXTURE_2D,0,3,TextureImage[0].sizeX,TextureImage[0].sizeY,0,GL_RGB,GL_UNSIGNED_BYTE,TextureImage[0].data); // 生成纹理
// gluBuild2DMipmaps(GL_TEXTURE_2D,3,TextureImage[0].sizeX,TextureImage[0].sizeY,GL_RGB,GL_UNSIGNED_BYTE,TextureImage[0].data); // 生成纹理
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); // 线形滤波
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); // 线形滤波
end;
end;
// 重置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
//启用纹理映射
if not LoadGLTextures then // 加载纹理
begin
Result := false;
exit;
end;
glEnable(GL_TEXTURE_2D); // 启用纹理映射
//启用光源
//设置环境光的发光量,光源light1开始发光。环境光的发光量存放在LightAmbient数组中。
glLightfv(GL_LIGHT1, GL_AMBIENT, @LightAmbient); // 设置环境光
//接下来我们设置漫射光的发光量。它存放在LightDiffuse数组中(全亮度白光)。
glLightfv(GL_LIGHT1, GL_DIFFUSE, @LightDiffuse); // 设置漫射光
//设置光源的位置。位置存放在 LightPosition 数组中(正好位于木箱前面的中心,X-0.0f,Y-0.0f,Z方向移向观察者2个单位<位于屏幕外面>)。
glLightfv(GL_LIGHT1, GL_POSITION,@LightPosition); // 设置光源位置
//启用一号光源。我们还没有启用GL_LIGHTING,所以您看不见任何光线。记住:只对光源进行设置、定位、甚至启用,光源都不会工作。除非我们启用GL_LIGHTING。
glEnable(GL_LIGHT1); // 启用一号光源
//启用光源
glEnable(GL_LIGHTING); // 启用光源
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(); // 重置当前指定的矩阵为单位矩阵
//注意到这次我们将立方体移地更远离屏幕了。因为立方体的大小要比金字塔大,同样移入6个单位时,立方体看起来要大的多。这是透视的缘故。越远的对象看起来越小。
glTranslatef(0.0,0.0,-7.0); // 向屏幕深处移动7个单位
//旋转
glRotatef(rquadx,1.0,0.0,0.0); // 绕X轴旋转
glRotatef(rquadY,0.0,1.0,0.0); // 绕Y轴旋转
glRotatef(rquadZ,0.0,0.0,1.0); // 绕Z轴旋转
glBegin(GL_QUADS); // 开始绘制立方体
//glNormal3f是这一课的新东西。
//Normal就是法线的意思,所谓法线是指经过面(多边形)上的一点且垂直于这个面(多边形)的直线。
//使用光源的时候必须指定一条法线。法线告诉OpenGL这个多边形的朝向,并指明多边形的正面和背面。
//如果没有指定法线,什么怪事情都可能发生:不该照亮的面被照亮了,多边形的背面也被照亮....。
//对了,法线应该指向多边形的外侧。
//看着木箱的前面您会注意到法线与Z轴正向同向。
//这意味着法线正指向观察者-您自己。这正是我们所希望的。
//对于木箱的背面,也正如我们所要的,法线背对着观察者。
//如果立方体沿着X或Y轴转个180度的话,前侧面的法线仍然朝着观察者,背面的法线也还是背对着观察者。
//换句话说,不管是哪个面,只要它朝着观察者这个面的法线就指向观察者。
//由于光源紧邻观察者,任何时候法线对着观察者时,这个面就会被照亮。
//并且法线越朝着光源,就显得越亮一些。
//如果您把观察点放到立方体内部,你就会法线里面一片漆黑。因为法线是向外指的。
//如果立方体内部没有光源的话,当然是一片漆黑。
// 前面
glNormal3f( 0.0, 0.0, 1.0); // 法线指向观察者
glTexCoord2f(0.0, 0.0); glVertex3f(-1.0, -1.0, 1.0); // 纹理和立方体的左下
glTexCoord2f(1.0, 0.0); glVertex3f( 1.0, -1.0, 1.0); // 纹理和立方体的右下
glTexCoord2f(1.0, 1.0); glVertex3f( 1.0, 1.0, 1.0); // 纹理和立方体的右上
glTexCoord2f(0.0, 1.0); glVertex3f(-1.0, 1.0, 1.0); // 纹理和立方体的左上
// 后面
glNormal3f( 0.0, 0.0,-1.0); // 法线背向观察者
glTexCoord2f(1.0, 0.0); glVertex3f(-1.0, -1.0, -1.0); // 纹理和立方体的右下
glTexCoord2f(1.0, 1.0); glVertex3f(-1.0, 1.0, -1.0); // 纹理和立方体的右上
glTexCoord2f(0.0, 1.0); glVertex3f( 1.0, 1.0, -1.0); // 纹理和立方体的左上
glTexCoord2f(0.0, 0.0); glVertex3f( 1.0, -1.0, -1.0); // 纹理和立方体的左下
// 顶面
glNormal3f( 0.0, 1.0, 0.0); // 法线向上
glTexCoord2f(0.0, 1.0); glVertex3f(-1.0, 1.0, -1.0); // 纹理和立方体的左上
glTexCoord2f(0.0, 0.0); glVertex3f(-1.0, 1.0, 1.0); // 纹理和立方体的左下
glTexCoord2f(1.0, 0.0); glVertex3f( 1.0, 1.0, 1.0); // 纹理和立方体的右下
glTexCoord2f(1.0, 1.0); glVertex3f( 1.0, 1.0, -1.0); // 纹理和立方体的右上
// 底面
glNormal3f( 0.0,-1.0, 0.0); // 法线朝下
glTexCoord2f(1.0, 1.0); glVertex3f(-1.0, -1.0, -1.0); // 纹理和立方体的右上
glTexCoord2f(0.0, 1.0); glVertex3f( 1.0, -1.0, -1.0); // 纹理和立方体的左上
glTexCoord2f(0.0, 0.0); glVertex3f( 1.0, -1.0, 1.0); // 纹理和立方体的左下
glTexCoord2f(1.0, 0.0); glVertex3f(-1.0, -1.0, 1.0); // 纹理和立方体的右下
// 右面
glNormal3f( 1.0, 0.0, 0.0); // 法线朝右
glTexCoord2f(1.0, 0.0); glVertex3f( 1.0, -1.0, -1.0); // 纹理和立方体的右下
glTexCoord2f(1.0, 1.0); glVertex3f( 1.0, 1.0, -1.0); // 纹理和立方体的右上
glTexCoord2f(0.0, 1.0); glVertex3f( 1.0, 1.0, 1.0); // 纹理和立方体的左上
glTexCoord2f(0.0, 0.0); glVertex3f( 1.0, -1.0, 1.0); // 纹理和立方体的左下
// 左面
glNormal3f(-1.0, 0.0, 0.0); // 法线朝左
glTexCoord2f(0.0, 0.0); glVertex3f(-1.0, -1.0, -1.0); // 纹理和立方体的左下
glTexCoord2f(1.0, 0.0); glVertex3f(-1.0, -1.0, 1.0); // 纹理和立方体的右下
glTexCoord2f(1.0, 1.0); glVertex3f(-1.0, 1.0, 1.0); // 纹理和立方体的右上
glTexCoord2f(0.0, 1.0); glVertex3f(-1.0, 1.0, -1.0); // 纹理和立方体的左上
glEnd();
rquadx:= rquadx + 0.35; // 增加立方体x方向的旋转变量
rquady:= rquady + 0.35; // 增加立方体y方向的旋转变量
rquadz:= rquadz + 0.35; // 增加立方体z方向的旋转变量
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.