OpenGL教程(2)——第一个窗口
注:本文可转载,转载请注明出处:http://www.cnblogs.com/collectionne/p/6618419.html。
OpenGL环境终于配置好了,现在我们可以开始学习OpenGL了。
首先,创建一个.cpp文件,然后打上几行#include指令:
#include <iostream>
using std::cout;
using std::endl; #include <GL/glew.h>
#include <GLFW/glfw3.h>
先从<iostream>说起。其实<iostream>不是必须的,这里包含它,是因为后面可能需要在控制台上打印信息,提醒用户。glew.h和glfw3.h是我们上一篇教程里所下载的头文件,注意一定要先包含glew.h,再包含glfw3.h(因为glfw3.h会包含gl.h,而这是glew.h不允许的)。最后,如果你使用的是静态版的GLEW,需要在包含glew.h前加上下面的#define指令:
#define GLEW_STATIC
然后我们进入main()函数:
int main()
{
GLFW
首先我们需要初始化GLFW:
if (!glfwInit())
{
cout << "Failed to initialize GLFW!\n";
return -;
}
glfwInit()函数用于初始化GLFW,在调用大部分其它GLFW函数前,都需要初始化GLFW。glfwInit()如果成功,将会返回GLFW_TRUE,否则返回GLFW_FALSE(GLFW_TRUE和GLFW_FALSE是GLFW定义的常量,被定义为于1和0)。也就是说,如果返回GLFW_FALSE,就说明初始化失败,这时我们将打印一条信息告知用户GLFW初始化失败,并且返回(注意,你可能无法看到打印的信息,这时可以在return -1前加上一句cin.get()或system("pause")暂停程序)。不过,检查返回值也不是必须的。
然后我们需要使用glfwWindowHint()函数设置hint:
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, );
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, );
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
Hint直接翻译成中文是“线索”,这里大致指“选项”。使用glfwWindowHint()可以设置一些关于窗口的选项。glfwWindowHint()接受两个参数,第一个是我们要设置的hint的名字,使用GLFW常量(以GLFW_开头)指定;第二个是我们要把该hint设置成的值,该值随要设置的hint而异。
Hint也有很多种,这里我们只设置了4种,分别是OpenGL主版本号(GLFW_CONTEXT_VERSION_MAJOR)、OpenGL副版本号(GLFW_CONTEXT_VERSION_MINOR)、OpenGL模式(GLFW_OPENGL_PROFILE)、窗口是否可调整大小(GLFW_RESIZABLE)。这4个hint,我们分别设置为3、3、GLFW_CORE_PROFILE、GLFW_FALSE。
因为我们要使用OpenGL 3.3,所以我们把GLFW_CONTEXT_VERSION_MAJOR和GLFW_CONTEXT_VERSION_MINOR对应的hint都设置为3。因为我们要使用OpenGL核心模式(这个后面会提到更多),所以我们把GLFW_OPENGL_PROFILE对应的hint设置为GLFW_OPENGL_CORE_PROFILE,表示使用OpenGL核心模式。最后,把GLFW_RESIZABLE对应的hint设置为GLFW_FALSE,表示窗口不允许用户调整大小。之所以这样做是因为如果允许用户调整大小,大小发生变化后,窗口的绘制区域默认不变(依然是原来窗口的区域),也就是说窗口上绘制的图像的大小、位置不会发生改变。为了避免这种现象发生,我们就简单地不让用户调整窗口大小(当然也有更好的方法,就是用GLFW设置一个窗口大小的回调函数,但这样比较简单)。
注意,如果你使用Mac OS X,你需要再加上下面这句调用:
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE);
然后就该创建窗口了:
GLFWwindow * window = glfwCreateWindow(, , "First window", nullptr, nullptr);
if (window == nullptr)
{
cout << "Failed to create window using GLFW!\n";
return -;
}
glfwCreateWindow()接受5个参数。第一个、第二个是窗口的宽和高,以像素为单位,这里分别是800和600;第三个是窗口标题,这里是"First window";第四个和第五个参数可以忽略,直接传入nullptr。
glfwCreateWindow()将会使用前面glfwWindowHint()所设置的hint创建窗口,返回一个GLFWwindow指针,我们把这个指针叫做窗口句柄(window handle)。简单说来,就是从此以后我们都用它来代表我们的窗口,后面会需要对窗口进行设置、询问一些关于窗口的信息,就需要传入这个指针。如果创建窗口出现问题,将返回NULL(C++里就是nullptr),因此如果window为nullptr,说明创建窗口失败,打印错误信息,返回-1。
创建完毕之后,需要让当前窗口的环境在当前线程上成为当前环境(说法有点复杂,不太严谨地说,就是接下来的画图都会画在我们刚刚创建的窗口上):
glfwMakeContextCurrent(window);
GLEW
GLFW设置完毕后,还需要初始化GLEW:
glewExperimental = GL_TRUE;
if (glewInit())
{
cout << "Failed to init GLEW!\n";
return -;
}
首先我们把glewExperimental设置为GL_TRUE,这样GLEW就会使用更加新的方法管理OpenGL,减少错误。然后调用glewInit()初始化GLEW。注意glewInit()成功返回0,失败返回1,与GLFW相反。如果失败则输出错误信息,并返回-1。
游戏循环
和控制台程序不同,我们希望这个程序可以一直运行,直到用户关闭窗口。这样我们就需要创建一个循环,叫做游戏循环(game loop)。
while (!glfwWindowShouldClose(window))
{
glfwSwapBuffers(window);
glfwPollEvents();
}
glfwWindowShouldClose()检查窗口是否需要关闭。如果是,游戏循环就结束了,接下来我们将会清理资源,结束程序。
glfwSwapBuffers()用来交换窗口的两个颜色缓冲(color buffer)。这个概念叫做双缓冲(double buffer)。如果不使用双缓冲,就可能会出现闪屏现象,因为绘制一般不是一下子就绘制完毕的,而是从左到右、从上到下地绘制。为了避免这个问题,一般会使用双缓冲,前缓冲(front buffer)是最终的图像,而程序会在后缓冲(back buffer)上绘制。后缓冲绘制完毕后,就交换两个缓冲,这样就不会有闪屏的问题了。
glfwPollEvents()用来检查是否有事件被触发,例如点击关闭按钮、点击鼠标、按下键盘,等等。如果有,GLFW将会对这些事件进行处理。更严谨地说(以键盘、鼠标键为例),GLFW自己会记录每个键、鼠标键的状态(按下/没有按下),但当某个按键松开或被按下时,GLFW不会自动更新状态,必须调用glfwPollEvents()才能更新。调用glfwPollEvents()时会检查状态是否有变化(如按下的是否松开,没有按下的是否被按下),如果有就会更新该状态。如果设置了回调函数(这个将在以后讲),还会调用相应的回调函数。如果不调用这个函数,不仅无法检测输入(后文会需要这样),我们在点击窗口右上角的X时,GLFW也不会知道需要关闭窗口。所以必须在每一轮游戏循环中调用这个函数。
最后,当循环执行完毕后,我们需要释放前面所申请的资源:
glfwTerminate();
return ;
}
glfwTerminate()将会释放所有GLFW资源,关闭当前窗口。然后程序就成功退出了。
现在运行你的程序,如果你得到了一个黑色窗口,恭喜你,成功了!如果没有,请参考结尾的代码,对照自己的代码。
额外的东西
现在我们已经成功创建窗口,并且在用户点击右上角的X时退出。但是还不够——你可能会问,可不可以让用户按ESC键时退出,或者换一个窗口背景颜色?这里我们就来实现这两项。
关于第一个功能,可以这样写(在游戏循环里):
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
glfwSetWindowShouldClose(window, GLFW_TRUE);
glfwGetKey()用来判断一个键是否按下。第一个参数是GLFW窗口句柄,第二个参数是一个GLFW常量,代表一个键。GLFW_KEY_ESCAPE表示Esc键。如果Esc键按下了,glfwGetKey将返回GLFW_PRESS(值为1),否则返回GLFW_RELEASE(值为0)。
如果Esc被按下了,我们就会调用glfwSetWindowShouldClose()函数,为窗口设置关闭标志。第一个参数是窗口句柄,第二个参数表示是否关闭,这里为GLFW_TRUE,表示关闭该窗口。注意,这时窗口不会立即被关闭,但是glfwWindowShouldClose()将返回GLFW_TRUE,到了glfwTerminate()就会关闭窗口。
关于第二个功能,可以在进入游戏循环之前,加一句:
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
与其它glfw开头的函数(这些函数由GLFW提供)不同,这个函数是以gl开头的,是OpenGL的函数。glClearColor用来设置窗口被清除时的颜色,也就是背景颜色。前3个参数分别是背景颜色的R、G、B分量,范围是0~1;第4个是alpha值表示透明度,这里只是设为1.0f,表示不透明。(0.2, 0.3, 0.3)这一颜色大致是较暗的蓝绿色。
然后在游戏循环中加一句:
glClear(GL_COLOR_BUFFER_BIT);
这将会清除当前窗口,把所有像素的颜色都设置为前面所设置的清除颜色。GL_COLOR_BUFFER_BIT是OpenGL定义的常量,表示清除颜色缓存。后面会学到,还有GL_DEPTH_BUFFER_BIT和GL_STENCIL_BUFFER_BIT。
现在运行程序,你应该会得到下面的画面,并且按Esc时会关闭窗口:
最终代码
#include <iostream>
using std::cout;
using std::endl; #include <GL/glew.h>
#include <GLFW/glfw3.h> int main()
{
// init GLFW
if (!glfwInit())
{
cout << "Failed to init GLFW!\n";
return -;
} // set window hints
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, );
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, );
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
// create the window
GLFWwindow * window = glfwCreateWindow(, , "First window", nullptr, nullptr);
if (window == nullptr)
{
cout << "Failed to create window using GLFW!\n";
return -;
}
glfwMakeContextCurrent(window); // init GLEW
glewExperimental = GL_TRUE;
if (glewInit())
{
cout << "Failed to init GLEW!\n";
return -;
} // set clear color
glClearColor(0.2f, 0.3f, 0.3f, 1.0f); // game loop
while (!glfwWindowShouldClose(window))
{
// if Esc is pressed, close the window
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
glfwSetWindowShouldClose(window, GLFW_TRUE); // clear the window
glClear(GL_COLOR_BUFFER_BIT); // poll and swap
glfwSwapBuffers(window);
glfwPollEvents();
}
// cleanup
glfwTerminate();
return ;
}
好啦,你已经学会了创建窗口的基本技巧,下一课,我们将会学习如何画我们的第一个三角形!
OpenGL教程(2)——第一个窗口的更多相关文章
- OpenGL教程(3)——第一个三角形
我们已经学会了创建窗口,这一讲,我们将学习如何使用现代OpenGL画一个三角形.在开始写代码之前,我们需要先了解一些OpenGL概念.本文会很长,请大家做好心理准备~ 注:以下OpenGL概念翻译自h ...
- NeHe OpenGL教程 第一课:OpenGL窗口
转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...
- Nehe OpenGL教程第一课-创建一个OpenGL窗口(Win32)
原文英文地址为:Creating an OpenGL Window (Win32),翻译的chm中文格式文档下载地址为:OpenGL教程电子书(chm格式)中文版,源代码在官网上也可以下载到,每 ...
- NeHe OpenGL教程 第四十二课:多重视口
转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...
- NeHe OpenGL教程 第三十八课:资源文件
转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...
- NeHe OpenGL教程 第三十五课:播放AVI
转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...
- NeHe OpenGL教程 第三十二课:拾取游戏
转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...
- NeHe OpenGL教程 第二十四课:扩展
转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...
- NeHe OpenGL教程 第二十二课:凹凸映射
转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...
随机推荐
- 关于php 高并发解决的一点思路
涉及抢购.秒杀.抽奖.抢票等活动时,为了避免超卖,那么库存数量是有限的,但是如果同时下单人数超过了库存数量,就会导致商品超卖问题.那么我们怎么来解决这个问题呢,我的思路如下(伪代码): sql1:查询 ...
- 纯原生javascript实现分页效果
随着近几年前端行业的迅猛发展,各种层出不穷的新框架,新方法让我们有点眼花缭乱. 最近刚好比较清闲,所以没事准备撸撸前端的根基javascript,纯属练练手,写个分页,顺便跟大家分享一下 functi ...
- CF Manthan, Codefest 16 G. Yash And Trees 线段树+bitset
题目链接:http://codeforces.com/problemset/problem/633/G 大意是一棵树两种操作,第一种是某一节点子树所有值+v,第二种问子树中节点模m出现了多少种m以内的 ...
- oracle 归档日志满 报错ORA-00257: archiver error. Connect internal only, until freed
归档日志满导致无法用户无法登陆 具体处理办法 --用户登陆 Microsoft Windows [Version 6.1.7601] Copyright (c) Microsoft Corporati ...
- Windows7下安装IIS
1.点击开始→控制面板,然后再点击程序,勿点击卸载程序,否则到不了目标系统界面. 2.然后在程序和功能下面,点击打开和关闭windows功能. 3.进入Windows功能窗口,然后看到internet ...
- smart beta
本文来至人大经济论坛,http://bbs.pinggu.org/thread-3151691-1-1.html 众所周知,beta在CAPM模型中衡量了相对于持有整个市场所带来的风险溢价(risk ...
- 关于echarts的那些事(地图标点,折线图,饼图)
前记:离上一篇博客的发布已经过去两个月了,这期间总想写点什么,却怎么都写不出来,一直拖到了现在.现在的感觉,不是像这期间一样,想好好整理一番,写一篇好博客,却写不出来.事实发现,随心就好,较好的博客, ...
- 画地为Mask,随心所欲的高效遮罩组件[Unity]
在上一篇博文"扔掉遮罩,更好的圆形Image组件"中,笔者改变Image的顶点数据,使得Image呈圆形显示,避免了Mask的使用,从而节省Drawcall消耗,提高渲染效率了.这 ...
- OC 中 @synthesize 关键字介绍和使用
@synthesize用法 )@property int age; @synthesize age; 表示生成.h中变量 age的 get和 set方法 注意: 如果@synthesize 变量名要先 ...
- 查看当前用户名称:whoami命令
没什么可讲的,就是显示当前用户名称,效果同"id -un"命令.