(5) 地形、天空和雾

简介

    本课程展现如何在你的应用中加入地形、天空和雾。学完本课程,你应该对天空盒、天空穹、天空面的概念有所了解,并清楚他们的差别及用法。同时你能够了解各种类型的雾的差别并使用他们。

 

从这里开始

初始代码清单如下

  BaseApplication.h

  BaseApplication.cpp

  BasicTutorial4.h

  BasicTutorial4.cpp

链接

   将静态链接库'OgreTerrain.lib' 加入到你的工程配置中

 

地形(Terrain)

    目前OGRE 1.7有3个组件:Terrain(地形)、Paging(分层)、Property(属性)。本章我们先接触地形组件,后续再讲解3个组件之间的联系。

调整相机

    首先,先修改相机对象,以便于观察我们后续创建的地形。将如下代码加入到BasicTutorial4::createScene函数中:

   

    至于他干了些啥,请参看注释

设置光线

    地形组件使用有向光来反衬其纹理,首先往场景中放入一个有向光BasicTutorial4::createScene:

   

配置地形

    首先,创建一组全局的地形配置信息:

   

    然后,创建一个TerrainGroup对象,使用该对象我们可以管理后续创建出来的地形:

   

    到了配置地形的时候了:

   

    此函数后续再讲解,注意下我们将有向光的对象作为其参数了。然后我们定义我们的地形并加入到TerrainGroup中去:

   

    这样我们得到一个地形(因为只循环了一次),后续我们再来讲解defineTerrain函数。

    使用下面的代码处理只载入已存在地形的信息:

   

    上面代码使用迭代器遍历了TerrainGroup的所有已存在的地形,并使用函数initBlendMaps作用之,至于本函数的用法后续说明。

    最后一件事情:清除初始化地形中的临时资源:

   

    以下列出到目前为止的代码清单,先别忙着编译执行哦:

 

默认地形配置

    Terrain组件是非常灵活很容易配置的。mTerrainGlobals变量存储了全局的地形配置信息。

  在configureTerrainDefaults函数中添加如下代码:

  

  做了2个配置,具体见代码注释说明。然后我们需要使用有向光,处理地形的光照贴图:

 

  然后我们再设置“载入配置”的信息:

 

  这些值的意义暂时不讲,后续中我们再来详细解释。最后一件事情是设置纹理贴图:

 

  我们设置了地形有3层,每一层我们初始化了其大小、材质。默认情况下,材质生成器为每层提供了2个纹理贴图:

    1、diffuse_specular

    2、normal_height

  这里先不展开讨论,知道有这么回事就行。至此,我们的代码如下:

 

 

defineTerrain

  本函数的代码如下:

 

  本函数的工作比较简单也比较灵巧,具体见代码注释。他使用了函数:getTerrainImage。

 

getTerrainImage

  本函数的代码如下:

 

 

initBlendMaps

  本函数的完整代码如下至于他如何工作暂时不讨论,:

 

 

编译运行

  尝试编译和运行下,看到一个渲染得挺好的地面了吧?不过呢,我们可以做3个改进:

  1、地形生成进度指示

  2、地形保存

  3、自我清除

  首先,我们在BasicTutorial4类声明中,加入如下变量:

 

  然后,在本类中重载3个函数:

  1、frameRenderingQueued - 用于显示地形载入进度

  2、createFrameListener - 创造信息标签

  3、destroyScene - 自我销毁

  下面我们继续……

 

frameRenderingQueued

  

 

createFrameListener

    之所以要重载此函数,唯一的目的就是为了加入个信息标签以显示载入进度,代码如下:

   

 

destroyScene

    代码如下:

   

 

编译运行

    编译运行你就可以看到载入的信息栏了。当信息栏消失后,地形信息将被保存起来,去目录OGRE_HOME/media看下,是否有*.dat的文件?

 

天空

    OGRE提供了3种天空:天空盒、天空穹和天空面,下面逐一介绍。

 

