点击这里 点"> GLFW--Getting started - 11GX
首页 > GLFW--Getting started

GLFW--Getting started

这篇文章是GLFW使用的简介。算是翻译吧:原文在here

Step by step

包含头文件

在使用OpenGL或GLFW工程源文件里需要包含GLFW3头文件。

#include 

这个头文件中定义了GLFW API所有的常量,类型和函数原型,同时也包含了OpenGL头文件并定义了在本系统平台上需要用到的所有常量和类型。

例如:在windows平台下,通常要求在GL/gl.h之前包含windows.h,这使得源文件与Windows绑定在一起,同时会将整个Win32 API与代码空间混淆。

然后,其实不用包含windows.h,GLFW头文件就会处理这些问题。GLFW并不是简单的复制windows.h文件,而是使用它的一部分而已。所以,当在源文件里包含了windows.h的时候,GLFW头文件就不会试着去重定义那些符号了(those symbols)。

也就是说:

  • 不要包含OpenGL头文件,GLFW会为你包含的
  • 不要包含windows.h头文件或者与系统相关的头文件,除非你想直接使用这些API。
  • 如果确实需要包含这些头文件,那么要在GLFW之前把他们包含进去,以便能先检测到这些头文件。

从3.0版本开始,GLU头文件glu.h就不再是默认包含了。如果要使用,需要在GLFW头文件之前定义GLFW_INCLUDE_GLU

#definne GLFW_INCLUDE_GLU
#include 

初始化和终止GLFW

在使用GLFW之前要对其进行初始化,初始化成功返回GL_TRUE,如果有错误则返回GL_FALSE

if(!glfwInit())exit(EXIT_FAILURE);

GLFW使用完成之后,在应用退出之前要终止GLFW

glfwTerminate();

设置错误回调函数(error callback)

大多数事件是由回调报告,比如某个键是否按下,GLFW窗口是否被移动,错误是否产生。回调(Callback)是简单地C函数(或者C++静态方法)。这些回调由带有描述事件的参数的GLFW调用。

如果GLFW函数调用失败,那么错误就会报告给GLFW的粗无回调函数(error callback)。你可以收到带有错误回调的报告。函数必须要有如下的签名(signature)。这个简单的错误回调函数只是简单的把错误描述打印到stderr。

void error_callback(int error, const char *description)
{fputs(description, stderr);
}

必须设置回调函数,这样GLFW才能调用他们。错误回调函数是GLFW函数初始化之前要调用的函数的一部分,这样在初始化时和初始化之后显示出错误。

glfwSetErrorCallback(error_callback);

创建窗口和上下文(Context)

利用一个调用就可以创建一个窗口和它的上下文,并返回一个句柄(Handle)。例如,如下创建一个640x480的窗口和OpenGL 上下文

GLFWwindow *window = glfwCreateWindow(640, 480, "My Title", NULL, NULL);

如果窗口和上下文创建失败就返回NULL,所以要检测其返回值。

if(!window)
{glfwTerminate();exit(EXIT_FAILURE);
}

窗口句柄会传递给所有与window相关的函数和所有与window相关的调用,这样就可以决定哪个window接受消息。

当不需要窗口是,就销毁它。

glfwDestoryWindow(window);

设置OpenGL的上下文

在使用OpenGL API之前,必须设置OpenGL上下文环境。

glfwMakeContextCurrent(window);

检测窗口关闭标志

每个窗口都有标志指示一个窗口是否应该关闭。

当用户试图关闭窗口(不管是点击标题栏上的关闭部件还是使用混合键Alt+F4),这个标志就会置1。注意:此时窗口并没有真正关闭,所以要检测这个标志或者由这个标志销毁窗口或者给用户一些反馈信息。

while(!glfwWindowShouldClose(window))
{//keep running
}

当用户要关闭窗口时可以通过glfwSetWindowCloseCallBack设置一个关闭回调函数(Close Callback)来通知用户。当关闭标志置1时立刻调用关闭回调函数。

接收输入事件

每个窗口都有很多回调函数用来接收各种各样事件。为了接收键盘按键和释放的事件,可以创建一个按键回调函数。

static void key_callback(GLFWwindow *window, int key, int scancode, int action, int mods)
{if(key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)glfwSetWindowShouldClose(window, GL_TRUE);
}

