JeffMolofee(NeHe)的OPENGL教程-第一课[4]-CreateGLWindow

时间:2023-01-15 20:09:18

接下来的代码段创建我们的OpenGL窗口。我花了很多时间来做决定是否创建固定的全屏模式这样不需要许多额外的代码,还是创建一个容易定制的友好的窗口但需要更多的代码。当然最后我选择了后者。我经常在EMail中收到诸如此类的问题:怎样创建窗口而不使用全屏幕?怎样改变窗口的标题栏?怎样改变窗口的分辨率或pixel format(象素格式)?以下的代码完成了所有这一切!尽管最好要学学材质,这会让您写自己的OpenGL程序变得容易的多!

正如您所见,此过程返回布尔变量(TRUE 或 FALSE)。他还带有5个参数:窗口的标题栏,窗口的宽度,窗口的高度,色彩位数(16/24/32),和全屏标志(TRUE --全屏模式, FALSE--窗口模式 )。返回的布尔值告诉我们窗口是否成功创建。
BOOL CreateGLWindow(char* title, int width, int height, int bits, bool fullscreenflag)
{
当我们要求Windows为我们寻找相匹配的象素格式时,Windows寻找结束后将模式值保存在变量PixelFormat中。
GLuint PixelFormat; // 保存查找匹配的结果

wc用来保存我们的窗口类的结构。窗口类结构中保存着我们的窗口信息。通过改变类的不同字段我们可以改变窗口的外观和行为。每个窗口都属于一个窗口类。当您创建窗口时,您必须为窗口注册类。
WNDCLASS wc; // 窗口类结构

dwExStyle和dwStyle存放扩展和通常的窗口风格信息。我使用变量来存放风格的目的是为了能够根据我需要创建的窗口类型(是全屏幕下的弹出窗口还是窗口模式下的带边框的普通窗口);来改变窗口的风格。
DWORD dwExStyle;  // 扩展窗口风格
DWORD dwStyle;
     // 窗口风格

下面的5行代码取得矩形的左上角和右下角的坐标值。我们将使用这些值来调整我们的窗口使得其上的绘图区的大小恰好是我们所需的分辨率的值。通常如果我们创建一个640x480的窗口,窗口的边框会占掉一些分辨率的值。
RECT WindowRect;
WindowRect.left=(long)0;                 
WindowRect.right=(long)width;
WindowRect.top=(long)0;
WindowRect.bottom=(long)height;
 

下一行代码我们让全局变量fullscreen等于fullscreenflag。如果我们希望在全屏幕下运行而将fullscreenflag设为TRUE,但没有让变量fullscreen等于fullscreenflag的话,fullscreen变量将保持为FALSE。当我们在全屏幕模式下销毁窗口的时候,变量fullscreen的值却不是正确的TRUE值,计算机将误以为已经处于桌面模式而无法切换回桌面。上帝啊,但愿这一切都有意义。就是一句话,fullscreen的值必须永远fullscreenflag的值,否则就会有问题
fullscreen=fullscreenflag; // 设置全局全屏标志

下一部分的代码中,我们取得窗口的实例,然后定义窗口类。
CS_HREDRAW 和 CS_VREDRAW 的意思是无论何时,只要窗口发生变化时就强制重画。CS_OWNDC为窗口创建一个私有的DC。这意味着DC不能在程序间共享。WndProc是我们程序的消息处理过程。由于没有使用额外的窗口数据,后两个字段设为零。然后设置实例。接着我们将hIcon设为NULL,因为我们不想给窗口来个图标。鼠标指针设为标准的箭头。背景色无所谓(我们在GL中设置)。我们也不想要窗口菜单,所以将其设为NULL。类的名字可以您想要的任何名字。出于简单,我将使用"OpenGL"。

hInstance = GetModuleHandle(NULL);         // 取得我们窗口的实例
wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;    // 移动时重画,并为窗口取得DC
wc.lpfnWndProc = (WNDPROC) WndProc;    // WndProc处理消息
wc.cbClsExtra = 0;             // 无额外窗口数据
wc.cbWndExtra = 0;          // 无额外窗口数据
wc.hInstance = hInstance;          // 设置实例
wc.hIcon = LoadIcon(NULL, IDI_WINLOGO);       // 装入缺省图标
wc.hCursor = LoadCursor(NULL, IDC_ARROW);    // 装入鼠标指针
wc.hbrBackground = NULL;            // GL不需要背景
wc.lpszMenuName = NULL;           // 不需要菜单
wc.lpszClassName = "OpenGL";
     // 设定类名字

 
现在注册类名字。如果有错误发生,弹出错误消息窗口。按下上面的OK按钮后,程序退出。
if (!RegisterClass(&wc))              // 尝试注册窗口类
{
MessageBox(NULL,"Failed To Register The Window Class.","ERROR",MB_OK|MB_ICONEXCLAMATION);
return FALSE;
}

 查看程序应该在全屏模式还是窗口模式下运行。如果应该是全屏模式的话,我们将尝试设置全屏模式。
if (fullscreen)         // 要尝试全屏模式吗?
{


下一部分的代码看来很多人都会有问题要问关于.......切换到全屏模式。在切换到全屏模式时,有几件十分重要的事您必须牢记。必须确保您在全屏模式下所用的宽度和高度等同于窗口模式下的宽度和高度。最最重要的是要在创建窗口之前设置全屏模式。这里的代码中,您无需再担心宽度和高度,它们已被设置成与显示模式所对应的大小。


DEVMODE dmScreenSettings;                         // 设备模式
memset(&dmScreenSettings,0,sizeof(dmScreenSettings));      // 确保内存分配
dmScreenSettings.dmSize=sizeof(dmScreenSettings);            // Devmode 结构的大小
dmScreenSettings.dmPelsWidth = width;                             // 所选屏幕宽度
dmScreenSettings.dmPelsHeight = height;                           // 所选屏幕高度
dmScreenSettings.dmBitsPerPel = bits;                               // 每象素所选的色彩深度
dmScreenSettings.dmFields=DM_BITSPERPEL|DM_PELSWIDTH|DM_PELSHEIGHT;
    
 

上面的代码中,我们分配了用于存储视频设置的空间。设定了屏幕的宽,高,色彩深度。下面的代码我们尝试设置全屏模式。我们在dmScreenSettings中保存了所有的宽,高,色彩深度讯息。下一行使用ChangeDisplaySettings来尝试切换成与dmScreenSettings所匹配模式。我使用参数CDS_FULLSCREEN来切换显示模式,因为这样做不仅移去了屏幕底部的状态条,而且它在来回切换时,没有移动或改变您在桌面上的窗口。

// 尝试设置显示模式并返回结果。注: CDS_FULLSCREEN 移去了状态条。
if (ChangeDisplaySettings(&dmScreenSettings,CDS_FULLSCREEN)!=DISP_CHANGE_SUCCESSFUL)
{

如果模式未能设置成功,我们将进入以下的代码。如果不能匹配全屏模式,弹出消息窗口,提供两个选项:在窗口模式下运行或退出。

// 若模式失败,提供两个选项:退出或在窗口内运行。
if (MessageBox(NULL,"The Requested Fullscreen Mode Is Not Supported By\nYour Video Card. Use Windowed Mode Instead?","NeHe GL",MB_YESNO|MB_ICONEXCLAMATION)==IDYES)
{

  //如果用户选择窗口模式,变量fullscreen 的值变为FALSE,程序继续运行。
  
 fullscreen=FALSE;
}
else
{
 
     //如果用户选择退出,弹出消息窗口告知用户程序将结束。并返回FALSE告诉程序窗口未能成功创建。程序退出。 
  MessageBox(NULL,"Program Will Now Close.","ERROR",MB_OK|MB_ICONSTOP);
  return FALSE;
//退出并返回 FALSE
}
}
}