天空盒

    天空盒是个巨大的立方体,他将场景中的所有物件全部包含在里面。直接用代码来体验下,将下列一行加入到函数createScene中:

   
    编译运行,不怎么像个天空是吧?因为我们使用的天空贴图比较挫:)。当你使用如下代码时:

    

   什么都没有变,是吧?这是因为第四个参数的默认值是true,也就是说天空盒会最先渲染,其它的东西会渲染到天空盒之上,从而使天空盒看起来像是在背后。(注意你不应该让第三个参数小于摄像机的近剪切面距离,否则他会不可见!)其实天空盒并不应该最先渲染,因为你会渲染他的全部。假如你最后渲染他,OGRE只会渲染可见的部分,从而提高一定的运行速度。

天空穹

    天空穹和天空盒非常像,他也是通过一个巨大的立方体围绕着场景中所有的实体,但最大的不同是:贴图被用球体的方法投影到立方体上。你看到的其实还是一个立方体,只不过它的贴图看上去像贴到了一个球体上一样。这种天空有一个很大的缺陷,就是立方体的下面没有任何贴图,所有你必须要有一个类似地面的东西来遮盖下面。

    OGRE的示例贴图将让你很明显地看到这个缺陷。删掉setSkyBox然后在createScene中添加下面代码:    

   

    当你运行时,将镜头移动到地面的中间,然后是镜头几乎和地面平行。按R键切换到网格状态,你会发现你看到的仍然是一个立方体(没有底的),但是那些云彩看起来却像覆盖在球体上一样。(注意云彩的变换是在"Examples/CloudySky"脚本里定义的,并不是所有的天空穹都有这样的效果。)

    天空穹的前两个参数和天空盒一样,第三个参数是天空穹的弯度。OGRE的API建议使用2到65之间的数值,降低第三个参数会有更好的距离感,增加第三个参数会降低扭曲度从而达到更平滑的效果。试着将它设为2然后再设为65来观察它们的区别。

    第四个参数是贴图重复的次数,你需要根据你贴图的大小来设置它。但是注意这个参数是Real类型(浮点数)的而不是整形。第五第六个参数分别是距离和渲染顺序,与天空盒的相同。

 

天空面

    天空面与前两种天空有相当大的区别。我们用一个平面来替代正方体。(注意下面所有的天空面的属性都是尽量靠地面中间)删掉createScene里所有天空穹的代码。我们要先创建一个平面,然后是他朝下。setSkyPlane与前两个函数不同的是他没有距离参数。相应的,我们在Plane的d变量里设置它的距离。

   

    现在我们定义了平面,接下来就可以创建天空面了。注意第四个参数是天空面的大小(在这里是1500x1500个单位)第五个参数是他重复的次数:

   

    编译运行你的程序。咱们创建的天空面有两个缺陷。第一个是由于贴图像素低,重复的时候不好看。你自己画一个高像素边缘处理好的贴图就行了。最主要的缺陷是当你注视水平线的时候,你能看到天空面的结尾。即使你有一个好的贴图,他看起来并不会很逼真,好像你能看到天空的结尾。天空面的这种用法只能用在盆地或者四周都是墙的场景。不过他对显卡的要求会比天空盒和天空穹要低。

    幸运的是天空面的功能还不仅是这些。第六个参数跟天空盒和天空穹的渲染顺序很相似。第七个参数允许你设置天空面的弯曲度,这样一来平面就变成弧形的了。同时我们还需要设置x和y的线段数量(天空面是一个巨大的正方形,但是假如我们想让他弯曲那么它就要变成许多小正方形)。第八个和第九个参数就是x和y的线段数量了:

   

    编译运行看看,效果会好多了。你还可以用云彩的那个材质:

   

    编译运行你的程序。云彩的移动以及他重复的方法比天空穹还差一点,尤其是当你接近地面的边缘往水平线的方向看去时。

还有一点,你可以通过 mSceneMgr->setSkyPlane( false, Plane(), "" ); 来取消天空面。

 

用哪个好?

    究竟用哪种天空完全取决于你的程序。假如你需要看到周围所有的东西,包括负y的方向,那你可能只有天空盒这个选择了。假如你有一个地面或者类似地板的东西可以遮住负y方向,那么使用天空穹会有一个更真实的效果。假如你看不到海平线(比如一个峡谷,一个监狱,或者城堡中间的院子),天空面会有一个很好的效果同时只有很低的GPU使用量。我们会在下一节中来解释用天空面的最重要的一个原因,是因为它能和云雾很好地结合在一起。

 

