Win32 OpenGL编程(14) 显示列表

系统 1970 0

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

讨论新闻组及文件

前段时间因为刚换工作,很多东西都没有步入正轨,因为工作原因,学习了很多其他的东西,比如irrlicht等,现在慢慢缓下来了,还是好好的将OpenGL学习完。呵呵,其实这个系列主要还是自己学习为主,但是选择了一条比较累的写博方式,其实一边学习一般完成NEHE教程的Win32代码就可以了,但是选择了跟随《 OpenGL Programming Guide 》的方式,这样会比NEHE学的更详细一些,但是因为自己需要额外打很多字,所以也累了很多。闲话少说,继续学习。

显示列表

显示列表可以提高性能,事实上就像把一对的函数存入OpenGL的缓存中,然后能很快的调用一样,事实上,比普通的函数调用的优点仅在于速度更快,但是却无法更改。甚至包括参数都无法更改。。。。。。。相当于用牺牲灵活性来换取一定的性能。。。。。当然,在对图形程序的性能追求中,牺牲的又何止是灵活性啊。。。。。。可读性,可维护性,扩展性。。。。。都壮烈牺牲过

效率对比

下面具体来看看例子,为了更加具体的表现显示列表对性能的优化,一两个球的绘制是没有办法的,我决定绘制在屏幕上绘制100个球,源代码利用原来文章中的2009-11-9/LightSimple,这样还可以加大动态光照的运算,进一步加大运算量。利用普通函数完成这样的工作,用已有的知识已经没有问题了,代码如下:

    
      void 
    
    
      DrawAll
    
    
()
{
    
    
      glPushMatrix
    
    
();
    
    
      glTranslatef
    
    
(-0.9, 0.9, 0.0);
    
    
      for
    
    
(
    
      int 
    
    
      i 
    
    
= 0; 
    
      i 
    
    
< 10; ++
    
      i
    
    
)
    {
        
    
      for
    
    
(
    
      int 
    
    
      j 
    
    
= 0; 
    
      j 
    
    
< 10; ++
    
      j
    
    
)
        {
            
    
      glutSolidSphere
    
    
(0.1, 30, 16); 
    
      // r = 1 / 20
            
    
    
      glTranslatef
    
    
(0.2, 0.0, 0.0);
        }
        
    
      glTranslatef
    
    
(-2.0, -0.2, 0.0);
    }
    
    
      glPopMatrix
    
    
();
}
  

代码的含义不解释应该也很清楚,先移到屏幕左上角,每绘制一个球移动一个球的距离,绘制下一个,每10个球绘制后换一行,并且移动到开头,继续绘制。

然后放开fps的限制,加入一段计算fps的代码:

    
      // called every frame

    
    
      int 
    
    
      CalculateFPS
    
    
(
    
      DWORD now
    
    
)
{
    
    
      static int 
    
    
      frameCounted 
    
    
= 0;
    
    
      static int 
    
    
      startTime 
    
    
= 
    
      GetTickCount
    
    
();
    
    
      static int 
    
    
      fps 
    
    
= 0;
    ++
    
      frameCounted
    
    
;

    
    
      int 
    
    
      elapsed 
    
    
= 
    
      now 
    
    
- 
    
      startTime
    
    
;

    
    
      if 
    
    
(
    
      elapsed 
    
    
>= 1500 )
    {
        
    
      fps 
    
    
= ( 1000 * 
    
      frameCounted 
    
    
) / 
    
      elapsed
    
    
;
        
    
      startTime 
    
    
= 
    
      now
    
    
;
        
    
      frameCounted 
    
    
= 0;
    }

    
    
      return 
    
    
      fps
    
    
;
}
  

为了这里特别写的,假如有什么问题请指出来,含义也很简单,每1500毫秒(一秒半)算一下fps,为什么用1500毫秒,是感觉要是定在1000,好像误差会大点,事实上,这里用的时间越久,误差越小,但是计算的间隔长了,不能及时的反应变化。

对了,为了更好的展示效率,使用了离屏绘制,(双缓冲)不明白的看看第一节。

效果及大概的运行效率如下图所示:

image

基本上在150~200帧左右。(因为抓屏的瞬间会导致瞬间的fps下降)

我们再看看显示列表的版本:

利用一下几句生成新的显示列表:

    
      giTorus 
    
    
= 
    
      glGenLists
    
    
(2);


    
      glNewList
    
    
(
    
      giTorus
    
    
, 
    
      GL_COMPILE
    
    
);
    
    
      glutSolidSphere
    
    
(0.1, 30, 16); 
    
      // r = 1 / 20

    
    
      glEndList
    
    
();


    
      glNewList
    
    
(
    
      giTorus
    
    
+1, 
    
      GL_COMPILE
    
    
);

    
      glPushMatrix
    
    
();

    
      glTranslatef
    
    
(-0.9, 0.9, 0.0);

    
      for
    
    
(
    
      int 
    
    
      i 
    
    
= 0; 
    
      i 
    
    
< 10; ++
    
      i
    
    
)
{
    
    
      for
    
    
(
    
      int 
    
    
      j 
    
    
= 0; 
    
      j 
    
    
< 10; ++
    
      j
    
    
)
    {
        
    
      glCallList
    
    
(
    
      giTorus
    
    
);
        
    
      glTranslatef
    
    
(0.2, 0.0, 0.0);
    }
    
    
      glTranslatef
    
    
(-2.0, -0.2, 0.0);
}

    
      glPopMatrix
    
    
();

    
      glEndList
    
    
();
  

然后再调用:

glCallList(giTorus+1);

基本上就好了,与一般的OpenGL特性不同的是,显示列表使用上非常简单,但是知道什么时候应该使用,却需要一定的知识和经验,在《 OpenGL Programming Guide 》中,甚至有专门的一节《显示列表的设计哲学》来阐述这个。

我们看看效果:

image

基本上在370~450之间(同样因为抓屏的瞬间会导致瞬间的fps下降)

从fps上来看,效率成倍的增加。这就是显示列表的作用,仅仅这么一个小程序都无法掩盖其强大。。。。。。

使用

显示列表牵涉的API并不算太多,而且很好理解:

OpenGL Reference Manual 》:

GLuint glGenLists( GLsizei range);
Parameters

range

Specifies the number of contiguous empty display lists
to be generated.

glGenLists返回一个由range指定长度的显示列表起始标记,相当于普通操作的分配内存,返回的第一个内存的指针,glGenLists返回的也是第一个显示列表的标志,但是是以整数值来表示的。

然后,再就是glNewList和glEndList函数对,指示了一个显示列表的开始及结束,具体指示的显示列表由glNewList的第一个参数决定,就像定义一个函数的“{”和“}” 一样。

再然后,就是glCallList用于调用显示列表,就像调用函数一样,用参数指定调用的是哪个显示列表。

    
      giTorus 
    
    
= 
    
      glGenLists
    
    
(2);


    
      glNewList
    
    
(
    
      giTorus
    
    
, 
    
      GL_COMPILE
    
    
);
    
    
      glutSolidSphere
    
    
(0.1, 30, 16); 
    
      // r = 1 / 20

    
    
      glEndList
    
    
();
  

以上的4句已经定义了一个完整的显示列表了,分配,定义,都齐了。GL_COMPILE参数表示仅仅定义,但是不执行。(就像真正的函数第一次定义时那样)

    
      glNewList
    
    
(
    
      giTorus
    
    
+1, 
    
      GL_COMPILE
    
    
);

    
      glPushMatrix
    
    
();

    
      glTranslatef
    
    
(-0.9, 0.9, 0.0);

    
      for
    
    
(
    
      int 
    
    
      i 
    
    
= 0; 
    
      i 
    
    
< 10; ++
    
      i
    
    
)
{
    
    
      for
    
    
(
    
      int 
    
    
      j 
    
    
= 0; 
    
      j 
    
    
< 10; ++
    
      j
    
    
)
    {
        
    
      glCallList
    
    
(
    
      giTorus
    
    
);
        
    
      glTranslatef
    
    
(0.2, 0.0, 0.0);
    }
    
    
      glTranslatef
    
    
(-2.0, -0.2, 0.0);
}

    
      glPopMatrix
    
    
();

    
      glEndList
    
    
();
  

在上面这几句,更加完整的展示了一个显示列表的操作,不仅定义了一个完整的显示列表,(就是上面那整个屏幕的球)而且在定义显示列表的时候还用“glCallList(giTorus);”调用了刚才定义的显示列表。这也展示了显示列表的可嵌套性。

为节省篇幅仅贴出关键片段,完整源代码见我博客源代码的 2009-12-21/DisplayListTest目录,获取方式见文章最后关于获取博客完整源代码的说明。

完整程序中一个程序实现了上述两种方式,利用gbIsUseDisplayList全局变量控制,需要注意,自然,还有lightsimple中包含变换光照位置的代码。

参考资料

1. 《 OpenGL Reference Manual 》,OpenGL参考手册

2. 《OpenGL 编程指南》(《 OpenGL Programming Guide 》),Dave Shreiner,Mason Woo,Jackie Neider,Tom Davis 著,徐波译,机械工业出版社

3. 《Nehe OpenGL Tutorials》,Nehe著,在 http://nehe.gamedev.net/ 上可以找到教程及相关的代码下载,(有PDF版本教程下载)Nehe自己还做了一个面向对象的框架,作为演示程序来说,这样的框架非常合适。也有 中文版 ,各取所需吧。

完整源代码获取说明

由于篇幅限制,本文一般仅贴出代码的主要关心的部分,代码带工程(或者makefile)完整版(如果有的话)都能用Mercurial在Google Code中下载。文章以博文发表的日期分目录存放,请直接使用Mercurial克隆下库:

https://blog-sample-code.jtianling.googlecode.com/hg/

Mercurial使用方法见《 分布式的,新一代版本控制系统Mercurial的介绍及简要入门

要是仅仅想浏览全部代码也可以直接到google code上去看,在下面的地址:

http://code.google.com/p/jtianling/source/browse?repo=blog-sample-code

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

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

Win32 OpenGL编程(14) 显示列表


更多文章、技术交流、商务合作、联系博主

微信扫码或搜索:z360901061

微信扫一扫加我为好友

QQ号联系: 360901061

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描下面二维码支持博主2元、5元、10元、20元等您想捐的金额吧,狠狠点击下面给点支持吧,站长非常感激您!手机微信长按不能支付解决办法:请将微信支付二维码保存到相册,切换到微信,然后点击微信右上角扫一扫功能,选择支付二维码完成支付。

【本文对您有帮助就好】

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描上面二维码支持博主2元、5元、10元、自定义金额等您想捐的金额吧,站长会非常 感谢您的哦!!!

发表我的评论
最新评论 总共0条评论