原文链接 : http://www.cnblogs.com/effulgent/archive/2008/10/06/1305029.html
原文:
Realistic and Fast Cloud Rendering
NinianeWang
MicrosoftCorporation(nowatGoogleInc.)
niniane@ofb.net
November11,2003
最近在网上看到很多云渲染的效果图,但很多人吝于向人分享经验知识,我不是很赞同这种想法,本来就是属于别人的知识,自己有幸学习得到就应该拿出来分享。 废话不多说了,这篇文章中所述的方法正是微软飞行模拟2004采用的方法,也是CRYSIS中云的实现方法,当然CRYSIS中有更多改进。感谢我前同事 提供原论文。
傍晚的云彩
云是构成室外场景的重要元素,这篇文章将讲述利用在粒子上赋予纹理的方式来近视各种形态的云彩,而且即使在漫天乌云的情况下也能得到非常理想的运行效率,在光照着色方面我们给予美工最大的控制权,利用美术资源来获取理想的光照效果,而不是传统的利用简单光照模型计算得到,这样对于最终效果可控性会更强.同时我们还将讲述模拟云彩群聚和分散的效果。
真实的云彩能让室外场景精彩万分,特别是在交互写实的应用中尤为重要,好的云彩系统必须能呈现各种形态各种密度,并且各方向都真实的云,我们的系统必须满足以下几个要求: 1.必须高效,平台应用广泛 2.要能很好的模拟云彩的光照效果 3.云彩需要运动起来才行。
传统云彩技术
很多技术用来对云彩进行建模、动画和渲染。比如最传统的体渲染,利用程序生成的噪声对一个椭球体纹理进行干扰,而着色计算也是基于真实的大气条件,最大的缺点就是不可控,无论是噪声生成的形态还是着色效果,然后就体渲染本身与多边形3D还是有些差别,2者聚合在一起比较麻烦。
如果对体渲染云彩感兴趣的请参考:http://www.vrvis.at/via/resources/course-volgraphics-2004/
Course notes "Real-Time Volume Graphics", Course #28, Siggraph 2004
如果对纯物理模拟感兴趣的请参考:《Real-Time Cloud Simulation and Rendering》By Mark Jason Harris
我们云彩如何建模?
我们利用5-400个精灵的ALPHA混合来渲染单朵云彩,渲染时所有精灵都面对相机,聚合在一起形成一个具有3D分布的体积。然后我们需要依照相机距离来从后向前渲染他们,然而如何来建立这些精灵呢?我们选择了一个能给与最终效果更好可控性的方式,传统的程序生成方式,特别是来暴露了许多方程参数的调控让美工痛苦万分,而且并不能所见既所得。我们写了个3DS-MAX插件,它能让美工通过一系列的方盒来近似一个云彩整体形状,美工还能指定之后生成的精灵数量,这样能控制云的浓淡,还能设置生成精灵尺寸范围,生成的精灵大部分是正方形的,当然也会用一些狭长的矩形来模拟云际见最缥缈的部分,通常一个16平方公里的天空会用20-200个盒子来描述云彩外行,而每个盒子根据云彩浓淡又会最终生成1-100个精灵。导出时,插件会首先根据盒子范围、云彩密度等参数,生成一系列的精灵中心点,然后插件会遍历精灵,将距离太近的精力合并消除,这样能很好的提高效率,我们发现这个阀值指定在1/3精灵尺寸时在普通云彩中能得到满意效果,而稠密得云彩则使用1/5~1/6尺寸。导出时,我们需要包含如下信息:精灵位置、尺寸(长宽)、旋转(增加随机性)、纹理以及着色信息(稍后详述)。
我们使用什么样得纹理呢?
我们综合使用(不同数量比例,不同渲染参数,旋转扭曲,拉伸)以上16种云彩纹理来模拟各种截然不同得云彩,这个图时32位带ALPHA通道得.比如使用较多得第4种纹理来模拟底部平坦得积雨云,而第一排得其他3种雾一样得纹理多用来模拟蓝灰色调的稀薄层云,最下两排中饱满膨胀的纹理则多用来模拟堆积云,剩下的纹理则在所有类型的云中都又应用.为了使用有限的16种纹理模拟尽可能多的云彩,我们需要做更多的变换,比如随即旋转,一般的云彩我们让它在0~360范围内随机,而雨云平坦的底部一般使用-5~5度.
在云中飞翔我们需要做些什么?
我们的系统将为相机穿梭在云层中提供真实的体验,当相机穿过精灵后,精灵马上从视野中消失,通常这能达到一个完美的效果,但如果相机穿过云层的时候正在做变形动画,可能就会在视觉上有一些不协调的突变。当我们首次实现这个解决方案的时候,我们让精灵作为公告板始终面对相机平面,这样就不会在相机运动过程中明显感觉到精灵的边界,当这样有个很大的缺点,就时当相机离精灵很近的时候,精灵的旋转会非常明显,为了解决这个问题,我们决定当相机距离精灵距离小于精灵半径的一半时,就不再旋转精灵了,这样能很好解决前面提到的精灵旋转问题,但又引进了一个新问题,就是如果精灵旋转被锁定后,如果此时相机旋转观察精灵,此时精灵的边界又会比较明显了,最终我们决定根据相机观察方向与精灵锁定时的方向之间的夹角,来调节精灵的透明度。
云彩如何生成为消亡?
云能自然的生成和消亡将会极大的增加真实性,这里我们通过控制精灵ALPHA程度来完成云彩的演化过程,我们通过精灵在云中的位置来来决定顶点的透明程度,当云形成时,我们首先只渲染位于云中半径一半范围内的精灵,随着时间流逝,慢慢减少他们的透明度,当透明度达到一个阀值时,我们才开始渲染半径一半外的精灵。云彩的消亡过程与此相反,当半径一半之外的精灵基本全部透明后,开始增加半径一半内精灵的透明度,需要注意的是,记得对更外面的精灵使用大一些的透明变化。
云彩如何进行光照着色?
我们的着色模型中主要又两个控制因子,天光和阳光。
天光的模拟
现实中,当光线穿过云层时会被云层的粒子散射和吸收,通常在均匀天光条件下,云彩会呈现上白下暗的外观,为模拟这个效果,美工首先定义5个颜色等级,每个颜色等级由一个RGBA颜色和高度组成。
美工可以使用不同的亮度等级来模拟不同类型的云彩。使用方法见原文。美工还需要定义一个百分比等级来模拟一天不同时段的天光强度,比如中午的天光最强,晚上基本没光了。最后计算流程如下:首先必须完成公告板的旋转,让他们面对相机,还有根据精灵与相机的远近程度将精灵锁定到相关角度,对于每个精灵的顶点,我们取它的高度分量,利用它在高度等级中对天光颜色进行插值,再利用当前游戏时间,插值得到当前天光百分比等级,将两者相乘就得到了顶点得当前时间得天光颜色。
阳光的模拟
太阳会向天空中的云彩投射方向光,特别是再黎明和黄昏,变化的阳光能让云彩更加生动,我们现在将模拟云彩面向太阳光的部分与被向阳光部分的明暗。但我们不模拟云彩之间,云彩自身以及云彩与场景中其他物体之间的遮挡与投影效果。美工再3DSMAX中会指定着色组,通常1~30个精灵为一组,每个BOX都有一个用户属性来指定这个BOX产生精灵所属的着色组,每个组用来模拟现实云彩中的紧密聚在一起的一簇,当我们为一个顶点着色时,首先计算其所属着色组中心到这个顶点的向量,然后再计算所属着色组中心到太阳的向量,将他们正规化后,进行点积,得到一个位于[-1, 1]之间得值,现在我们要将这个值映射到[Cmin, Cmax]之间,[-1, 0]映射到[Cmin, Cmed], [0, 1]映射到[Cmed, Cmax]。这样单位云簇从明到暗就有一个光滑得过渡,而Cmin Cmed Cmax均为亮度百分比,由美工指定,美工还会指定一天中太阳光变化T0->C0 T1->C1,给定一个时间,则顶点得颜色计算为:
C_sun = Func_map(v0*v1) * ( A * C0 + ( 1 – A ) * C1 ) A = (T1 – T) / (T1 – T0)
最终颜色为:
C_vertex = ( C_sky + C_sun ) * C_tex * Alpha_morph
如何优化性能?
主要再两个方面,一个时顶点的位置计算和着色计算,第二个时GPU象素填充率 。文中提到使用了顶点信息预计算和八角形环代理纹理方式,这里不再详述,请参看原文。
限制
我们的云彩系统因为是体积分布的精灵,所以非常不适合做平面云,如果要做高积云等非常扁平的云,可以直接使用纹理平面.因为所有的粒子再渲染前都做了从后向前的排序,这样才可以得到正确的渲染效果,但当相机移动的时候,很可能导致渲染顺序的改变,这样再视觉上可能会由一些可以察觉的变化,特别是在太阳光分量比较重的黎明和黄昏,但这个问题还没有严重到一定需要解决的地步。还有一些情况与现实不符,比如相机在云彩中间时看到的云比在外面看要更透明,因为有一半的精灵位于相机后面了,而现实并非如此。当相机靠近云层后向下看,因为精灵都被垂直的锁定了,所以我们将会看到大片无云空白,一个建议的解决方案是当相机进入到云彩中心的时候,给世界加上一个额外的雾化来增强云的浓度。到此为止我们的云没有任何的动画,现实中的云一般都会随气流运动变形,我们可以通过旋转和移动云中的精灵来模仿被风扰动的效果,同样我们可以通过透明掉其中部分精灵来改变云的形状等等,因为我们的云没有模拟对光的散射,云彩自身不会投影也不会对其他物体产生投影,还有利用相对与太阳的夹角来评估光照是错误的,应该是根据阳光穿过云层的距离和密度来估算,这样我们就不能得到一些现实中得效果,比如在云层后面观察太阳能看到云得光边。