雾化

 

介绍

    你需要知道关于设置雾化效果的很重要的一点是他并不像你想象的那样在空的地方创建“雾”的实体。实际上雾只是当你观看物体时的一个滤镜。当你面前没有任何物体时,你是看不见雾的。事实上,你只会看到视口(viewport)的背景色。所以想要使雾逼真,我们必须将视口的背景色设成雾的颜色。

   

 

    雾分两种:线性的和指数的。线性雾“线性的”增浓,而指数雾“指数的”增浓(每个距离单位雾的浓度都会比上一个单位增加得更多)。你自己看他们效果的差距比这样讲更容易理解,所以我们来看几个例子。

 

雾化类型

    第一种类型是线性的雾,他是最容易理解的。在调用setWorldGeometry后首先要设置视口的背景颜色。我们可以用createViewport方法(象用在上一个指南)一样,但是有时我们并不需要调用视口。在上述基础上增加代码如下:

   

    如果视口不唯一的话,你可以用getNumViewports元函数来得到视口的数量然后不断的操作她,但是通常这种情况是很少见的(目前为止我们知道我们仅用了一个视口),我们可以直接得到视口。设置完背景色以后我们就可以创建雾了

   

    第一个参数设置雾的类型(这里我们设的是线性的)。第二个参数是我们用到的雾的颜色。第三个参数在线性雾里面是不用设置的。第四个和第五个参数是设置雾慢慢变浓的范围。这里我们从50开始结束于500。这就意味着在摄像机的0到50个单位内是没有雾的。从50到500单位内雾慢慢线性变浓。在500单位以外全是雾了。编译运行这个应用程序。

    另一种雾的类型是指数雾。和设置雾的起点和终点不一样的是,我们要设置雾的密度(第四个和第五个参数不需要设置)。代码如下:

   

    编译运行这个应用程序。这里还有一种更厉害的指数雾函数(与前一种比较起来,离摄像机越远它的雾更浓)。注意,如果你使用FOG_EXP2会得到更加浓密的雾。将上面的代码替换成下面的:

   

    再一次编译和运行这个程序。基本上Ogre提供的这三种雾函数是可以互换的。你应该都实验一下这些函数,来看看哪一种在你的应用程序里效果更好。

 

雾与天空

    当你试图将雾与天空盒/天空穹一起使用时,会遇到些有趣的问题。因为天空盒/天空穹是方形的,而雾效是以球形的方式工作的。清空createScene方法里的天空和雾的内容,如果巧妙地调整天空穹和雾的参数,我们就能明显地发现这个问题。

    编译运行程序。如果你转动摄像机,你会发现天空穹的某些部分会探出来(蓝色部分从边缘探出来,而不是中央)。这显然不是我们想要的。另一种选择就是使用天空面。在createScene里用以下代码替换:

   

    这样看起来就对了。如果我们往上看,就能看见天空(真实情况也是这样),而不会滑稽地从别处探出来。不管你是否使用弯曲,这都能解决当你往地平线上看的时候出现的不正常现象。

    还有一种办法能让雾效不涉及整个天空,但需要对材质脚本进行修改。这已不是本课的内容,为了今后参考,它就是材质里禁用雾效的那个参数。

 

黑暗中的雾

    当你设置雾的时候,可能不想使用天空,因为雾已经浓到你无法看见天空了。上面讲的雾效的一些技巧在某些场景非常有用。不把雾设置成明亮的颜色,而是设成非常暗,看看有什么效果。(注意,我们把天空面设置成离摄像机只有10个单位距离,在设置雾之前):

   

    编译运行程序,不算太糟糕。当然,一旦你会使用光照,你就不必用这种技巧了。但这的确展示了雾效的灵活性,以及用这个引擎你能做一些有趣的事情。如果你正写一个第一人称游戏,使用黑色雾能够制造“失明”或“符咒”这样的特效。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值