同其他窗口相关的调用一样,每个窗口都有对应按键回调函数。

glfwSetKeyCallback(window, key_callback);

当事件产生时,为了事件回调函数能够被调用,应该按如下方法处理事件。

与OpenGL一起渲染

当有了OpenGL上下文环境之后,就可以正常的使用OpenGL了。在这个教程里,将会绘制一个多颜色的三角形。为了使用glViewport要获取帧缓冲区的大小

int width, height;
glfwGetFramebufferSize(window, &width, &height);
glViewport(0,0, width, height);

读计时器

为了创建平滑的动画,需要time source. GLFW 提供一个计时器可以返回开始初始化到当前的时间间隔。time source 是系统最精确的通常是毫秒或者是微妙。

double time = glfwGetTime();

交换缓冲区(Swapping buffers)

GLFW默认使用双缓冲区,这表示每个窗口都有两个渲染缓冲区:前缓冲区和后缓冲区;前缓冲区是正在绘制的缓冲区,后缓冲区是等待绘制的缓冲区。

当一个帧绘制完成后,两个缓冲区需要交换,此时后缓冲区变为前缓冲区,反之亦然。

glfwSwaBuffers(window);

交换间隔是指要交换两个缓冲区之间的帧数,即vsync。默认情况下,交换间隔为0,即交换会立刻进行。在很快的一些机器上,不会看到这些帧,因为屏幕始终以60-70/s的速度更新,这会浪费很多CPU或GPU周期。

同样,因为缓冲区交换发生在屏幕更新中间,就会造成屏幕撕裂问题screen tearing

由于这个原因,应用通常会把交换间隔设置为1。也可以设置更高的值,但是通常不建议这样,因为这会导致输入延迟。

glfwSwapInterval(1);

处理事件

GLFW需要和窗口系统经常交流,为了接收事件和显示应用没有锁住。在有可见窗口时必须进行事件处理,通常在每帧缓冲区交换后。

有两种方法处理挂起事件(pending event):轮询(polling)和等待(waiting)。这个例子将会使用轮询,即仅仅在已经接收到事件时处理并立即返回。

glfwPollEvents();

在连续绘制时这是最好的选择,大多数游戏也是这样做的。如果你仅仅在接收到新的输入时需要这样做,那么glfwWaitEvents是个更好的选择。它等待至少接收到一个事件,将线程设置为休眠状态,并处理所有接收到的事件。这样做会节省大量的CPU周期,通常用于编辑工具。

总结Demo

现在,你已经知道了如何初始化GLFW、创建窗口并轮询键盘输入。此时,可以创建一个简单的程序。

#include 
#include 
#include 
static void error_callback(int error, const char* description)
{fputs(description, stderr);
}
static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods)
{if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)glfwSetWindowShouldClose(window, GL_TRUE);
}
int main(void)
{GLFWwindow* window;glfwSetErrorCallback(error_callback);if (!glfwInit())exit(EXIT_FAILURE);window = glfwCreateWindow(640, 480, "Simple example", NULL, NULL);if (!window){glfwTerminate();exit(EXIT_FAILURE);}glfwMakeContextCurrent(window);glfwSwapInterval(1);glfwSetKeyCallback(window, key_callback);while (!glfwWindowShouldClose(window)){float ratio;int width, height;glfwGetFramebufferSize(window, &width, &height);ratio = width / (float) height;glViewport(0, 0, width, height);glClear(GL_COLOR_BUFFER_BIT);glMatrixMode(GL_PROJECTION);glLoadIdentity();glOrtho(-ratio, ratio, -1.f, 1.f, 1.f, -1.f);glMatrixMode(GL_MODELVIEW);glLoadIdentity();glRotatef((float) glfwGetTime() * 50.f, 0.f, 0.f, 1.f);glBegin(GL_TRIANGLES);glColor3f(1.f, 0.f, 0.f);glVertex3f(-0.6f, -0.4f, 0.f);glColor3f(0.f, 1.f, 0.f);glVertex3f(0.6f, -0.4f, 0.f);glColor3f(0.f, 0.f, 1.f);glVertex3f(0.f, 0.6f, 0.f);glEnd();glfwSwapBuffers(window);glfwPollEvents();}glfwDestroyWindow(window);glfwTerminate();exit(EXIT_SUCCESS);
}

 

更多相关: