GLFW 简单入门学习

write by 九天雁翎(JTianLing) -- blog.csdn.net/vagrxie

讨论新闻组及文件

概要

实际学习使用GLFW创建窗口,并绘制图形。作为比较,可以参考一篇关于SDL的类似文章

前言

使用SDL时,对其使用的简单印象非常深刻,假如没有效率上的原因,(SDL据说效率也不差)我想,即使是做一般的游戏引擎都可以考虑用SDL来实现。现在尝试使用一下GLFW( http://www.glfw.org/),GLFW在国内并不是很出名,第一次听说也是从Orx的作者iarwain那里,SDL那篇文章已经说过了,因为SDL给我的感觉非常好,我很难想象GLFW会超过它,但是,平时想真的自己使用OpenGL的时候,有个框架可以使用也挺不错的,而且可以不使用glut,使得真的需要进一步做成产品时,不至于像使用glut那样受到限制。另外,GLFW使用的是zlib的协议,比SDL更加自由。

实际的使用

现在的最新版GLFW是2.6,我下载的是源码包,下载后,support目录有VS的工程,有两个projects,一个是编译成lib,(因为GLFW使用的是zlib协议,所以这也是允许的)一个是编译成dll。我为了减少编译和链接的时间,使用dll。编译成库以后,使用时,只用包含一个头文件即可,只需要一个头文件。。。。。
其实,假如你原因,GLFW的文件并不是很多,全部包含进自己的工程也可以。(这也不违反协议)而且,我注意到support目录下有很多有意思的东西,包括通过lua,basic,汇编语言来使用GLFW和OpenGL的例子,也算是很有意思了。因为GLFW的接口比较少,所以,事实上,做一个语言的绑定工作的工作量不是太大,而且提供的都是C接口,这让语言的绑定更加简单,只要该语言支持C语言接口,而且有了OpenGL的绑定。虽然GLFW没有提供Python绑定,不过我感觉也不难做。呵呵,这些都是题外话了。
编译后创建工程,这些都没有什么好说的了,不过第一次编译一个使用了glfwInit函数的小工程,竟然报链接错误,我很奇怪,网上查了后,发现需要自己定义一个GLFW_DLL的宏,这点倒是比较奇怪。见此贴 。心理有点不快,这点在官方的user guide中也没有提及。

创建窗口

一直记得在Windows下使用OpenGL有多么的麻烦,见《Win32 OpenGL 编程(1)Win32下的OpenGL编程必须步骤 》,那甚至值得用一篇论文来专门描述。使用GLFW呢?
也还算简单,主要是调用glfwOpenWindow这个函数,参数比较多,但是还算容易理解。当我按照网上少有的教程 ,(这点也体现了越流行的东西越好用的道理,SDL的教程太丰富了,其中很好的也很多,我很容易就上手了。GLFW就没有那么好了,我联想到自己用Orx的经历,更加发现如此。)开始尝试运行起一个程序时,我发现一个问题,我建立自己的主循环时,(GLFW也没有内部为你控制)尽管加入了对键盘的响应,但是还是没有用,窗口仍然死在那里。
类似这样:
//错误示范,切勿模仿
int _tmain(int argc, _TCHAR* argv[])
{
if (!glfwInit()) {
printf("GLFW init error." );
}

if (!glfwOpenWindow(800 , 600 , 6 , 6 , 6 , 0 , 32 , 0 , GLFW_WINDOW) ) {
glfwTerminate();
exit(1 );
}
glfwSetWindowTitle("The GLFW Window" );

// this just loops as long as the program runs
while (true ) {
if (glfwGetKey(GLFW_KEY_ESC) == GLFW_PRESS) {
break ;
}

glfwSleep(0.05 );
}

glfwTerminate();

return 0 ;
}

我不得其解,后来感觉主循环可能需要加入事件的轮询吧,而且还发现了GLFW中有类似的机制,pollevent的函数也在那里,但是这个教程为什么没有用却可以呢?后来在GLFW的文档中找到这么一句:

If it is not desirable that glfwPollEvents is called implicitly from glfwSwapBuffers, call glfwDisable
with the argument GLFW_AUTO_POLL_EVENTS.

我就晕了,glfwSwapBuffers竟然隐含着调用了poolEvents。。。。。。。。。。我无语。因为我暂时没有绘制图形,所以从网上的教程中去掉了此句,这正是问题所在,在glfwSleep(0.05 ); 前添加glfwSwapBuffers后,问题解决。再次感叹,好的接口容易让人用对,坏的接口反之。。。。。。。不要去说是用某个东西前需要将文档全看了话,那是狡辩,好的语言,API设计,应该让人仅仅看了一部分,也能正常的工作和使用,即使是想当然的撞大运编程方式,也应该让人撞对才对,这才符合最小惊讶原则。。。。。。

用到这里,我得感叹一句,GLFW的user guide写的真的不咋地,和reference没有区别,狂列举API,却缺少实际的例子,这和reference有啥区别,user guide应该就是写来让人快速掌握的。

OpenGL使用

有了上述的东西,基本上OpenGL的环境已经搭好了,可以尝试用OpenGL干点什么了。

这里将原来学OpenGL中的教程的例子《Win32 OpenGL 编程(1)Win32下的OpenGL编程必须步骤 》拉过来改改,先尝试一下。


#include "stdafx.h"
#include "stdlib.h"
#include "gl/glfw.h"
#define WINDOW_WIDTH ( 800 )
#define WINDOW_HEIGHT ( 600 )

//OpenGL初始化开始
void SceneInit(int w,int h)
{
glClearColor(0.0f , 0.0f , 0.0f , 0.0f );// 黑色背景
glColor3f(1.0f , 1.0f , 1.0f );

glShadeModel(GL_FLAT);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(-50.0f , 50.0f , -50.0f , 50.0f , -1.0f , 1.0f );
}

//这里进行所有的绘图工作
void SceneShow(GLvoid) {
// 旋转角度

static float fSpin = 0.0f ;

fSpin += 2.0f ;

if (fSpin > 360.0f ) {
fSpin -= 360.0f ;
}

glClear(GL_COLOR_BUFFER_BIT);
glPushMatrix();

// 旋转矩形的主要函数
glRotatef(fSpin, 0.0f , 0.0f , 1.0f );
glRectf(-25.0 , -25.0 , 25.0 , 25.0 );
glPopMatrix();

// 交换缓冲区
glfwSwapBuffers();
}

int _tmain(int argc, _TCHAR* argv[])
{
if (!glfwInit()) {
printf("GLFW init error." );
}

if (!glfwOpenWindow(WINDOW_WIDTH, WINDOW_HEIGHT, 6 , 6 , 6 , 0 , 32 , 0 , GLFW_WINDOW) ) {
glfwTerminate();
exit(1 );
}
glfwSetWindowTitle("The GLFW Window" );

SceneInit(WINDOW_WIDTH, WINDOW_HEIGHT);

// this just loops as long as the program runs
while (true ) {
if (glfwGetKey(GLFW_KEY_ESC) == GLFW_PRESS) {
break ;
}

SceneShow();
glfwSleep(0.05 );
}

glfwTerminate();

return 0 ;
}

运行正常,总的来说在GLFW中搭建一个OpenGL的环境还是很简单的吧,起码比Windows下使用Win32 API来使用OpenGL要简单的多,并且,这还是跨平台的。(知道和很多人说跨平台就是废话)


真的用GLFW使用过OpenGL后,发现其实还算是比较简单,我的那些话也有些苛责了。虽然我一向很喜欢真的了解底层,比如Win32 API,比如cocoa,但是,能够有个跨平台的库,为我将这些东西都管理起来,就算我要写什么,那也会简单很多。另外,虽然SDL也能做到这些,但是相对于GLFW对于OpenGL的直接支持,(SDL中其实也可以)我感觉用起来还是更加亲切一些。

图形的显示

这才是最终的目的。
真正的位图的显示,在OpenGL中都不是那么容易的,需要掌握一堆的东西。见《Win32 OpenGL编程(15) 位图显示 》《Win32 OpenGL编程(16) 纹理贴图 》,那是因为OpenGL中实际完全对图形的显示没有直接的支持。听起来有些奇怪。。。实际的意思就是,OpenGL的API完全不理解位图,png图的含义。(虽然在上述15中提到一些bmp的操作接口,但是很遗憾的,实际的使用中都是使用纹理贴图,即16中提到的东西) 在GLFW中呢?我看到GLFW有对图形操作的接口。可是遗憾的是仅支持TGA,连BMP都不支持,不知道这种取舍是为啥,一般而言,我感觉,支持bmp的话,是最基础的。
这里还是用《SDL 简单入门学习 》中的那个龙图。
其中,图形文件操作的接口分两种,这里只看OpenGL常用的使用纹理贴图的方式。用到的API名字叫glfwLoadTexture2D。先显示个tga试一下。
代码的主要部分还是OpenGL,所以可以参考《Win32 OpenGL编程(16) 纹理贴图 》中的代码,仅仅借用了glfw的glfwLoadTexture2D函数而已。主要代码如下:

#include "stdafx.h"
#include "stdlib.h"
#include "gl/glfw.h"
#define WINDOW_WIDTH ( 800 )
#define WINDOW_HEIGHT ( 600 )

GLuint gTexName;
//OpenGL初始化开始
void SceneInit(int w,int h) {
glClearColor(0.0f , 0.0f , 0.0f , 0.0f );// 黑色背景
glColor3f(1.0f , 1.0f , 1.0f );

glShadeModel(GL_FLAT);

glGenTextures(1 , &gTexName);
glBindTexture(GL_TEXTURE_2D, gTexName);

if ( !glfwLoadTexture2D("dragon.tga" , GLFW_BUILD_MIPMAPS_BIT) ) {
printf("glfw load the file failed." );
}

// Use trilinear interpolation for minification
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
GL_LINEAR_MIPMAP_LINEAR );
// Use bilinear interpolation for magnification

glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
GL_LINEAR );

// Enable texturing
glEnable( GL_TEXTURE_2D );
}

//这里进行所有的绘图工作
void SceneShow(GLvoid) {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glBindTexture(GL_TEXTURE_2D, gTexName);

glBegin(GL_QUADS);
glTexCoord2f(0.0 , 0.0 ); glVertex3f(-1.0 , -1.0 , 0.0 );
glTexCoord2f(1.0 , 0.0 ); glVertex3f(1.0 , -1.0 , 0.0 );
glTexCoord2f(1.0 , 1.0 ); glVertex3f(1.0 , 1.0 , 0.0 );
glTexCoord2f(0.0 , 1.0 ); glVertex3f(-1.0 , 1.0 , 0.0 );

glEnd();

// 交换缓冲区
glfwSwapBuffers();
}

int _tmain(int argc, _TCHAR* argv[])
{
if (!glfwInit()) {
printf("GLFW init error." );
}

if (!glfwOpenWindow(WINDOW_WIDTH, WINDOW_HEIGHT, 6 , 6 , 6 , 0 , 32 , 0 , GLFW_WINDOW) ) {
glfwTerminate();
exit(1 );
}
glfwSetWindowTitle("The GLFW Window" );

SceneInit(WINDOW_WIDTH, WINDOW_HEIGHT);

// this just loops as long as the program runs
while (true ) {
if (glfwGetKey(GLFW_KEY_ESC) == GLFW_PRESS) {
break ;
}

SceneShow();
glfwSleep(0.05 );
}

glfwTerminate();

return 0 ;
}

感觉相对于图像显示来说,glfw并没有如SDL那般省事,因为毕竟还是全程使用OpenGL,对比原来OpenGL中显示位图的代码来说,仅仅是没有调用Windows API了而已,仅仅是多了跨平台的特性而已,并没有简化工作,而且,glfw同样的,也没有内置png图形的支持。虽然说tga在3D中用的非常多,主要是因为无损压缩,但是2D中,我还是喜欢使用png,因为小的多。当然,一旦使用png,无可避免的会需要使用libpng/zlib,所以GLFW为了保持自身的简单,没有做这样的工作吧,相对来说tga的解压就要简单太多了。
另外,GLFW还有glfwReadImage函数可以将tga图直接读入内存,然后获取到图形的相关信息的办法(上面就没有办法获取到图形的宽高)。但是使用上都已经差不多了。


都到了这个地步了,不显示一下GLFW对OpenGL的强力支持,所以做3D比较方便都不像话了。这里套用原来的代码。见《Win32 OpenGL编程(16) 纹理贴图 》。

//OpenGL初始化开始
void SceneInit(int w,int h) {
glClearColor(0.0f , 0.0f , 0.0f , 0.0f );// 黑色背景
glColor3f(1.0f , 1.0f , 1.0f );

glViewport(0 ,0 ,WINDOW_WIDTH,WINDOW_HEIGHT);// Reset The Current Viewport

glMatrixMode(GL_PROJECTION);// Select The Projection Matrix
glLoadIdentity(); // Reset The Projection Matrix

// Calculate The Aspect Ratio Of The Window
gluPerspective(45.0f ,(GLfloat)WINDOW_WIDTH/(GLfloat)WINDOW_HEIGHT,0.1f ,100.0f );

glMatrixMode(GL_MODELVIEW); // Select The Modelview Matrix
glLoadIdentity(); // Reset The Modelview Matrix


glGenTextures(1 , &gTexName);
glBindTexture(GL_TEXTURE_2D, gTexName);

if ( !glfwLoadTexture2D("dragon.tga" , GLFW_BUILD_MIPMAPS_BIT) ) {
printf("glfw load the file failed." );
}

// Use trilinear interpolation for minification
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
GL_LINEAR_MIPMAP_LINEAR );
// Use bilinear interpolation for magnification

glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
GL_LINEAR );

glEnable(GL_DEPTH_TEST);
// Enable texturing
glEnable( GL_TEXTURE_2D );
}

//这里进行所有的绘图工作
void SceneShow(GLvoid) {
static float xrot = 0.0f ,yrot = 0.0f ,zrot = 0.0f ;
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity(); // Reset The View
glTranslatef(0.0f ,0.0f ,-5.0f );

glRotatef(xrot,1.0f ,0.0f ,0.0f );
glRotatef(yrot,0.0f ,1.0f ,0.0f );
glRotatef(zrot,0.0f ,0.0f ,1.0f );

glBindTexture(GL_TEXTURE_2D, gTexName);

glBegin(GL_QUADS);
// Front Face
glTexCoord2f(0.0f , 0.0f ); glVertex3f(-1.0f , -1.0f ,1.0f );
glTexCoord2f(1.0f , 0.0f ); glVertex3f( 1.0f , -1.0f ,1.0f );
glTexCoord2f(1.0f , 1.0f ); glVertex3f( 1.0f ,1.0f ,1.0f );
glTexCoord2f(0.0f , 1.0f ); glVertex3f(-1.0f ,1.0f ,1.0f );
// Back Face
glTexCoord2f(1.0f , 0.0f ); glVertex3f(-1.0f , -1.0f , -1.0f );
glTexCoord2f(1.0f , 1.0f ); glVertex3f(-1.0f ,1.0f , -1.0f );
glTexCoord2f(0.0f , 1.0f ); glVertex3f( 1.0f ,1.0f , -1.0f );
glTexCoord2f(0.0f , 0.0f ); glVertex3f( 1.0f , -1.0f , -1.0f );
// Top Face
glTexCoord2f(0.0f , 1.0f ); glVertex3f(-1.0f ,1.0f , -1.0f );
glTexCoord2f(0.0f , 0.0f ); glVertex3f(-1.0f ,1.0f ,1.0f );
glTexCoord2f(1.0f , 0.0f ); glVertex3f( 1.0f ,1.0f ,1.0f );
glTexCoord2f(1.0f , 1.0f ); glVertex3f( 1.0f ,1.0f , -1.0f );
// Bottom Face
glTexCoord2f(1.0f , 1.0f ); glVertex3f(-1.0f , -1.0f , -1.0f );
glTexCoord2f(0.0f , 1.0f ); glVertex3f( 1.0f , -1.0f , -1.0f );
glTexCoord2f(0.0f , 0.0f ); glVertex3f( 1.0f , -1.0f ,1.0f );
glTexCoord2f(1.0f , 0.0f ); glVertex3f(-1.0f , -1.0f ,1.0f );
// Right face
glTexCoord2f(1.0f , 0.0f ); glVertex3f( 1.0f , -1.0f , -1.0f );
glTexCoord2f(1.0f , 1.0f ); glVertex3f( 1.0f ,1.0f , -1.0f );
glTexCoord2f(0.0f , 1.0f ); glVertex3f( 1.0f ,1.0f ,1.0f );
glTexCoord2f(0.0f , 0.0f ); glVertex3f( 1.0f , -1.0f ,1.0f );
// Left Face
glTexCoord2f(0.0f , 0.0f ); glVertex3f(-1.0f , -1.0f , -1.0f );
glTexCoord2f(1.0f , 0.0f ); glVertex3f(-1.0f , -1.0f ,1.0f );
glTexCoord2f(1.0f , 1.0f ); glVertex3f(-1.0f ,1.0f ,1.0f );
glTexCoord2f(0.0f , 1.0f ); glVertex3f(-1.0f ,1.0f , -1.0f );
glEnd();

xrot+=0.3f ;
yrot+=0.2f ;
zrot+=0.4f ;


// 交换缓冲区
glfwSwapBuffers();
}

效果:


其实最后一个例子已经与介绍GLFW没有关系了,新添加的部分纯粹属于OpenGL的内容,这里参看原来的文章,这里不多加解释了。仅仅用于演示,当使用了OpenGL后,3D图形的使用的方便。

小结

GLFW无愧于其号称的lightweight的OpenGL框架,的确是除了跨平台必要做的事情都没有做,所以一个头文件,很少量的API,就完成了任务。GLFW的开发目的是用于替代glut的,从代码和功能上来看,我想它已经完全的完成了任务,(虽然从历史原因上考虑还没有,毕竟红宝书都还是用glut。。。)并且glfw还在持续的开发当中,虽然作者总说他很忙。
作为与SDL(参考《SDL 简单入门学习》 )比较而写的两篇文章。这里也做个小结。相对来说,SDL真的将API做的很简单,而且因为使用的人比较多,所以第3方扩展也做的很好,并且,碰到问题,你比较容易找到答案。假如是做2D应用,SDL真的已经非常不错了。GLFW作为一个跨平台的OpenGL框架,也出色的完成了任务,不过因为定位不同,封装较少,所以在做一些基本任务的时候,因为OpenGL本身的复杂性,会复杂一些。同时,资料也少的多,所以我写本文花费的时间比SDL那篇就要长了很多,说明学习周期也会长一些。。。。(但是个人感觉类似GLFW,SDL这样的库,学习周期基本为0。。。。比起编写图形或者游戏的其他方面来说可以忽略不计)但是,同时的,GLFW封装的少,也带来更大的灵活性,我们可以还是自由的用OpenGL完成工作,也可以完成自己的进一步封装,相当于GLFW将跨平台的一些脏活累活都干了,我们就剩最核心的渲染去自由发挥了。碰到一个任务,该怎么在两者之间选择呢?出于简单考虑,2D方面的东西用SDL做实在再合适和简单不过了,但是假如想要学习OpenGL或者是做3D应用,GLFW的确是不错的选择。绝对比在glut上的投入要值,当然,其实因为glut实在太过简单,其实出于教学/学习目的去看一下也实在没有什么投入,实际用OpenGL开发东西的时候,还是选择用GLFW做吧。
也许,下一步,我可以看看怎么在SDL中使用OpenGL。在Windows下,SDL的默认渲染API使用的是D3D,不知道是否可以更改,并且完全使用OpenGL来工作呢?

原创文章作者保留版权 转载请注明原作者 并给出链接

write by 九天雁翎(JTianLing) -- blog.csdn.net/vagrxie







  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值