Win32 OpenGL编程(13) 隐藏表面消除(深度测试

系统 1591 0

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

讨论新闻组及文件

隐藏表面消除(深度测试)

其实这是个滞后的话题了,事实上应该在光照一节就应该详述的,但是因为光照的内容本来就多,所以当时并没有再牵涉此内容。

在我们填充三维物体时,与平面绘制不同,因为牵涉到了前面物体遮挡后面物体的问题,假如没有一个很好的内置机制,我们就只能通过程序记住每个物体的Z轴(其实也不一定是Z轴,看当时的观察方向),然后按顺序将远的物体先绘制,然后再绘制离我们近的,通过这种方式,可以让离我们近的物体绘制在后面,覆盖掉远的物体。

当然,假如觉得不麻烦的话,这样做倒也不是不行,但是假如这些事情是由硬件实现的话,效率可是会上一个层次的,所以OpenGL也提供了此方法,被称作深度测试。(Depth Test)使用方法那是相当简单,仅仅需要通过glEnable启用就可以了,我们现在看看使用前后的区别。

我们通过以下函数绘制一大一小两个球

    
      void 
    
    
      DrawBigRedSphere
    
    
()
{
    
    
      GLfloat mat_specular
    
    
[] = { 1.0, 0.0, 0.0, 1.0 };
    
    
      GLfloat mat_shininess
    
    
[] = { 50.0 };
    
    
      GLfloat mat_ambient
    
    
[] = { 1.0, 0.0, 0.0, 1.0 };
    
    
      GLfloat mat_diffuse
    
    
[] = { 1.0, 0.0, 0.0, 1.0 };

    
    
      glMaterialfv
    
    
(
    
      GL_FRONT
    
    
, 
    
      GL_SPECULAR
    
    
, 
    
      mat_specular
    
    
);
    
    
      glMaterialfv
    
    
(
    
      GL_FRONT
    
    
, 
    
      GL_SHININESS
    
    
, 
    
      mat_shininess
    
    
);
    
    
      glMaterialfv
    
    
(
    
      GL_FRONT
    
    
, 
    
      GL_AMBIENT
    
    
, 
    
      mat_ambient
    
    
);
    
    
      glMaterialfv
    
    
(
    
      GL_FRONT
    
    
, 
    
      GL_DIFFUSE
    
    
, 
    
      mat_diffuse
    
    
);

    
    
      glutSolidSphere
    
    
(0.5, 32, 32);
}


    
      void 
    
    
      DrawSmallBlueSphere
    
    
()
{
    
    
      GLfloat mat_specular
    
    
[] = { 0.0, 0.0, 1.0, 1.0 };
    
    
      GLfloat mat_shininess
    
    
[] = { 50.0 };
    
    
      GLfloat mat_ambient
    
    
[] = { 0.0, 0.0, 1.0, 1.0 };
    
    
      GLfloat mat_diffuse
    
    
[] = { 0.0, 0.0, 1.0, 1.0 };

    
    
      glMaterialfv
    
    
(
    
      GL_FRONT
    
    
, 
    
      GL_SPECULAR
    
    
, 
    
      mat_specular
    
    
);
    
    
      glMaterialfv
    
    
(
    
      GL_FRONT
    
    
, 
    
      GL_SHININESS
    
    
, 
    
      mat_shininess
    
    
);
    
    
      glMaterialfv
    
    
(
    
      GL_FRONT
    
    
, 
    
      GL_AMBIENT
    
    
, 
    
      mat_ambient
    
    
);
    
    
      glMaterialfv
    
    
(
    
      GL_FRONT
    
    
, 
    
      GL_DIFFUSE
    
    
, 
    
      mat_diffuse
    
    
);

    
    
      glutSolidSphere
    
    
(0.3, 32, 32);
}
  

DrawBigRedSphere绘制的是大的红球,DrawSmallBlueSphere绘制的是小的蓝球,绘制的时候都采用默认坐标,即绘制在屏幕的中央,并且Z轴也不动,那么,从理论上来讲,大球会包含小球,小球就被大球遮掩了,我们显示要的应该也是这种效果。

看一下未开启深度测试情况下的情况我们先绘制大球,再绘制小球的和用相反顺序再绘制的情形:

代码如下:

    
      void 
    
    
      SceneShow
    
    
(
    
      GLvoid
    
    
)        
{
    
    
      glClear
    
    
(
    
      GL_COLOR_BUFFER_BIT 
    
    
| 
    
      GL_DEPTH_BUFFER_BIT
    
    
);    

    
    
      glPushMatrix
    
    
();
    
    
      glTranslatef
    
    
(-0.5, 0.0, 0.0);
    
    
      DrawBigRedSphere
    
    
();
    
    
      DrawSmallBlueSphere
    
    
();
    
    
      glPopMatrix
    
    
();

    
    
      glPushMatrix
    
    
();
    
    
      glTranslatef
    
    
(0.5, 0.0, 0.0);
    
    
      DrawSmallBlueSphere
    
    
();
    
    
      DrawBigRedSphere
    
    
();
    
    
      glPopMatrix
    
    
();

    
    
      glFlush
    
    
();
}  
  

image

我们会观察到很明显的绘制顺序导致的问题,左边的图因为先绘制大球再绘制小球,小的篮球竟然覆盖掉了应该在其外部的红球。实际使用中,我们可以都自己判断,然后用右边的正确绘制方式,随着图形的增多,会增加一定的计算量,OpenGL提供的深度测试就是为我们提供了更简便高效的方式,事实上,由于OpenGL使用的方式因为可以在测试后,直接绘制出最终图形而忽略绘制被覆盖掉的图形,效率上比覆盖绘制会更高一些(需要硬件支持)。

上述代码,仅仅需要添加一行,

glEnable(GL_DEPTH_TEST);

以启用深度测试,效果大不一样:

image

天下至简之事莫过于此:)

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

