以文本方式查看主题 - 计算机科学论坛 (http://bbs.xml.org.cn/index.asp) -- 『 C/C++编程思想 』 (http://bbs.xml.org.cn/list.asp?boardid=61) ---- [推荐]NeHe OpenGL教程(中英文版附带VC++源码)Lesson 01-lesson 02 (http://bbs.xml.org.cn/dispbbs.asp?boardid=61&rootid=&id=53679) |
-- 作者:一分之千 -- 发布时间:10/12/2007 10:39:00 AM -- [推荐]NeHe OpenGL教程(中英文版附带VC++源码)Lesson 01-lesson 02 第一课第二课源码下载: 第一课 中文版 第01课 在这个教程里,我将教你在Windows环境中创建OpenGL程序.它将显示一个空的OpenGL窗口,可以在窗口和全屏模式下切换,按ESC退出.它是我们以后应用程序的框架. 理解OpenGL如何工作非常重要,你可以在教程的末尾下载源程序,但我强烈建议你至少读一遍教程,然后再开始编程. 现在就让我们直接从代码开始吧。第一件事是打开VC然后创建一个新工程。如果您不知道如何创建的话,您也许不该学习OpenGL,而应该先学学VC。某些版本的VC需要将 bool 改成 BOOL , true 改成 TRUE , false 改成 FALSE ,请自行修改。 在您创建一个新的Win32程序(不是console控制台程序)后,您还需要链接OpenGL库文件。在VC中操作如下:Project-> Settings,然后单击LINK标签。在"Object/Library Modules"选项中的开始处(在 kernel32.lib 前)增加 OpenGL32.lib GLu32.lib 和 GLaux.lib 后单击OK按钮。现在可以开始写您的OpenGL程序了。 代码的前4行包括了我们使用的每个库文件的头文件。如下所示: 下面的第一行设置一个用来监控键盘动作的数组。有许多方法可以监控键盘的动作,但这里的方法很可靠,并且可以处理多个键同时按下的情况。 现在我们需要先定义WndProc()。必须这么做的原因是CreateGLWindow()有对WndProc()的引用,但WndProc()在CreateGLWindow()之后才出现。在C语言中,如果我们想要访问一个当前程序段之后的过程和程序段的话,必须在程序开始处先申明所要访问的程序段。所以下面的一行代码先行定义了WndProc(),使得CreateGLWindow()能够引用WndProc()。 下面的代码的作用是重新设置OpenGL场景的大小,而不管窗口的大小是否已经改变(假定您没有使用全屏模式)。甚至您无法改变窗口的大小时(例如您在全屏模式下),它至少仍将运行一次--在程序开始时设置我们的透视图。OpenGL场景的尺寸将被设置成它显示时所在窗口的大小。 glViewport(0, 0, width, height); // 重置当前的视口 下面几行为透视图设置屏幕。意味着越远的东西看起来越小。这么做创建了一个现实外观的场景。此处透视按照基于窗口宽度和高度的45度视角来计算。0.1f,100.0f是我们在场景中所能绘制深度的起点和终点。 // 设置视口的大小 glMatrixMode(GL_MODELVIEW); // 选择模型观察矩阵 接下的代码段中,我们将对OpenGL进行所有的设置。我们将设置清除屏幕所用的颜色,打开深度缓存,启用smooth shading(阴影平滑),等等。这个例程直到OpenGL窗口创建之后才会被调用。此过程将有返回值。但我们此处的初始化没那么复杂,现在还用不着担心这个返回值。 下一行启用smooth shading(阴影平滑)。阴影平滑通过多边形精细的混合色彩,并对外部光进行平滑。我将在另一个教程中更详细的解释阴影平滑。 下一行设置清除屏幕时所用的颜色。如果您对色彩的工作原理不清楚的话,我快速解释一下。色彩值的范围从0.0f到1.0f。0.0f代表最黑的情况,1.0f就是最亮的情况。glClearColor 后的第一个参数是Red Intensity(红色分量),第二个是绿色,第三个是蓝色。最大值也是1.0f,代表特定颜色分量的最亮情况。最后一个参数是Alpha值。当它用来清除屏幕的时候,我们不用关心第四个数字。现在让它为0.0f。我会用另一个教程来解释这个参数。 接下来的三行必须做的是关于depth buffer(深度缓存)的。将深度缓存设想为屏幕后面的层。深度缓存不断的对物体进入屏幕内部有多深进行跟踪。我们本节的程序其实没有真正使用深度缓存,但几乎所有在屏幕上显示3D场景OpenGL程序都使用深度缓存。它的排序决定那个物体先画。这样您就不会将一个圆形后面的正方形画到圆形上来。深度缓存是OpenGL十分重要的部分。 接着告诉OpenGL我们希望进行最好的透视修正。这会十分轻微的影响性能。但使得透视图看起来好一点。 下一段包括了所有的绘图代码。任何您所想在屏幕上显示的东东都将在此段代码中出现。以后的每个教程中我都会在例程的此处增加新的代码。如果您对OpenGL已经有所了解的话,您可以在 glLoadIdentity()调用之后,返回TRUE值之前,试着添加一些OpenGL代码来创建基本的形。如果您是OpenGL新手,等着我的下个教程。目前我们所作的全部就是将屏幕清除成我们前面所决定的颜色,清除深度缓存并且重置场景。我们仍没有绘制任何东东。 下一段代码只在程序退出之前调用。KillGLWindow() 的作用是依次释放着色描述表,设备描述表和窗口句柄。我已经加入了许多错误检查。如果程序无法销毁窗口的任意部分,都会弹出带相应错误消息的讯息窗口,告诉您什么出错了。使您在您的代码中查错变得更容易些。 我们在KillGLWindow()中所作的第一件事是检查我们是否处于全屏模式。如果是,我们要切换回桌面。我们本应在禁用全屏模式前先销毁窗口,但在某些显卡上这么做可能会使得桌面崩溃。所以我们还是先禁用全屏模式。这将防止桌面出现崩溃,并在Nvidia和3dfx显卡上都工作的很好! 我们使用ChangeDisplaySettings(NULL,0)回到原始桌面。将NULL作为第一个参数,0作为第二个参数传递强制Windows使用当前存放在注册表中的值(缺省的分辨率、色彩深度、刷新频率,等等)来有效的恢复我们的原始桌面。切换回桌面后,我们还要使得鼠标指针重新可见。 接下来的代码查看我们是否拥有着色描述表(hRC)。如果没有,程序将跳转至后面的代码查看是否拥有设备描述表。 如果存在着色描述表的话,下面的代码将查看我们能否释放它(将 hRC从hDC分开)。这里请注意我使用的的查错方法。基本上我只是让程序尝试释放着色描述表(通过调用wglMakeCurrent(NULL,NULL),然后我再查看释放是否成功。巧妙的将数行代码结合到了一行。 if (!wglMakeCurrent(NULL,NULL)) // 我们能否释放DC和RC描述表? 如果不能释放DC和RC描述表的话,MessageBox()将弹出错误消息,告知我们DC和RC无法被释放。NULL意味着消息窗口没有父窗口。其右的文字将在消息窗口上出现。"SHUTDOWN ERROR"出现在窗口的标题栏上。MB_OK的意思消息窗口上带有一个写着OK字样的按钮。 MessageBox(NULL,"释放DC或RC失败。","关闭错误",MB_OK | MB_ICONINFORMATION); 下一步我们试着删除着色描述表。如果不成功的话弹出错误消息。 [IMG] if (!wglDeleteContext(hRC)) // 我们能否删除RC? 如果无法删除着色描述表的话,将弹出错误消息告知我们RC未能成功删除。然后hRC被设为NULL。 MessageBox(NULL,"释放RC失败。","关闭错误",MB_OK | MB_ICONINFORMATION); 现在我们查看是否存在设备描述表,如果有尝试释放它。如果不能释放设备描述表将弹出错误消息,然后hDC设为NULL。 现在我们来查看是否存在窗口句柄,我们调用 DestroyWindow( hWnd )来尝试销毁窗口。如果不能的话弹出错误窗口,然后hWnd被设为NULL。 最后要做的事是注销我们的窗口类。这允许我们正常销毁窗口,接着在打开其他窗口时,不会收到诸如"Windows Class already registered"(窗口类已注册)的错误消息。 [IMG] if (!UnregisterClass("OpenG",hInstance)) // 能否注销类? 接下来的代码段创建我们的OpenGL窗口。我花了很多时间来做决定是否创建固定的全屏模式这样不需要许多额外的代码,还是创建一个容易定制的友好的窗口但需要更多的代码。当然最后我选择了后者。我经常在EMail中收到诸如此类的问题:怎样创建窗口而不使用全屏幕?怎样改变窗口的标题栏?怎样改变窗口的分辨率或pixel format(象素格式)?以下的代码完成了所有这一切!尽管最好要学学材质,这会让您写自己的OpenGL程序变得容易的多! 当我们要求Windows为我们寻找相匹配的象素格式时,Windows寻找结束后将模式值保存在变量PixelFormat中。 wc用来保存我们的窗口类的结构。窗口类结构中保存着我们的窗口信息。通过改变类的不同字段我们可以改变窗口的外观和行为。每个窗口都属于一个窗口类。当您创建窗口时,您必须为窗口注册类。 WNDCLASS wc; // 窗口类结构 dwExStyle和dwStyle存放扩展和通常的窗口风格信息。我使用变量来存放风格的目的是为了能够根据我需要创建的窗口类型(是全屏幕下的弹出窗口还是窗口模式下的带边框的普通窗口);来改变窗口的风格。 下面的5行代码取得矩形的左上角和右下角的坐标值。我们将使用这些值来调整我们的窗口使得其上的绘图区的大小恰好是我们所需的分辨率的值。通常如果我们创建一个640x480的窗口,窗口的边框会占掉一些分辨率的值。 下一行代码我们让全局变量fullscreen等于fullscreenflag。如果我们希望在全屏幕下运行而将fullscreenflag设为TRUE,但没有让变量fullscreen等于fullscreenflag的话,fullscreen变量将保持为FALSE。当我们在全屏幕模式下销毁窗口的时候,变量fullscreen的值却不是正确的TRUE值,计算机将误以为已经处于桌面模式而无法切换回桌面。上帝啊,但愿这一切都有意义。就是一句话,fullscreen的值必须永远fullscreenflag的值,否则就会有问题。 fullscreen=fullscreenflag; // 设置全局全屏标志 下一部分的代码中,我们取得窗口的实例,然后定义窗口类。 hInstance = GetModuleHandle(NULL); // 取得我们窗口的实例 现在注册类名字。如果有错误发生,弹出错误消息窗口。按下上面的OK按钮后,程序退出 if (!RegisterClass(&wc)) // 尝试注册窗口类 查看程序应该在全屏模式还是窗口模式下运行。如果应该是全屏模式的话,我们将尝试设置全屏模式。 下一部分的代码看来很多人都会有问题要问关于.......切换到全屏模式。在切换到全屏模式时,有几件十分重要的事您必须牢记。必须确保您在全屏模式下所用的宽度和高度等同于窗口模式下的宽度和高度。最最重要的是要在创建窗口之前设置全屏模式。这里的代码中,您无需再担心宽度和高度,它们已被设置成与显示模式所对应的大小。 上面的代码中,我们分配了用于存储视频设置的空间。设定了屏幕的宽,高,色彩深度。下面的代码我们尝试设置全屏模式。我们在dmScreenSettings中保存了所有的宽,高,色彩深度讯息。下一行使用ChangeDisplaySettings来尝试切换成与dmScreenSettings所匹配模式。我使用参数CDS_FULLSCREEN来切换显示模式,因为这样做不仅移去了屏幕底部的状态条,而且它在来回切换时,没有移动或改变您在桌面上的窗口。 如果模式未能设置成功,我们将进入以下的代码。如果不能匹配全屏模式,弹出消息窗口,提供两个选项:在窗口模式下运行或退出。 如果用户选择窗口模式,变量fullscreen 的值变为FALSE,程序继续运行。 如果用户选择退出,弹出消息窗口告知用户程序将结束。并返回FALSE告诉程序窗口未能成功创建。程序退出。 由于全屏模式可能失败,用户可能决定在窗口下运行,我们需要在设置屏幕/窗口之前,再次检查fullscreen的值是TRUE或FALSE。 if (fullscreen) // 仍处于全屏模式吗? 如果我们仍处于全屏模式,设置扩展窗体风格为WS_EX_APPWINDOW,这将强制我们的窗体可见时处于最前面。再将窗体的风格设为WS_POPUP。这个类型的窗体没有边框,使我们的全屏模式得以完美显示。 dwExStyle=WS_EX_APPWINDOW; // 扩展窗体风格 如果我们使用窗口而不是全屏模式,我们在扩展窗体风格中增加了 WS_EX_WINDOWEDGE,增强窗体的3D感观。窗体风格改用 WS_OVERLAPPEDWINDOW,创建一个带标题栏、可变大小的边框、菜单和最大化/最小化按钮的窗体。 下一行代码根据创建的窗体类型调整窗口。调整的目的是使得窗口大小正好等于我们要求的分辨率。通常边框会占用窗口的一部分。使用AdjustWindowRectEx 后,我们的OpenGL场景就不会被边框盖住。实际上窗口变得更大以便绘制边框。全屏模式下,此命令无效。 AdjustWindowRectEx(&WindowRect, dwStyle, FALSE, dwExStyle); // 调整窗口达到真正要求的大小 下一段代码开始创建窗口并检查窗口是否成功创建。我们将传递CreateWindowEx()所需的所有参数。如扩展风格、类名字(与您在注册窗口类时所用的名字相同)、窗口标题、窗体风格、窗体的左上角坐标(0,0 是个安全的选择)、窗体的宽和高。我们没有父窗口,也不想要菜单,这些参数被设为NULL。还传递了窗口的实例,最后一个参数被设为NULL。 下来我们检查看窗口是否正常创建。如果成功, hWnd保存窗口的句柄。如果失败,弹出消息窗口,并退出程序。 下面的代码描述象素格式。我们选择了通过RGBA(红、绿、蓝、alpha通道)支持OpenGL和双缓存的格式。我们试图找到匹配我们选定的色彩深度(16位、24位、32位)的象素格式。最后设置16位Z-缓存。其余的参数要么未使用要么不重要(stencil buffer模板缓存和accumulation buffer聚集缓存除外)。 如果前面创建窗口时没有错误发生,我们接着尝试取得OpenGL设备描述表。若无法取得DC,弹出错误消息程序退出(返回FALSE)。 设法为OpenGL窗口取得设备描述表后,我们尝试找到对应与此前我们选定的象素格式的象素格式。如果Windows不能找到的话,弹出错误消息,并退出程序(返回FALSE)。 Windows 找到相应的象素格式后,尝试设置象素格式。如果无法设置,弹出错误消息,并退出程序(返回FALSE)。 正常设置象素格式后,尝试取得着色描述表。如果不能取得着色描述表的话,弹出错误消息,并退出程序(返回FALSE)。 if (!(hRC=wglCreateContext(hDC))) // 能否取得着色描述表? 如果到现在仍未出现错误的话,我们已经设法取得了设备描述表和着色描述表。接着要做的是激活着色描述表。如果无法激活,弹出错误消息,并退出程序(返回FALSE)。 一切顺利的话,OpenGL窗口已经创建完成,接着可以显示它啦。将它设为前端窗口(给它更高的优先级),并将焦点移至此窗口。然后调用ReSizeGLScene 将屏幕的宽度和高度设置给透视OpenGL屏幕。 ShowWindow(hWnd,SW_SHOW); // 显示窗口 跳转至 InitGL(),这里可以设置光照、纹理、等等任何需要设置的东东。您可以在 InitGL()内部自行定义错误检查,并返回 TRUE (一切正常)或FALSE (有什么不对)。例如,如果您在InitGL()内装载纹理并出现错误,您可能希望程序停止。如果您返回 FALSE的话,下面的代码会弹出错误消息,并退出程序。 if (!InitGL()) // 初始化新建的GL窗口 到这里可以安全的推定创建窗口已经成功了。我们向WinMain()返回TRUE,告知WinMain()没有错误,以防止程序退出。 下面的代码处理所有的窗口消息。当我们注册好窗口类之后,程序跳转到这部分代码处理窗口消息。 下面的代码比对uMsg的值,然后转入case处理,uMsg 中保存了我们要处理的消息名字。 如果uMsg等于WM_ACTIVE,查看窗口是否仍然处于激活状态。如果窗口已被最小化,将变量active设为FALSE。如果窗口已被激活,变量active的值为TRUE。 return 0; // 返回消息循环 如果消息是WM_SYSCOMMAND(系统命令),再次比对wParam。如果wParam 是 SC_SCREENSAVE 或 SC_MONITORPOWER的话,不是有屏幕保护要运行,就是显示器想进入节电模式。返回0可以阻止这两件事发生。 如果 uMsg是WM_CLOSE,窗口将被关闭。我们发出退出消息,主循环将被中断。变量done被设为TRUE,WinMain()的主循环中止,程序关闭。 如果键盘有键按下,通过读取wParam的信息可以找出键值。我将键盘数组keys[ ]相应的数组组成员的值设为TRUE。这样以后就可以查找key[ ]来得知什么键被按下。允许同时按下多个键。 同样,如果键盘有键释放,通过读取wParam的信息可以找出键值。然后将键盘数组keys[ ]相应的数组组成员的值设为FALSE。这样查找key[ ]来得知什么键被按下,什么键被释放了。键盘上的每个键都可以用0-255之间的一个数来代表。举例来说,当我们按下40所代表的键时,keys[40]的值将被设为TRUE。放开的话,它就被设为FALSE。这也是key数组的原理。 case WM_KEYUP: // 有键放开么? 当调整窗口时,uMsg 最后等于消息WM_SIZE。读取lParam的LOWORD 和HIWORD可以得到窗口新的宽度和高度。将他们传递给ReSizeGLScene(),OpenGL场景将调整为新的宽度和高度。 其余无关的消息被传递给DefWindowProc,让Windows自行处理。 下面是我们的Windows程序的入口。将会调用窗口创建例程,处理窗口消息,并监视人机交互。 我们设置两个变量。msg 用来检查是否有消息等待处理。done的初始值设为FALSE。这意味着我们的程序仍未完成运行。只要程序done保持FALSE,程序继续运行。一旦done的值改变为TRUE,程序退出。 这段代码完全可选。程序弹出一个消息窗口,询问用户是否希望在全屏模式下运行。如果用户单击NO按钮,fullscreen变量从缺省的TRUE改变为FALSE,程序也改在窗口模式下运行。 接着创建OpenGL窗口。CreateGLWindow函数的参数依次为标题、宽度、高度、色彩深度,以及全屏标志。就这么简单!我很欣赏这段代码的简洁。如果未能创建成功,函数返回FALSE。程序立即退出。 // 创建OpenGL窗口 下面是循环的开始。只要done保持FALSE,循环一直进行。 我们要做的第一件事是检查是否有消息在等待。使用PeekMessage()可以在不锁住我们的程序的前提下对消息进行检查。许多程序使用GetMessage(),也可以很好的工作。但使用GetMessage(),程序在收到paint消息或其他别的什么窗口消息之前不会做任何事。 下面的代码查看是否出现退出消息。如果当前的消息是由PostQuitMessage(0)引起的WM_QUIT,done变量被设为TRUE,程序将退出。 如果不是退出消息,我们翻译消息,然后发送消息,使得WndProc() 或 Windows能够处理他们。 如果没有消息,绘制我们的OpenGL场景。代码的第一行查看窗口是否激活。如果按下ESC键,done变量被设为TRUE,程序将会退出。 如果程序是激活的且ESC没有按下,我们绘制场景并交换缓存(使用双缓存可以实现无闪烁的动画)。我们实际上在另一个看不见的"屏幕"上绘图。当我们交换缓存后,我们当前的屏幕被隐藏,现在看到的是刚才看不到的屏幕。这也是我们看不到场景绘制过程的原因。场景只是即时显示。 DrawGLScene(); // 绘制场景 下面的一点代码是最近新加的(05-01-00)。允许用户按下F1键在全屏模式和窗口模式间切换。 if (keys[VK_F1]) // F1键按下了么? 如果done变量不再是FALSE,程序退出。正常销毁OpenGL窗口,将所有的内存释放,退出程序。 // 关闭程序 在这一课中,我已试着尽量详细解释一切。每一步都与设置有关,并创建了一个全屏OpenGL程序。当您按下ESC键程序就会退出,并监视窗口是否激活。我花了整整2周时间来写代码,一周时间来改正BUG并讨论编程指南,2天( 整整22小时来写HTML文件)。如果您有什么意见或建议请给我EMAIL。如果您认为有什么不对或可以改进,请告诉我。我想做最好的OpenGL教程并对您的反馈感兴趣。 版权与使用声明: 版权声明 发展计划 感谢
[此贴子已经被作者于2007-10-12 11:54:36编辑过]
|
-- 作者:一分之千 -- 发布时间:10/12/2007 11:05:00 AM -- 第一课 英文 Lesson 01 I created this web site so that people interested in learning OpenGL would have a place to come if they needed help. In each of my tutorials I try to explain, in as much detail as humanly possible, what each line of code is doing. I try to keep my code simple (no MFC code to learn)! An absolute newbie to both Visual C++ and OpenGL should be able to go through the code, and have a pretty good idea of what's going on. My site is just one of many sites offering OpenGL tutorials. If you're a hardcore OpenGL programmer, my site may be too simplistic, but if you're just starting out, I feel my site has a lot to offer! This tutorial was completely rewritten January 2000. This tutorial will teach you how to set up an OpenGL window. The window can be windowed or fullscreen, any size you want, any resolution you want, and any color depth you want. The code is very flexible and can be used for all your OpenGL projects. All my tutorials will be based on this code! I wrote the code to be flexible, and powerful at the same time. All errors are reported. There should be no memory leaks, and the code is easy to read and easy to modify. Thanks to Fredric Echols for his modifications to the code! I'll start this tutorial by jumping right into the code. The first thing you will have to do is build a project in Visual C++. If you don't know how to do that, you should not be learning OpenGL, you should be learning Visual C++. The downloadable code is Visual C++ 6.0 code. Some versions of VC++ require that bool is changed to BOOL, true is changed to TRUE, and false is changed to FALSE. By making the changes mentioned, I have been able to compile the code on Visual C++ 4.0 and 5.0 with no other problems. After you have created a new Win32 Application (NOT a console application) in Visual C++, you will need to link the OpenGL libraries. In Visual C++ go to Project, Settings, and then click on the LINK tab. Under "Object/Library Modules" at the beginning of the line (before kernel32.lib) add OpenGL32.lib GLu32.lib and GLaux.lib. Once you've done this click on OK. You're now ready to write an OpenGL Windows program. The first 4 lines include the header files for each library we are using. The lines look like this: #include <windows.h> // Header File For Windows The first line sets up a Rendering Context. Every OpenGL program is linked to a Rendering Context. A Rendering Context is what links OpenGL calls to the Device Context. The OpenGL Rendering Context is defined as hRC. In order for your program to draw to a Window you need to create a Device Context, this is done in the second line. The Windows Device Context is defined as hDC. The DC connects the Window to the GDI (Graphics Device Interface). The RC connects OpenGL to the DC. In the third line the variable hWnd will hold the handle assigned to our window by Windows, and finally, the fourth line creates an Instance (occurrence) for our program. HGLRC hRC=NULL; // Permanent Rendering Context The active variable will be used to tell our program whether or not our Window has been minimized to the taskbar or not. If the Window has been minimized we can do anything from suspend the code to exit the program. I like to suspend the program. That way it won't keep running in the background when it's minimized. The variable fullscreen is fairly obvious. If our program is running in fullscreen mode, fullscreen will be TRUE, if our program is running in Windowed mode, fullscreen will be FALSE. It's important to make this global so that each procedure knows if the program is running in fullscreen mode or not. bool keys[256]; // Array Used For The Keyboard Routine LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); // Declaration For WndProc GLvoid ReSizeGLScene(GLsizei width, GLsizei height) // Resize And Initialize The GL Window glViewport(0, 0, width, height); // Reset The Current Viewport glMatrixMode(GL_PROJECTION) indicates that the next 2 lines of code will affect the projection matrix. The perspective matrix is responsible for adding perspective to our scene. glLoadIdentity() is similar to a reset. It restores the selected matrix to it's original state. After glLoadIdentity() has been called we set up our perspective view for the scene. glMatrixMode(GL_MODELVIEW) indicates that any new transformations will affect the modelview matrix. The modelview matrix is where our object information is stored. Lastly we reset the modelview matrix. Don't worry if you don't understand this stuff, I will be explaining it all in later tutorials. Just know that it HAS to be done if you want a nice perspective scene. glMatrixMode(GL_PROJECTION); // Select The Projection Matrix // Calculate The Aspect Ratio Of The Window glMatrixMode(GL_MODELVIEW); // Select The Modelview Matrix int InitGL(GLvoid) // All Setup For OpenGL Goes Here glShadeModel(GL_SMOOTH); // Enables Smooth Shading You create different colors by mixing the three primary colors for light (red, green, blue). Hope you learned primaries in school. So, if you had glClearColor(0.0f,0.0f,1.0f,0.0f) you would be clearing the screen to a bright blue. If you had glClearColor(0.5f,0.0f,0.0f,0.0f) you would be clearing the screen to a medium red. Not bright (1.0f) and not dark (0.0f). To make a white background, you would set all the colors as high as possible (1.0f). To make a black background you would set all the colors to as low as possible (0.0f). glClearColor(0.0f, 0.0f, 0.0f, 0.0f); // Black Background glClearDepth(1.0f); // Depth Buffer Setup glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); // Really Nice Perspective Calculations return TRUE; // Initialization Went OK The return TRUE tells our program that there were no problems. If you wanted the program to stop for some reason, adding a return FALSE line somewhere before return TRUE will tell our program that the drawing code failed. The program will then quit. int DrawGLScene(GLvoid) // Here's Where We Do All The Drawing GLvoid KillGLWindow(GLvoid) // Properly Kill The Window if (fullscreen) // Are We In Fullscreen Mode? ChangeDisplaySettings(NULL,0); // If So Switch Back To The Desktop if (hRC) // Do We Have A Rendering Context? if (!wglMakeCurrent(NULL,NULL)) // Are We Able To Release The DC And RC Contexts? MessageBox(NULL,"Release Of DC And RC Failed.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION); if (!wglDeleteContext(hRC)) // Are We Able To Delete The RC? MessageBox(NULL,"Release Rendering Context Failed.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION); if (hDC && !ReleaseDC(hWnd,hDC)) // Are We Able To Release The DC if (hWnd && !DestroyWindow(hWnd)) // Are We Able To Destroy The Window? if (!UnregisterClass("OpenGL",hInstance)) // Are We Able To Unregister Class As you can see the procedure returns BOOL (TRUE or FALSE), it also takes 5 parameters: title of the Window, width of the Window, height of the Window, bits (16/24/32), and finally fullscreenflag TRUE for fullscreen or FALSE for windowed. We return a boolean value that will tell us if the Window was created successfully. BOOL CreateGLWindow(char* title, int width, int height, int bits, bool fullscreenflag) GLuint PixelFormat; // Holds The Results After Searching For A Match WNDCLASS wc; // Windows Class Structure DWORD dwExStyle; // Window Extended Style RECT WindowRect; // Grabs Rectangle Upper Left / Lower Right Values fullscreen=fullscreenflag; // Set The Global Fullscreen Flag The style CS_HREDRAW and CS_VREDRAW force the Window to redraw whenever it is resized. CS_OWNDC creates a private DC for the Window. Meaning the DC is not shared across applications. WndProc is the procedure that watches for messages in our program. No extra Window data is used so we zero the two fields. Then we set the instance. Next we set hIcon to NULL meaning we don't want an ICON in the Window, and for a mouse pointer we use the standard arrow. The background color doesn't matter (we set that in GL). We don't want a menu in this Window so we set it to NULL, and the class name can be any name you want. I'll use "OpenGL" for simplicity. hInstance = GetModuleHandle(NULL); // Grab An Instance For Our Window if (!RegisterClass(&wc)) // Attempt To Register The Window Class if (fullscreen) // Attempt Fullscreen Mode? DEVMODE dmScreenSettings; // Device Mode // Try To Set Selected Mode And Get Results. NOTE: CDS_FULLSCREEN Gets Rid Of Start Bar. // If The Mode Fails, Offer Two Options. Quit Or Run In A Window. fullscreen=FALSE; // Select Windowed Mode (Fullscreen=FALSE) // Pop Up A Message Box Letting User Know The Program Is Closing. if (fullscreen) // Are We Still In Fullscreen Mode? Finally, we disable the mouse pointer. If your program is not interactive, it's usually nice to disable the mouse pointer when in fullscreen mode. It's up to you though. dwExStyle=WS_EX_APPWINDOW; // Window Extended Style dwExStyle=WS_EX_APPWINDOW | WS_EX_WINDOWEDGE; // Window Extended Style AdjustWindowRectEx(&WindowRect, dwStyle, FALSE, dwExStyle); // Adjust Window To True Requested Size Notice we include the styles WS_CLIPSIBLINGS and WS_CLIPCHILDREN along with the style of window we've decided to use. WS_CLIPSIBLINGS and WS_CLIPCHILDREN are both REQUIRED for OpenGL to work properly. These styles prevent other windows from drawing over or into our OpenGL Window. if (!(hWnd=CreateWindowEx( dwExStyle, // Extended Style For The Window { static PIXELFORMATDESCRIPTOR pfd= // pfd Tells Windows How We Want Things To Be if (!(hDC=GetDC(hWnd))) // Did We Get A Device Context? if (!(PixelFormat=ChoosePixelFormat(hDC,&pfd))) // Did Windows Find A Matching Pixel Format? if(!SetPixelFormat(hDC,PixelFormat,&pfd)) // Are We Able To Set The Pixel Format? if (!(hRC=wglCreateContext(hDC))) // Are We Able To Get A Rendering Context? if(!wglMakeCurrent(hDC,hRC)) // Try To Activate The Rendering Context ShowWindow(hWnd,SW_SHOW); // Show The Window if (!InitGL()) // Initialize Our Newly Created GL Window return TRUE; // Success LRESULT CALLBACK WndProc( HWND hWnd, // Handle For This Window switch (uMsg) // Check For Windows Messages case WM_ACTIVATE: // Watch For Window Activate Message return 0; // Return To The Message Loop case WM_SYSCOMMAND: // Intercept System Commands case WM_CLOSE: // Did We Receive A Close Message? case WM_KEYDOWN: // Is A Key Being Held Down? case WM_KEYUP: // Has A Key Been Released? case WM_SIZE: // Resize The OpenGL Window // Pass All Unhandled Messages To DefWindowProc int WINAPI WinMain( HINSTANCE hInstance, // Instance MSG msg; // Windows Message Structure // Ask The User Which Screen Mode They Prefer // Create Our OpenGL Window while(!done) // Loop That Runs Until done=TRUE if (PeekMessage(&msg,NULL,0,0,PM_REMOVE)) // Is There A Message Waiting? if (msg.message==WM_QUIT) // Have We Received A Quit Message? TranslateMessage(&msg); // Translate The Message // Draw The Scene. Watch For ESC Key And Quit Messages From DrawGLScene() DrawGLScene(); // Draw The Scene if (keys[VK_F1]) // Is F1 Being Pressed? // Shutdown Jeff Molofee (NeHe) |
-- 作者:一分之千 -- 发布时间:10/12/2007 11:49:00 AM -- 第二课 中文 在第一个教程的基础上,我们添加了一个三角形和一个四边形。也许你认为这很简单,但你已经迈出了一大步,要知道任何在OpenGL中绘制的模型都会被分解为这两种简单的图形。 读完了这一课,你会学到如何在空间放置模型,并且会知道深度缓存的概念。 int DrawGLScene(GLvoid) // 此过程中包括所有的绘制代码 glTranslatef(-1.5f,0.0f,-6.0f); // 左移 1.5 单位,并移入屏幕 6.0 glBegin之后的第一行设置了多边形的第一个顶点,glVertex 的第一个参数是X坐标,然后依次是Y坐标和Z坐标。第一个点是上顶点,然后是左下顶点和右下顶点。glEnd()告诉OpenGL没有其他点了。这样将显示一个填充的三角形。 glBegin(GL_TRIANGLES); // 绘制三角形 glTranslatef(3.0f,0.0f,0.0f); // 右移3单位 glBegin(GL_QUADS); // 绘制正方形 if (keys[VK_F1]) // F1键按下了么? |
-- 作者:一分之千 -- 发布时间:10/12/2007 11:51:00 AM -- Lesson 02 In the first tutorial I taught you how to create an OpenGL Window. In this tutorial I will teach you how to create both Triangles and Quads. We will create a triangle using GL_TRIANGLES, and a square using GL_QUADS. Using the code from the first tutorial, we will be adding to the DrawGLScene() procedure. I will rewrite the entire procedure below. If you plan to modify the last lesson, you can replace the DrawGLScene() procedure with the code below, or just add the lines of code below that do not exist in the last tutorial. int DrawGLScene(GLvoid) // Here's Where We Do All The Drawing The center of an OpenGL screen is 0.0f on the X and Y axis. To the left of center would be a negative number. To the right would be a positive number. Moving towards the top of the screen would be a positive number, moving to the bottom of the screen would be a negative number. Moving deeper into the screen is a negative number, moving towards the viewer would be a positive number. glTranslatef(x, y, z) moves along the X, Y and Z axis, in that order. The line of code below moves left on the X axis 1.5 units. It does not move on the Y axis at all (0.0), and it moves into the screen 6.0 units. When you translate, you are not moving a set amount from the center of the screen, you are moving a set amount from wherever you currently were on the screen. glTranslatef(-1.5f,0.0f,-6.0f); // Move Left 1.5 Units And Into The Screen 6.0 In our simple program, we draw just one triangle. If we wanted to draw a second triangle, we could include another 3 lines of code (3 points) right after the first three. All six lines of code would be between glBegin(GL_TRIANGLES) and glEnd(). There's no point in putting a glBegin(GL_TRIANGLES) and a glEnd() around every group of 3 points. This applies to quads as well. If you know you're drawing all quads, you can include the second group of four lines of code right after the first four lines. A polygon on the other hand (GL_POLYGON) can be made up of any amount of point so it doesn't matter how many lines you have between glBegin(GL_POLYGON) and glEnd(). The first line after glBegin, sets the first point of our polygon. The first number of glVertex is for the X axis, the second number is for the Y axis, and the third number is for the Z axis. So in the first line, we don't move on the X axis. We move up one unit on the Y axis, and we don't move on the Z axis. This gives us the top point of the triangle. The second glVertex moves left one unit on the X axis and down one unit on the Y axis. This gives us the bottom left point of the triangle. The third glVertex moves right one unit, and down one unit. This gives us the bottom right point of the triangle. glEnd() tells OpenGL there are no more points. The filled triangle will be displayed. glBegin(GL_TRIANGLES); // Drawing Using Triangles glTranslatef(3.0f,0.0f,0.0f); // Move Right 3 Units glBegin(GL_QUADS); // Draw A Quad if (keys[VK_F1]) // Is F1 Being Pressed? "[I mentioned] inches and millimeters - do these really have anything to do with OpenGL? The answer is, in a word, no. The projection and other transformations are inheritly unitless. If you want to think of the near and far clipping planes as located at 1.0 and 20.0 meters, inches, kilometers, or leagues, it's up to you. The only rule is that you have to use consistent unit of measurement." In this tutorial I have tried to explain in as much detail, every step involved in drawing polygons, and quads on the screen using OpenGL. If you have comments or questions please email me. If you feel I have incorrectly commented something or that the code could be done better in some sections, please let me know. I want to make the best OpenGL tutorials I can. I'm interested in hearing your feedback. Jeff Molofee (NeHe) |
-- 作者:snowtower -- 发布时间:10/16/2007 3:03:00 AM -- 强! 兄弟你这是自己翻译的?太仰慕了 |
-- 作者:wanggangzero -- 发布时间:11/22/2007 8:44:00 PM -- 回复英文版 兄弟,你写个中文版挺好的,干嘛还要翻译成英文?撑着了?好多国人头疼的是OpenGL的英文书好多,也很好,就是中文版少;以至于,每每想要深造一下,都倍感难以推进,但是,谁也不缺英文资料啊?而且范例也不错哦。总之一句话,你的中文版好,但是英文版还是留着自己看吧,不要拿出来炫了。同志们绝对没有利用你的资料再去学英文的兴趣。 |
-- 作者:卷积内核 -- 发布时间:11/23/2007 8:00:00 AM --
这是英文原稿,而不是从英文翻译成中文的。现在很多程序高手还是喜欢看英文版的哦,能看出作者最原本的意图。现在所以还是鼓励大家多看英文版的东西,因为很多优秀的资料都是英文版的,等翻译过来你再看有可能你已经跟不上计算机业界的发展潮流了。 |
-- 作者:一分之千 -- 发布时间:11/24/2007 3:57:00 PM --
兄弟,看到你的话无语了。 |
-- 作者:一分之千 -- 发布时间:11/24/2007 3:58:00 PM --
|
-- 作者:vipwkuc4 -- 发布时间:12/27/2007 2:45:00 PM -- LZ 赞] |
-- 作者:shuichuanmu -- 发布时间:5/27/2008 10:34:00 PM -- 真诚的感谢! |
-- 作者:zihenwuming -- 发布时间:12/27/2008 10:48:00 AM -- 太强大了...太太感谢了..最近因为一个图形的作业都快要被搞疯了.. |
-- 作者:riserui -- 发布时间:1/23/2009 3:55:00 PM -- 这个是个好东西 找了好久opengl 的资料,都没有比较全的 |
-- 作者:小楼昨夜 -- 发布时间:1/20/2010 5:21:00 PM -- 正在学习,谢谢 |
-- 作者:barbecue -- 发布时间:7/23/2010 5:06:00 PM -- 清华的哥哥就是牛啊! |
-- 作者:zf199012 -- 发布时间:2/18/2011 1:55:00 PM -- hahhahah |
-- 作者:m1594660 -- 发布时间:1/3/2012 9:52:00 PM -- 忽然要学这个,苦于没有资料~ |
-- 作者:lovelylcj -- 发布时间:2/16/2012 11:40:00 PM -- DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD |
W 3 C h i n a ( since 2003 ) 旗 下 站 点 苏ICP备05006046号《全国人大常委会关于维护互联网安全的决定》《计算机信息网络国际联网安全保护管理办法》 |
312.500ms |