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管线 ...
随机推荐
- ## Python中的Package和Jupyter中import包问题
前言 关于python包的一些知识 Java中的package概念 我们知道在java中的import package概念, java中的包就是一个目录,里面包含着子目录,子目录套着子目录,当需要引入 ...
- 表单的序列化ajax
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/ ...
- C++ 中关于optional 使用过程中遇到的问题
头文件:#include <boost/optional.hpp> using namespace boost; optional很像一个仅能存放一个元素的容器,它实现了"未初始 ...
- js原型二
function Box(name,age){ this.name = name; this.age = age; this.family = ['哥哥',‘姐姐’,‘妹妹’]: } Box.prot ...
- MySQL执行sql查询并上传至远程服务器
最近项目中有需要做一个shell脚本,可以对一个数据库执行sql操作,并将结果转为txt,筛选结果用tab隔开,保存至一个远程服务器上,以供其他人用Excel读取用txt中的内容. MySQL中将结果 ...
- Nmap功能与常用命令
Nmap功能与常用命令 其基本功能有三个,一是探测一组主机是否在线:其次是扫描主机端口,嗅探所提供的网络服务:还可以推断主机所用的操作系统. Nmap可用于扫描仅有两个节点的LAN,直至500个节点以 ...
- Android系统--输入系统(九)Reader线程_核心类及配置文件
Android系统--输入系统(九)Reader线程_核心类及配置文件 1. Reader线程核心类--EventHub 1.1 Reader线程核心结构体 实例化对象:mEventHub--表示多个 ...
- [HNOI2004]宠物收养场 Treap前驱后继
凡凡开了一间宠物收养场.收养场提供两种服务:收养被主人遗弃的宠物和让新的主人领养这些宠物. 每个领养者都希望领养到自己满意的宠物,凡凡根据领养者的要求通过他自己发明的一个特殊的公式,得出该领养者希望领 ...
- webapp万能选择器:iosselect
iosselect是个什么东西? 移动端浏览器对于select的展示样式是不一致的,ios下是类似原生的picker,安卓下各浏览器展示各异,我们需要一个选择器组件来统一各端下各种浏览器的展示.下面是 ...
- Codeforces Round #410 (Div. 2)C题
C. Mike and gcd problem time limit per test 2 seconds memory limit per test 256 megabytes input stan ...