扇子驱不散大雾 -- 阿拉伯谚语

像云像雾 又像风 -- 某电视剧名

雾作为一种常见的天气现象,OpenGL中有所模拟体现也算是正常,但是在OpenGL中雾的作用却又不止这些,事实上,雾效果还用于实现模糊效果,用于关于模拟模糊,薄雾,烟,污染。

使用步骤非常简单,一如OpenGL惯例,首先通过glEnable开启,然后又有一个glFog*这个用于设置雾效果的函数可以使用,呵呵,OpenGL学多了,会发现除了概念不一样,基本上的使用步骤都差不多,开始觉得OpenGL的这种一个函数通过pname参数去决定函数作用的方式不太符合好的API的设计哲学,(可以参考《 设计Qt风格的C++API【转】 》)但是想来,API的设计还不一定有绝对意义上的好坏,这样的设计倒是在一定程度上减少了API的数量,并且查找时会稍微方便一点,虽然同时增加了查文档看参数作用的负担,这些又是题外话了。

首先看没有雾效果时,我用OpenGL绘制的3个红球,红球的Z轴是依次向内。

主要代码如下:

    
      void 
    
    
      DrawRedSphere
    
    
()
{
    
    
      GLfloat mat_specular
    
    
[] = { 1.0, 0.0, 0.0, 1.0 };
    
    
      GLfloat mat_shininess
    
    
[] = { 50.0 };
    
    
      GLfloat mat_ambient
    
    
[] = { 1.0, 0.0, 0.0, 1.0 };
    
    
      GLfloat mat_diffuse
    
    
[] = { 1.0, 0.0, 0.0, 1.0 };

    
    
      glMaterialfv
    
    
(
    
      GL_FRONT
    
    
, 
    
      GL_SPECULAR
    
    
, 
    
      mat_specular
    
    
);
    
    
      glMaterialfv
    
    
(
    
      GL_FRONT
    
    
, 
    
      GL_SHININESS
    
    
, 
    
      mat_shininess
    
    
);
    
    
      glMaterialfv
    
    
(
    
      GL_FRONT
    
    
, 
    
      GL_AMBIENT
    
    
, 
    
      mat_ambient
    
    
);
    
    
      glMaterialfv
    
    
(
    
      GL_FRONT
    
    
, 
    
      GL_DIFFUSE
    
    
, 
    
      mat_diffuse
    
    
);

    
    
      glutSolidSphere
    
    
(0.25, 32, 32);
}


    
      //这里进行所有的绘图工作

    
    
      void 
    
    
      SceneShow
    
    
(
    
      GLvoid
    
    
)        
{
    
    
      glClear
    
    
(
    
      GL_COLOR_BUFFER_BIT 
    
    
| 
    
      GL_DEPTH_BUFFER_BIT
    
    
);    

    
    
      glPushMatrix
    
    
();
    
    
      glTranslatef
    
    
(-0.75, 0.0, 0.0);
    
    
      DrawRedSphere
    
    
();
    
    
      glPopMatrix
    
    
();

    
    
      glPushMatrix
    
    
();
    
    
      glTranslatef
    
    
(0.0, 0.0, -1.0);
    
    
      DrawRedSphere
    
    
();
    
    
      glPopMatrix
    
    
();

    
    
      glPushMatrix
    
    
();
    
    
      glTranslatef
    
    
(0.75, 0.0, -2.0);
    
    
      DrawRedSphere
    
    
();
    
    
      glPopMatrix
    
    
();

    
    
      glFlush
    
    
();
}  
  

image

从图形上效果上看不出Z轴的变化,因为使用的是正投影方式。现在我们启用雾效果来试一下。

添加进如下语句:

    
      glEnable
    
    
(
    
      GL_FOG
    
    
);

    
      GLfloat fogColor
    
    
[4] = {1.0, 1.0, 1.0, 1.0};


    
      glFogi 
    
    
(
    
      GL_FOG_MODE
    
    
, 
    
      GL_EXP
    
    
);

    
      glFogfv 
    
    
