通用 Win32 项目中所有课程的集中






4.71/5 (14投票s)
OpenGL 教程和课程的支持
引言
大约三个月前,我发表了关于集成到 Visual C++ MFC 项目中的课程的文章。令我有些惊讶的是,我发现很多人对 MFC 这个词感到恐慌(有人甚至称之为厌恶)。尽管如此,OpenGL 课程的想法很好,并且本网站提供的代码适用于任何版本的软件。因此,我也为 Win32 Visual C++ Visual Studio 2010-2012 专业版做了同样的工作。
我相信将所有课程集中在一个通用项目中,对于所有初学者开始学习 OpenGL 应该非常有帮助。这些课程的代码一个接一个地紧密排列,展示了从上一阶段到下一阶段的程序进展。此外,将所有课程集成到一个通用项目中,使得可以使用和组合不同课程的程序,并可以基于原始课程开发新应用程序。
在将代码适配到现代版本的 Visual C++ 时,我发现从一个版本的 MS Visual Studio 修改到另一个版本的项目中出现了一些冲突。最后,为了演示目的,我开发了项目 OpenGLWin32_2010
。这里的 2010 意味着该项目是为 Microsoft Visual Studio Professional 2010 开发的。尽管如此,所有项目都可以轻松地升级到 2012 版本,但该版本需要 Windows 7,而 2010 版本适用于 Windows XP SP3 及更高版本。
上述项目的开发得到了 NeHe 俄罗斯网站编辑 Sergey Anisimov 的大力帮助,我非常感谢他的善意协助。
上述代码对于想要从头开始学习 Visual C++ 的人来说将是一个巨大的帮助。毋庸置疑,学习任何编译器最好的方法是从一些可运行的项目开始。本课程的演示参考 Visual Studio 2010 专业版。但是,在 2012 版本中不会出现太多差异。
OpenGLWin32_2010 目录的内容如下
OpenGLWin32
- 包含所有课程项目的空项目Lesson01..X5
- 每个单独课程的项目- GLSummary - 包含所有课程的项目
Data
- 所有课程的源数据- NeHeProgs - 课程的主要和辅助代码
- GlobUse - 所有课程通用的程序
- DemoLessons - 所有课程的所有可执行文件
- HelpText - 所有课程的帮助和信息文件
要查看和编辑代码,请启动 OpenGLWin32.sln 文件。即使您没有使用 Visual C++ 的经验,也可以通过启动此项目(或任何其他包含的项目)来开始学习。
项目中所使用的程序列表可通过菜单“视图”->“解决方案资源管理器”找到。只需选择任何 *.h 或 *.cpp 文件,即可查看和编辑。乍一看,可能觉得包含的文件太多,但进一步考虑后会发现一切并非过于复杂。
要构建所有项目,只需启动构建(菜单“构建”->“构建解决方案”)。如果一切正确,输出窗口中的报告结果必须是:49 个成功,3 个失败,0 个跳过。
失败的项目是 OpenGLWin32
本身(它没有入口点)以及 Lesson43/Lesson47。遗憾的是,我对 OpenGL 的处理经验不足,其中两个课程未能成功适配到 Visual C++ 的现代版本。我希望原始代码的作者们能轻松改进我的代码,使其完全符合他们的初衷。
要启动任何项目,只需将其指定为启动项目(右键单击鼠标 - 菜单“指定为启动项目”)并选择菜单“调试”->“不带调试启动”。如果一切正常,将开始演示相应的课程。如果您将 GLSummary
项目指定为启动项目,则将启动其中一个课程(其名称存储在 LessonsGL/GLSummary 目录下的 LastNum.txt 文件中)。对于任何其他课程演示,请选择菜单“文件”->“课程...”,然后将出现“选择课程”对话框。
您可以从列表中选择任何其他课程进行演示。如果您从单个课程开始工作,程序会调用所选课程,然后自行终止。如果您从 GLSummary 开始,程序只会调用包含的所选课程。Lesson43/Lesson47 也可启动,但您不能从它们调用其他课程:它们是原始的 .exe 文件。
用于创建独立应用程序
- 创建一个新目录(例如,GLTest);
- 将所有
*.exe
程序(位于../lesson*/Release
和../GLSummary/Release
目录中)复制到这个新目录; - 将 Data 和 HelpText 目录复制到这个新目录中,然后应用程序就可以使用了,它们必须与 DemoLessons 目录中的程序完全一样运行。
CreateGLWindow
、ResizeGLScene
、WndProc
和 GlobWinMain
程序是所有课程通用的,位于 GlobUse/GLRoutine.cpp 文件中。
CreateGLWindow
程序与 NeHe 教程中的原始程序几乎相同。
在 ResizeGLScene
过程中发生了一些修改
GLvoid ReSizeGLScene(GLsizei width, GLsizei height) // Resize And Initialize The GL Window
{
if (height==0) // Prevent A Divide By Zero By
{
height=1; // Making Height Equal One
}
glViewport(0,0,width,height); // Reset The Current Viewport
glMatrixMode(GL_PROJECTION); // Select The Projection Matrix
glLoadIdentity(); // Reset The Projection Matrix
if(m_bOrtho)
glOrtho(0.0f,width,height,0.0f,-1.0f,1.0f); // Create Ortho View (0,0 At Top Left)
else
// Calculate The Aspect Ratio Of The Window
gluPerspective(fViewAngle,(GLfloat)width/(GLfloat)height,fNearPlane,fFarPlane);
glMatrixMode(GL_MODELVIEW); // Select The Modelview Matrix
glLoadIdentity(); // Reset The Modelview Matrix
HWND hWin = GetDlgItem(hWnd, IDC_MAIN_TOOL);
if(hWin != NULL)
{ //ToolBar Adjusted to Main window
RECT rect;
GetWindowRect(hWin, &rect);
BOOL bVisible = IsWindowVisible(hWin);
MoveWindow(hWin, rect.left, rect.top, rect.bottom - rect.top, width, TRUE);
}
HWND hSt = GetDlgItem(hWnd, IDC_MAIN_STATUS);
if(hSt != NULL)
{ //StatusBar Adjusted to Main window
RECT rect;
GetWindowRect(hSt, &rect);
BOOL bVisible = IsWindowVisible(hSt);
MoveWindow(hSt, rect.left, height + rect.top - rect.bottom, rect.bottom - rect.top, width, TRUE);
}
m_boxWidth = width; //remember thr current width and height in global variables.
m_boxHeight = height;
}
在 gluPerspective
过程中,出现了全局变量 fViewAngle, fNearPlane, fFarPlane
。在大多数课程中,将使用默认值。
fViewAngle = 45; fNearPlane = <st1:metricconverter
ProductID="0.1f" w:st="on">0.1f</st1:metricconverter>; fFarPlane = 100;
将使用。然而,在第30、31、34、36、44、45、46课中,这些值需要改变。
此外,全局变量 m_bOrtho
根据需要(第 21 和 24 课)安排 glOrtho
的性能。
TollBar
和 StatusBar
在 ResizeGLScene
期间调整到主窗口。
由于额外的对话框和条形处理,WndProc
和 GlobWinMain
过程也发生了一些变化。您可以在 GlobUse/GLRoutine.cpp 中查看这些变化 - 没什么特别的,只是常规操作。
与原始课程不同,同时按下 Ctrl+Enter 键可实现全屏显示。
F1 键与原始课程不同,用于调用帮助的标准目的(菜单“帮助”->“帮助对话框...”)
“帮助对话框”窗口是非模态的,因此程序处理既可以从该窗口进行,也可以通过其他方式进行。所有帮助信息都以纯文本文件的形式存储在 HelpText 目录中。
关于原始代码的作者们,关于对话框(菜单“帮助”->“关于...”)包含原始 NeHe 的 ReadMe 文本。
此 About Dialog
也可通过“关于...”按钮从其他“课程”和“帮助对话框”中访问。
与原始课程的另一个不同之处在于,所有纹理过程都集成在 GlobUse 目录下的 GlobGLTexture.cpp 文件中。我没有使用 auxDIBImageLoad
进行纹理加载,而是使用 CImage
类安排了如下过程:
HBITMAP GetImgBitmap(CImage * nImg)
{
if(nImg == NULL)
return NULL;
CImage * pImg = new CImage;
pImg->Create(nImg->GetWidth(),nImg->GetHeight(), 24);
nImg->BitBlt(pImg->GetDC(),CPoint(0,0));
HDC hdc = CreateCompatibleDC(pImg->GetDC());
HBITMAP hbm = CreateCompatibleBitmap(pImg->GetDC(), pImg->GetWidth(), pImg->GetHeight());
HBITMAP bmOld = (HBITMAP) SelectObject(hdc,hbm);
pImg->BitBlt(hdc, CPoint(0,0));
pImg->ReleaseDC();
delete pImg;
return hbm;
}
//////////////////////////////////////////////////////////////////////////
HBITMAP LoadMyBitmap(char * fName)
{
if(!isFileExist(fName))
{
char strk[_MAX_PATH];
_getcwd(str, _MAX_PATH);
sprintf_s(strk, "LoadMyBitmap\nNo File exist:\n%s\nin Directory:\n%s", fName, str);
MessageBox(NULL, strk, "No File:", MB_OK | MB_ICONINFORMATION);
return NULL;
}
CImage * pImg = new CImage;
pImg->Load(fName);
HBITMAP bm = GetImgBitmap(pImg);
delete pImg;
pImg = NULL;
return bm;
}
//////////////////////////////////////////////////////////////////////////////
您可以复制 GlobGLTexture.cpp 文件,并将其用于任何使用 OpenGL
开发的各种图像文件项目,只要 Windows 支持这些文件。
课程代码本身集中在 NeHeProgs
目录中,并根据课程编号以 "01_"、"02_"、..."X5_" 开头。这些是 NeHe 的 GameDev 网站(Grant James)中提供的代码,我尽力在适配到现代版本的 Visual Studio 时不更改代码。
唯一的巨大区别是我敢于从所有项目中排除 Glaux.lib 库。尽管如此,我希望原始代码的作者们不会抱怨。他们当然不可能期望 MS VS 开发人员从第 10 版开始就排除 Glaux.lib 库。而且,即使仍然可以在 MS VS 第 10 版中包含 Glaux.lib,但在第 11 版中,它将不再工作。
所有程序集中在一个公共目录 GlobUse 中,使得可以在单独的课程中使用和组合通用程序,并可以基于原始课程开发新应用程序。应用程序开发的示例在项目 lessonX4/UFOgame 和 lessonX5/BoxMan 中呈现。Lesson X4.UFO Games 结合了课程 07、11、30、32、34。Lesson X5.BoxMan 演示了在外部文本文件中创建图形及其移动的可能性,用户无需了解程序员细节,只需空间想象力就足够了。
而 LessonX5 展示了一个由于同时使用 <windows.h> 和 <afxwin.h> 而引发的问题。冲突在“属性窗口配置属性”->“链接器”->“常规”->“强制输出”中被弃用。错误已消除,但警告仍然存在,并且生成的 EXE 文件非常大。在 MFC 平台上,没有出现任何问题。
我认为借助现有的工作项目来学习 OpenGL 的想法,完全符合人们对 OpenGL 的教程需求。我希望所提供的项目能为 OpenGL 的教程和课程提供良好的支持。