如果你
和我一样刚刚学习OpenGL,跟着教程走了一遍但还是感觉对一些东西的理解很模糊,你可以继续往下看,我们一起来探讨。如果不是,很抱歉浪费了您的时间。
PS:看了快三个星期的OpenGL了,NeHe的教程看似很简单(因为你只要照着敲就能做出一些很有意思的小demo出来),不过有很多地方需要自己去弄懂和理解的,于是就先把自己的理解写下来。所以,纯属个人简介,如有错误,欢迎指正。
Lesson 1 窗口的建立
1描述
创建一个window窗口,以此作为windows的载体。
Nehe的窗口框架搭建的非常好,理解的难点随之出现。这一课有许多关于OpenGL视图环境的设置函数,这里暂且略过(因为我现在还说不清),主要描述一下windows窗口的创建以及浅谈一些消息机制吧。
2流程
1.定义一个WNDCLASS窗口结构体(这个变量名貌似取得不是很好啊)
WNDCLASS wc; // 窗口类结构
2.设置WNDCLASS的属性,也就是确定窗体的样式风格。
这里有一个很重要的属性,也就是WNDCLASS的lpfnWndProc字段,这里指定窗口的消息处理函 数,后面会再次提到。
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 = "OpenG"; // 设定类名字
3.注册该窗口
if (!RegisterClass(&wc)) // 尝试注册窗口类
{
MessageBox(NULL,"注册窗口失败","错误",MB_OK|MB_ICONEXCLAMATION);
return FALSE; // 退出并返回FALSE
}
4.创建窗口,返回一个HWND类型的窗口句柄,这个很重要,各种资源的获取以及属性的设置都要用到它。
hWnd=CreateWindowEx( dwExStyle, // 扩展窗体风格
TEXT("OpenGL"), // 类名字
(LPCWSTR)title, // 窗口标题
dwStyle | // 必须的窗体风格属性
WS_CLIPSIBLINGS | // 必须的窗体风格属性
WS_CLIPCHILDREN, // 必须的窗体风格属性
0, 0, // 窗口位置
WindowRect.right-WindowRect.left, // 计算调整好的窗口宽度
WindowRect.bottom-WindowRect.top, // 计算调整好的窗口高度
NULL, // 无父窗口
NULL, // 无菜单
hInstance, // 实例
NULL))
5.显示窗口
ShowWindow(hWnd,SW_SHOW); // 显示窗口
至此,一个基本的窗口就已经搭建好并显示成功了,但是貌似和OpenGL一点关系也没有。NeHe的窗口创建和OpenGL的舞台搭建基本上是揉在一起的,所以我将其分开。下面是OpenGL的舞台搭建
1.创建像素格式,这个可以看做是OpenGL与windows窗口的连接纽带,像素格式PIXELFORMATDESCRIPTOR告诉windows窗口你需要提供一个相应的格式来满足OpenGL的显示要求
static PIXELFORMATDESCRIPTOR pfd= // /pfd 告诉窗口我们所希望的东东,即窗口使用的像素格式
{
sizeof(PIXELFORMATDESCRIPTOR), // 上述格式描述符的大小
1, // 版本号
PFD_DRAW_TO_WINDOW | // 格式支持窗口
PFD_SUPPORT_OPENGL | // 格式必须支持OpenGL
PFD_DOUBLEBUFFER, // 必须支持双缓冲
PFD_TYPE_RGBA, // 申请 RGBA 格式
bits, // 选定色彩深度
0, 0, 0, 0, 0, 0, // 忽略的色彩位
0, // 无Alpha缓存
0, // 忽略Shift Bit
0, // 无累加缓存
0, 0, 0, 0, // 忽略聚集位
16, // 16位 Z-缓存 (深度缓存)
0, // 无蒙板缓存
0, // 无辅助缓存
PFD_MAIN_PLANE, // 主绘图层
0, // Reserved
0, 0, 0 // 忽略层遮罩
};
2.获得设备描述表,设备描述表...好吧~我第一次看的时候也懵了,什么东西?我们可以将设备理解成资源,很多资源的分配和获得都要用到它,就我目前的理解,将它理解成资源管理器也差不多,这里的资源可以包括内存空间和窗口的设备(又是设备,找不到其他词了),比如:OpenGL里面可以将需要重复绘制的图像模型先存放在内存里(有点像是享元模式),也就是常说的显示列表,这样就可以提高运行效率,和数据就需要设备描述表来进行创建。
if (!(hDC=GetDC(hWnd))) // 取得设备描述表了么?
{
KillGLWindow(); // 重置显示区
MessageBox(NULL,"不能创建一种相匹配的像素格式","错误",MB_OK|MB_ICONEXCLAMATION);
return FALSE; // 返回 FALSE
}
3.检测像素格式并进行设置
if (!(PixelFormat=ChoosePixelFormat(hDC,&pfd))) // Windows 找到相应的象素格式了吗?
{
KillGLWindow(); // 重置显示区
MessageBox(NULL,TEXT("不能创建一种相匹配的像素格式"),TEXT("错误"),MB_OK|MB_ICONEXCLAMATION);
return FALSE; // 返回 FALSE
}
if(!SetPixelFormat(hDC,PixelFormat,&pfd)) // 能够设置象素格式么?
{
KillGLWindow(); // 重置显示区
MessageBox(NULL,TEXT("不能设置像素格式"),TEXT("错误"),MB_OK|MB_ICONEXCLAMATION);
return FALSE; // 返回 FALSE
}
4.渲染描述表?暂时还没有用到过,以后了解之后会重写这里
做到这里,OpenGL的舞台环境已经基本搭建完成
if (!(hRC=wglCreateContext(hDC))) // 能否取得OpenGL渲染描述表?
{
KillGLWindow(); // 重置显示区
MessageBox(NULL,TEXT("不能创建OpenGL渲染描述表"),TEXT("错误"),MB_OK|MB_ICONEXCLAMATION);
return FALSE; // 返回 FALSE
}
if(!wglMakeCurrent(hDC,hRC)) // 尝试激活着色描述表
{
KillGLWindow(); // 重置显示区
MessageBox(NULL,TEXT("不能激活当前的OpenGL渲然描述表"),TEXT("错误"),MB_OK|MB_ICONEXCLAMATION);
return FALSE; // 返回 FALSE
}
5.OpenGL初始化,一般封装成一个函数如initGL在主体程序运行前进行调用。这里是一些基本的参数,一些其他的初始化工作,如3D世界的数据读入等操作,也可以写在这个里面。
int InitGL(GLvoid)
{
glShadeModel(GL_SMOOTH);//阴影模式
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);//背景色
//深度缓存设置和测试
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
//让系统修正透视
glHint(GL_PERSPECTIVE_CORRECTION_HINT,GL_NICEST);
buildFont();
return TRUE;
}
Extra 消息机制
虽然教程中没怎么说明,但是我觉得这是一个很重要的机制。
描述
windows的消息机制其实是一个不断循环的过程,对于窗口主体程序而言,其过程可以大体总结为 监听消息---取得消息---处理消息(消息处理函数)---监听消息......
流程
1.监听消息&取得消息
为什么要一起讲?这里要讨论两个函数
PeekMessage和GetMessage
PeekMessage:无论应用程序消息队列是否有消息,PeekMessage函数都立即返回,程序得以继续执行
后面的语句(无消息则执行其它指令,有消息时一般要将消息派发出去,再执行其它
指令)。
GetMessage:函数只有在消息对立中有消息时返回,队列中无消息就会一直等,直至下
一个消息出现时才返回。在等的这段时间,应用程序不能执行任何指令。
所以GetMessage对消息有一种监听的感觉,而PeekMessage就只是一味的取数据而已了。
在NeHe的教程中使用的是PeekMessage,而在《Windows程序设计》一书中则是使用的GetMessage,原因是,在NeHe中,主函数的消息循环不仅仅起到接收外部消息的作用,它还是OpenGL动画的一个“始终”,每循环一次就会重新绘制一次从而产生动态效果。
while(!done) // 保持循环直到 done=TRUE
{
if (PeekMessage(&msg,NULL,0,0,PM_REMOVE)) // 有消息在等待吗?
{
if (msg.message==WM_QUIT) // 收到退出消息?
{
done=TRUE; // 是,则done=TRUE
}
else // 不是,处理窗口消息
{
TranslateMessage(&msg); // 翻译消息
DispatchMessage(&msg); // 发送消息
}
}
else // 如果没有消息
{
// 绘制场景。监视ESC键和来自DrawGLScene()的退出消息
if (active) // 程序激活的么?
{
if (keys[VK_ESCAPE]) // ESC 按下了么?
{
done=TRUE; // ESC 发出退出信号
}
else // 不是退出的时候,刷新屏幕
{
DrawGLScene(); // 绘制场景
SwapBuffers(hDC); // 交换缓存 (双缓存)
}
}
if (keys[VK_F1]) // F1键按下了么?
{
keys[VK_F1]=FALSE; // 若是,使对应的Key数组中的值为 FALSE
KillGLWindow(); // 销毁当前的窗口
fullscreen=!fullscreen; // 切换 全屏 / 窗口 模式
// 重建 OpenGL 窗口
if (!CreateGLWindow(TEXT("NeHe's OpenGL 程序框架"),640,480,16,fullscreen))
{
return 0; // 如果窗口未能创建,程序退出
}
}
}
}
看到一上代码,注意到DrawGLScene(),SwapBuffers(hDC)两个函数,如果将PeekMessage换成GetMessage的话,湖面应该就动不了了吧。
2.处理消息
任然是上面的代码,当获得消息以后,则会执行下面两行代码
TranslateMessage(&msg); // 翻译消息
DispatchMessage(&msg); // 发送消息
TranslateMessage做的事情,《windows程序设计》写的是进行键盘翻译,第六章有深入探讨(懒得去看哦),我猜大概就是将键盘的消息装换成相应的ASCLL码的值吧,“望文生义”有时还是很必要的。
DispatchMessage(&msg)做的事情则是将消息交给在WNDCLASS定义阶段绑定的那个消息响应函数(还记得不?不记得翻上去看看那吧),由响应函数进行处理,下面贴出一部分
LRESULT CALLBACK WndProc( HWND hWnd, // 窗口的句柄
UINT uMsg, // 窗口的消息
WPARAM wParam, // 附加的消息内容
LPARAM lParam) // 附加的消息内容
{
switch (uMsg) // 检查Windows消息
{
case WM_ACTIVATE: // 监视窗口激活消息
{
if (!HIWORD(wParam)) // 检查最小化状态
{
active=TRUE; // 程序处于激活状态
}
else
{
active=FALSE; // 程序不再激活
}
return 0; // 返回消息循环
}
//各种消息类别的处理....省略
}
// 向 DefWindowProc传递所有未处理的消息。
return DefWindowProc(hWnd,uMsg,wParam,lParam);
}
DefWindowProc(hWnd,uMsg,wParam,lParam)是将我们不关心的消息扔回去让缺省的消息响应函数处理,比如WM_CLOSE消息什么的....
其他
首先,此文意在梳理学习过程中的疑问以及整合知识的结构流程。
最后附上我修改过后的OpenGL窗口文件,可在
VS2010下运行,我一般将它作为每次课程的“白板”,希望有所帮助。
Nehe中文教程地址
http://www.owlei.com/DancingWind/
分享到:
相关推荐
黑龙江省齐齐哈尔市讷河市拉哈一中在2020-2021学年高二下学期3月进行了一次摸底考试,本次考试的数学(文)试卷综合考查了学生对导数的深入理解和应用能力。试卷中不仅包含了选择题、填空题等基础题型,更有解答题,...
黑龙江省讷河市拉哈一中2020-2021学年高一下学期4月月考语文试卷 Word版含答案.doc
黑龙江省讷河市拉哈一中2020-2021学年高一上学期12月月考数学试卷,作为一次重要的阶段性评估,它不仅检验了学生们在本学期所学数学知识的掌握情况,而且反映出学生们的思维深度和解题技巧。本试卷内容涵盖广泛,从...
1. 线性回归与相关性:题目1和题目2涉及到线性回归方程的计算和理解。线性回归用于描述两个变量之间的线性关系,回归方程形式为`y = a + bx`,其中`a`是截距,`b`是斜率。题目中通过散点图确定线性关系,并求解回归...
黑龙江省齐齐哈尔市市讷河一中2020届高三理综联考试题PDF
此资源为“黑龙江省齐齐哈尔市市讷河一中2020届高三英语联考试题PDF”,是一份针对高三学生的英语考试试卷,主要目的是检验学生在高三阶段对英语知识的掌握程度,为高考做好准备。试题包含了听力、阅读、写作等多个...
《英雄》音乐微案例;黑龙江省讷河市六合镇中心学校赵明丽.doc
保护青山绿水 构筑生态文明——讷河市新能源公交车全面上路运营掠影.pdf
1. **项目背景与目标**:鸡西至讷河公路建设项目C23标段是连接两个城市的重要交通基础设施,旨在提升区域交通便利性,促进经济交流与发展。施工组织设计的目标是确保工程在预定的时间内、预设的质量标准下安全、高效...
2021届黑龙江省讷河市拉哈一中高一下学期历史3月月考试题.doc
《鸡西至讷河公路建设项目C23标段——施工组织与CAD技术在道路工程中的应用》 在交通基础设施建设中,公路项目是一项至关重要的工程,对于区域经济发展和社会进步具有深远影响。本项目——“鸡西至讷河公路建设项目...
1. **项目背景**:鸡西至讷河公路是连接两地的重要交通动脉,对改善区域交通条件、促进经济发展具有重大意义。C23标段是整个项目中的一个重要组成部分,通常包括特定的工程范围、工期和质量要求。 2. **工程规模与...
黑龙江省讷河市张静中学第一学期初三期中考试语文考试题及答案.doc.pdf
讷河市实验学校初三第二次月考的语文试卷是一份全面考察学生语文能力的测试题,它不仅覆盖了语文学习的基础知识点,还着重于检验学生的逻辑思维、文学素养和文化积累,这对于初中生而言是一次难得的自我检测和提升的...
这份资料是讷河市实验学校2015年初三第二次月考的数学试卷及答案,主要涵盖了几何、代数、概率等多个数学知识点。以下是试卷中涉及的主要内容: 1. **算术运算**:题目中涉及到分数的乘除、根号的运算以及π的近似...
黑龙江省讷河市拉哈一中2020-2021学年高一下学期4月月考地理试卷 Word版含答案.doc
鸡西至讷河公路建设项目C23标段的施工组织设计是公路建设中至关重要的一环,它涵盖了公路桥梁隧道的设计、施工、管理等多个关键领域。这份压缩包文件包含的详细设计方案,旨在确保项目的顺利实施,优化资源配置,...
- **定义与目的**:数学联考通常是指多个学校联合组织的一种考试形式,旨在通过统一的试题来检验学生在某一阶段的学习成果,尤其是对于即将参加高考的学生来说,这样的联考可以帮助他们更好地了解自己的水平以及在...
- **定义与目的**:数学联考通常是指多个学校联合组织的一种考试形式,旨在通过统一的试题来检验学生在某一阶段的学习成果,尤其是对于即将参加高考的学生来说,这样的联考可以帮助他们更好地了解自己的水平以及在...