(
    
      GL_FOG_COLOR
    
    
, 
    
      fogColor
    
    
);

    
      glFogf 
    
    
(
    
      GL_FOG_DENSITY
    
    
, 0.5);
  
    效果如下:
  
    
      image
    
  
    因为雾的颜色为白色,第3个红球基本上已经全白了。。。。大家可以调整上述代码中的参数以获取不同的效果。
  
    比如说,偏蓝色的雾:
  
    
      glEnable
    
    
(
    
      GL_FOG
    
    
);

    
      GLfloat fogColor
    
    
[4] = {0.0, 0.0, 0.5, 1.0};


    
      glFogi 
    
    
(
    
      GL_FOG_MODE
    
    
, 
    
      GL_EXP
    
    
);

    
      glFogfv 
    
    
(
    
      GL_FOG_COLOR
    
    
, 
    
      fogColor
    
    
);

    
      glFogf 
    
    
(
    
      GL_FOG_DENSITY
    
    
, 0.5);
  

image

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

主要函数其实也就一个glFog*,但是参数那是相当的多啊,留待大家自己尝试了。

OpenGL Reference Manual 》:

glFog — specify fog parameters
C Specification
void glFogf( GLenum pname,
GLfloat param);
void glFogi( GLenum pname,
GLint param);
Parameters

pname

Specifies a single-valued fog parameter.
GL_FOG_MODE,
GL_FOG_DENSITY,
GL_FOG_START,
GL_FOG_END,
GL_FOG_INDEX, and
GL_FOG_COORD_SRC
are accepted.
param

Specifies the value that pname will be set to.

C Specification
void glFogfv( GLenum pname,
const GLfloat * params);
void glFogiv( GLenum pname,
const GLint * params);
Parameters

pname

Specifies a fog parameter.
GL_FOG_MODE,
GL_FOG_DENSITY,
GL_FOG_START,
GL_FOG_END,
GL_FOG_INDEX,
GL_FOG_COLOR, and
GL_FOG_COORD_SRC
are accepted.
params

Specifies the value or values to be assigned to pname.
GL_FOG_COLOR requires an array of four values.
All other parameters accept an array containing only a single value.

Description

Fog is initially disabled.
While enabled, fog affects rasterized geometry,
bitmaps, and pixel blocks, but not buffer clear operations. To enable
and disable fog, call glEnable and glDisable with argument
GL_FOG.

glFog assigns the value or values in params to the fog parameter
specified by pname.
The following values are accepted for pname:

GL_FOG_MODE

params is a single integer or floating-point value that specifies
the equation to be used to compute the fog blend factor,
f.
Three symbolic constants are accepted:
GL_LINEAR,
GL_EXP,
and GL_EXP2.
The equations corresponding to these symbolic constants are defined below.
The initial fog mode is GL_EXP.
GL_FOG_DENSITY

params is a single integer or floating-point value that specifies
density,
the fog density used in both exponential fog equations.
Only nonnegative densities are accepted.
The initial fog density is 1.
GL_FOG_START

params is a single integer or floating-point value that specifies
start,
the near distance used in the linear fog equation.
The initial near distance is 0.
GL_FOG_END

params is a single integer or floating-point value that specifies
end,
the far distance used in the linear fog equation.
The initial far distance is 1.
GL_FOG_INDEX

params is a single integer or floating-point value that specifies
i
f
,
the fog color index.
The initial fog index is 0.
GL_FOG_COLOR

params contains four integer or floating-point values that specify
C
f
,
the fog color.
Integer values are mapped linearly such that the most positive representable
value maps to 1.0,
and the most negative representable value maps to
-1.0
.
Floating-point values are mapped directly.
After conversion,
all color components are clamped to the range
0
1
.
The initial fog color is (0, 0, 0, 0).
GL_FOG_COORD_SRC

params contains either of the following symbolic constants:
GL_FOG_COORD or GL_FRAGMENT_DEPTH. GL_FOG_COORD
specifies that the current fog coordinate should be used as distance value
in the fog color computation. GL_FRAGMENT_DEPTH specifies that the
current fragment depth should be used as distance value in the fog
computation.

本系列其他文章见OpenGL专题 《 Win32 OpenGL系列专题

参考资料

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自己还做了一个面向对象的框架,作为演示程序来说,这样的框架非常合适。也有 中文版 ,各取所需吧。

4. 《OpenGL入门学习》 ,eastcowboy著,这是我在网上找到的一个比较好的教程,较为完善,而且非常通俗。这是第一篇的地址: http://bbs.pfan.cn/post-184355.html

完整源代码获取说明

由于篇幅限制,本文一般仅贴出代码的主要关心的部分,代码带工程(或者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编程(13) 隐藏表面消除(深度测试)及雾效果


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

微信扫码或搜索:z360901061

微信扫一扫加我为好友

QQ号联系: 360901061

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

【本文对您有帮助就好】

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

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