基于Vega的闪电渲染方法
时间:2009-06-13 17:33:00
来源:UltraLAB图形工作站方案网站
人气:9017
作者:admin
1 概述
闪电模拟一直是视景仿真[1]中一个的重点。目前关于闪电模拟的方法基本都是采用粒子系统[2]。使用粒子系统模拟闪电,就是实时控制每一个粒子运动和变化,这样粒子运动的轨迹就构成了闪电[3~4]。
视景仿真主要基于Vega进行开发。Vega是美国Multi Gen-Paradigm公司用于虚拟现实、实时视景仿真、声音仿真以及其他可视化领域的世界领先级视景开发软件。Vega有自己的粒子系统,但是它没有办法进行闪电模拟。Vega的粒子系统是封装好的,用户只要设定好粒子的数量、产生方式、各种速度(风速、矢量速度和随机速度等)、颜色和重力加速度,就可以使所有的粒子按照要求开始运动和变化。由于这样的封装,Vega的粒子系统使用起来非常方便,尤其是实现雨和雪模拟的时候,然而也是由于这样的封装,用户只能知道整体粒子系统是按照什么方式工作的,根本不能得到单独一个粒子的实时状态和位置,因此使用Vega的粒子系统难以控制粒子模拟闪电。
现在,基于Vega的闪电模拟目前主要采用的是播放纹理技术,所谓播放纹理就是事先把闪电现象做成一系列的连续图片,当要产生闪电的时候,系统就高速播放这些连续的图片,就像播放闪电动画一样。然而这个技术存在很大的缺点:(1)因为每次播放的都是同样的纹理图片,所以闪电样子单一;(2)当闪电位置和大小变化的时候,都要对纹理图片进行缩放、变形,因此存在一定的失真;(3)由于闪电是纹理实现的,因此它无法实现因为闪电显现而产生的特效[5~6],比如闪电与地面物体的碰撞、闪电的光照现象、闪电云彩效果等。
图1 使用播放纹理技术产生的闪电现象
1994年,T.Reed[3]比较系统地提出了用粒子系统实现CG闪电模拟。具体方法:在云中放一个粒子,然后这个粒子随机产生几个子粒子,子粒子随机地产生向下的角度,向下运动,子粒子又随机地产生新粒子……,这样就产生了整个闪电整体。在渲染方面,他采用光线跟踪技术,不过仅仅只渲染了闪电主枝。由于当时技术的限制,他的闪电模拟做得比较粗糙,只追求模拟闪电的样子。1999年,杨子华[4]进一步总结阐述了T. Reed的闪电模拟方法。同年,P.Kruszewski[7]在随机二叉树的理论基础上,提出了一个可以改变参数的闪电模型。只要设定好模型的多个参数,就可以产生出令人满意的闪电样子。由于在渲染上,他还是采用T. Reed模型中的方法,因此他也只考虑闪电主枝的渲染,闪电分支以及周围物体的渲染没有考虑进去。
2001年,B.Sosorbaram等[6]提出自己的闪电模拟方法,在他之前的闪电模型、闪电形态的参数都是从图片或者录像中估计出来的,而B.Sosorbaram强调使用物理的参数,这样产生的闪电才更加真实。而且在他文章中也实现了闪电对周围云彩的光照效果。
闪电模拟中,闪电分支越多,渲染就越复杂,占用的时间就越多,而降低闪电分支的复杂度,整个闪电模拟就会失真,因此,闪电模拟还没有广泛应用到大型的虚拟现实仿真系统中。基于此,本文提出一种闪电渲染算法,可以通过简单的闪电形状,模拟出效果真实的闪电。
2 Vega中闪电渲染的新方法
2.1 可行性
图2是开发Vega应用的层次关系[8],从底层实现来看,Vega实际上是基于场景图(scene graph)之上的,而场景图管理系统本身又建立在OpenGL这样的标准图形库之上。在SGI平台上,Vega所依附的场景图管理系统就是Performer,而在Windows平台上,Vega所依附的是一套被称为“Jolt”的场景图管理系统(Jolt实际上就是PC上的Performer实现)。另外,Vega不但提供大量的API函数,还提供回调函数(CALLBACK)[8],因此,可以在回调函数中实现OpenGL命令和Vega API函数的结合。
图2 开发Vega应用的层次关系
Vega应用有3个必需步骤[8]:初始化系统(vgInitSys()),定义系统(vgDefineSys()),配置系统(vgConfigSys()),之后就可以进入Vega应用的主循环来渲染帧和帧同步(vgSync Frame()和vgFrame())。本文在配置系统之后使用vgAddFunc (vgGetChan(0), VGCHAN_POSTDRAW, Lightning, NULL ),定义一个名为Lightning的VGCHAN_PREDRAW回调函数,它的参数为空。VGCHAN_PREDRAW回调函数是在创建通道之后、开始画图之前调用。 #p#page_title#e#
2.2 闪电结构生成
为了提高模拟的真实度,采用以下方法进行优化:(1)采用优化过的随机函数来控制闪电段的角度和长度。(2)对所有闪电段都增加了参数来表示它的能量,同一层次的分支中,能量随着闪电段延续而减小,不同级别的分支,其初始能量也是不一样的,这样最终闪电效果不像传统方法中闪电粗细始终不变。(3)整个闪电段都共面,这个面是一个垂直地面的Billboard,其法向量平行于视线。
2.2.1 数据结构
struct segment //闪电段的结构
{
Vector3 start; //段的上节点坐标
float power_start; //上节点处能量
Vector3 end; //段的上节点坐标
float power_end; //下节点处能量
}; //闪电分支结构
{
Vector3 start; //分支最顶节点坐标
float power; //分支初始能量
float angle; //分支的初始角度
};
2.2.2 算法描述
Step1 取云间一点作为整个闪电的起始点,生成branch对象,随机生成它的power和angle,并将这个branch压入分支栈。
Step2 判断分支栈是否为空,如果为空,表示整个闪电结构已经生成,则转Step7;否则,从分支栈中弹出一个分支,以它的坐标和能量作为新生成segment对象上节点的坐标和能量。
Step3 随机决定是否在segment对象上节点处是否产生新的闪电分支,如果产生,则上节点的坐标作为新branch对象的start,而power和angle都采用优化随机函数产生(注:power小于上节点能量),并将此新的branch对象压入分支栈。
Step4 根据segment对象的上节点和能量以及整个分支的angle,采用随机函数生成其下节点的坐标和能量。因为在同一分支中能量递减,所以要控制随机函数,使得下节点的能量小于上节点能量而且还不能引起能量巨变。
Step5 保存上面生成segment对象,并将其下节点坐标和能量作为新的segment对象的上节点坐标和能量。
Step6 对新segment对象上节点的坐标和能量进行判断,如果它已经到达地面或者它的能量已经太小(可忽略),则转Setp2;否则Step3。
Step7 生成过程结束。
生成的segment对象集合就构成了整个闪电结构。
2.3 纹理生成
2.3.1 纹理结构生成
采用2.2.2节的生成算法在纹理图片上生成一个小型的闪电,这个闪电结构简单,分支少。然后根据分支的层次,以每个segment的上、下节点连线为中心向两边扩展,生成segment面。闪电主枝为最高层,因此它的面最宽,从上层分支上生成的分支为下一层,宽度比上一层递减,如图3。
图3 闪电纹理的结构层次
2.3.2 纹理着色
通常闪电段周围会有一些别的颜色,颜色是闪电与空气中化学物质发生作用以及物理作用产生的,称为闪电的燃烧颜色,一般其为粉红色或者淡蓝色。首先将2.2.1节生成的segment面着色为白色用来表示闪电本身的颜色;然后每个闪电段的燃烧颜色由式(1)计算: 2exp(())iiidGgWλλ=− (1)
其中,iGλ为段i在光线λ下的燃烧颜色;gλ为光线λ的最大值;为段i周围燃烧区域宽度的一半,把它设置为段i的长度;为距离段i的距离。由于当>2.2倍的时,ididiW2exp(())iidW−的值已经小于1%,因此在计算的时候只需要计算闪电段两侧2.2倍距离以内的点,这样就大大减小计算的复杂度。
图4是采用光线λ为淡蓝色时候渲染出来的纹理图片,其中,黑色部位为透明。
图4 渲染纹理
2.4 实现
在2.2.2节算法生成的闪电结构中,闪电被分解成闪电段,根据segment结构,以上、下节点连线为中心向两边扩展形成segment面,扩张宽度大小由上、下节点的能量power控制,这样就体现了分支层次结构以及闪电能量的递减。在每个扩展的segment面上都映射[9]2.3节生成的纹理,这样就完成了闪电本身的渲染。
对闪电周围环境的渲染,包括闪电对场景和云层的照亮效果,通过Dobashi[5]提出的在主要的闪电分支上增加数个点光源实现。
2.5 实验结果 #p#page_title#e#
与Vega中的播放纹理技术相比,本文提出的方法灵活,闪电的形状变化丰富,而且实现闪电本身渲染、闪电对周围环境的光照特效。
与其他的闪电渲染算法相比,此方法最大的特点就是简单,如果传统方法产生的闪电有10 000个闪电段,那么就必须计算10 000个闪电段,渲染10 000个闪电段,而如果采用此方法,制作的闪电纹理里面包含20个闪电段,整体闪电的闪电分支就只需要500个,那么程序就只需要计算520个闪电段,渲染20个闪电段就可以达到同样逼真的效果。
图5是近距离的效果,闪电主枝上的小分支非常多,采用纹理映射技术,只需要不到8个闪电就实现了。图6是远距离的效果,闪电形状更加真实,而且闪电对周围环境的光照特效也非常明显。
图5 近距离效果
图6 远距离效果
2.6 使用新方法需要注意的问题
Vega并不是所有的回调函数都可以使用OpenGL指令绘图,准确地说只有在属于Draw绘制进程[8]的回调函数中才可以调用OpenGL指令绘图。
由于在绘图通道中状态改变耗费严重,为了高性能运行,Vega和OpenGL都采用惰性状态评价。因此,在程序中使用OpenGL命令改变状态之前,一定要保存以前的状态,命令运行结束之后,也要及时恢复这些状态。
3 结束语
本文提出了一种新的闪电渲染方法,该方法与传统方法相比,实现简单,模拟效果真实,运算量小,尤其是在闪电本身渲染方面,适合实时性要求高的仿真系统;同时,结合Vega的特点,成功地将此方法应用到Vega环境中,解决了基于Vega的视景仿真中无法实现随机闪电渲染的难题,该方法产生的闪电模拟应用到民用飞机模拟器视景仿真中,取得了良好的仿真演示效果,此外其对于Vega中复杂模型的建立也有重要意义。
闪电模拟一直是视景仿真[1]中一个的重点。目前关于闪电模拟的方法基本都是采用粒子系统[2]。使用粒子系统模拟闪电,就是实时控制每一个粒子运动和变化,这样粒子运动的轨迹就构成了闪电[3~4]。
视景仿真主要基于Vega进行开发。Vega是美国Multi Gen-Paradigm公司用于虚拟现实、实时视景仿真、声音仿真以及其他可视化领域的世界领先级视景开发软件。Vega有自己的粒子系统,但是它没有办法进行闪电模拟。Vega的粒子系统是封装好的,用户只要设定好粒子的数量、产生方式、各种速度(风速、矢量速度和随机速度等)、颜色和重力加速度,就可以使所有的粒子按照要求开始运动和变化。由于这样的封装,Vega的粒子系统使用起来非常方便,尤其是实现雨和雪模拟的时候,然而也是由于这样的封装,用户只能知道整体粒子系统是按照什么方式工作的,根本不能得到单独一个粒子的实时状态和位置,因此使用Vega的粒子系统难以控制粒子模拟闪电。
现在,基于Vega的闪电模拟目前主要采用的是播放纹理技术,所谓播放纹理就是事先把闪电现象做成一系列的连续图片,当要产生闪电的时候,系统就高速播放这些连续的图片,就像播放闪电动画一样。然而这个技术存在很大的缺点:(1)因为每次播放的都是同样的纹理图片,所以闪电样子单一;(2)当闪电位置和大小变化的时候,都要对纹理图片进行缩放、变形,因此存在一定的失真;(3)由于闪电是纹理实现的,因此它无法实现因为闪电显现而产生的特效[5~6],比如闪电与地面物体的碰撞、闪电的光照现象、闪电云彩效果等。
图1 使用播放纹理技术产生的闪电现象
1994年,T.Reed[3]比较系统地提出了用粒子系统实现CG闪电模拟。具体方法:在云中放一个粒子,然后这个粒子随机产生几个子粒子,子粒子随机地产生向下的角度,向下运动,子粒子又随机地产生新粒子……,这样就产生了整个闪电整体。在渲染方面,他采用光线跟踪技术,不过仅仅只渲染了闪电主枝。由于当时技术的限制,他的闪电模拟做得比较粗糙,只追求模拟闪电的样子。1999年,杨子华[4]进一步总结阐述了T. Reed的闪电模拟方法。同年,P.Kruszewski[7]在随机二叉树的理论基础上,提出了一个可以改变参数的闪电模型。只要设定好模型的多个参数,就可以产生出令人满意的闪电样子。由于在渲染上,他还是采用T. Reed模型中的方法,因此他也只考虑闪电主枝的渲染,闪电分支以及周围物体的渲染没有考虑进去。
2001年,B.Sosorbaram等[6]提出自己的闪电模拟方法,在他之前的闪电模型、闪电形态的参数都是从图片或者录像中估计出来的,而B.Sosorbaram强调使用物理的参数,这样产生的闪电才更加真实。而且在他文章中也实现了闪电对周围云彩的光照效果。
闪电模拟中,闪电分支越多,渲染就越复杂,占用的时间就越多,而降低闪电分支的复杂度,整个闪电模拟就会失真,因此,闪电模拟还没有广泛应用到大型的虚拟现实仿真系统中。基于此,本文提出一种闪电渲染算法,可以通过简单的闪电形状,模拟出效果真实的闪电。
2 Vega中闪电渲染的新方法
2.1 可行性
图2是开发Vega应用的层次关系[8],从底层实现来看,Vega实际上是基于场景图(scene graph)之上的,而场景图管理系统本身又建立在OpenGL这样的标准图形库之上。在SGI平台上,Vega所依附的场景图管理系统就是Performer,而在Windows平台上,Vega所依附的是一套被称为“Jolt”的场景图管理系统(Jolt实际上就是PC上的Performer实现)。另外,Vega不但提供大量的API函数,还提供回调函数(CALLBACK)[8],因此,可以在回调函数中实现OpenGL命令和Vega API函数的结合。
图2 开发Vega应用的层次关系
Vega应用有3个必需步骤[8]:初始化系统(vgInitSys()),定义系统(vgDefineSys()),配置系统(vgConfigSys()),之后就可以进入Vega应用的主循环来渲染帧和帧同步(vgSync Frame()和vgFrame())。本文在配置系统之后使用vgAddFunc (vgGetChan(0), VGCHAN_POSTDRAW, Lightning, NULL ),定义一个名为Lightning的VGCHAN_PREDRAW回调函数,它的参数为空。VGCHAN_PREDRAW回调函数是在创建通道之后、开始画图之前调用。 #p#page_title#e#
2.2 闪电结构生成
为了提高模拟的真实度,采用以下方法进行优化:(1)采用优化过的随机函数来控制闪电段的角度和长度。(2)对所有闪电段都增加了参数来表示它的能量,同一层次的分支中,能量随着闪电段延续而减小,不同级别的分支,其初始能量也是不一样的,这样最终闪电效果不像传统方法中闪电粗细始终不变。(3)整个闪电段都共面,这个面是一个垂直地面的Billboard,其法向量平行于视线。
2.2.1 数据结构
struct segment //闪电段的结构
{
Vector3 start; //段的上节点坐标
float power_start; //上节点处能量
Vector3 end; //段的上节点坐标
float power_end; //下节点处能量
}; //闪电分支结构
{
Vector3 start; //分支最顶节点坐标
float power; //分支初始能量
float angle; //分支的初始角度
};
2.2.2 算法描述
Step1 取云间一点作为整个闪电的起始点,生成branch对象,随机生成它的power和angle,并将这个branch压入分支栈。
Step2 判断分支栈是否为空,如果为空,表示整个闪电结构已经生成,则转Step7;否则,从分支栈中弹出一个分支,以它的坐标和能量作为新生成segment对象上节点的坐标和能量。
Step3 随机决定是否在segment对象上节点处是否产生新的闪电分支,如果产生,则上节点的坐标作为新branch对象的start,而power和angle都采用优化随机函数产生(注:power小于上节点能量),并将此新的branch对象压入分支栈。
Step4 根据segment对象的上节点和能量以及整个分支的angle,采用随机函数生成其下节点的坐标和能量。因为在同一分支中能量递减,所以要控制随机函数,使得下节点的能量小于上节点能量而且还不能引起能量巨变。
Step5 保存上面生成segment对象,并将其下节点坐标和能量作为新的segment对象的上节点坐标和能量。
Step6 对新segment对象上节点的坐标和能量进行判断,如果它已经到达地面或者它的能量已经太小(可忽略),则转Setp2;否则Step3。
Step7 生成过程结束。
生成的segment对象集合就构成了整个闪电结构。
2.3 纹理生成
2.3.1 纹理结构生成
采用2.2.2节的生成算法在纹理图片上生成一个小型的闪电,这个闪电结构简单,分支少。然后根据分支的层次,以每个segment的上、下节点连线为中心向两边扩展,生成segment面。闪电主枝为最高层,因此它的面最宽,从上层分支上生成的分支为下一层,宽度比上一层递减,如图3。
图3 闪电纹理的结构层次
2.3.2 纹理着色
通常闪电段周围会有一些别的颜色,颜色是闪电与空气中化学物质发生作用以及物理作用产生的,称为闪电的燃烧颜色,一般其为粉红色或者淡蓝色。首先将2.2.1节生成的segment面着色为白色用来表示闪电本身的颜色;然后每个闪电段的燃烧颜色由式(1)计算: 2exp(())iiidGgWλλ=− (1)
其中,iGλ为段i在光线λ下的燃烧颜色;gλ为光线λ的最大值;为段i周围燃烧区域宽度的一半,把它设置为段i的长度;为距离段i的距离。由于当>2.2倍的时,ididiW2exp(())iidW−的值已经小于1%,因此在计算的时候只需要计算闪电段两侧2.2倍距离以内的点,这样就大大减小计算的复杂度。
图4是采用光线λ为淡蓝色时候渲染出来的纹理图片,其中,黑色部位为透明。
图4 渲染纹理
2.4 实现
在2.2.2节算法生成的闪电结构中,闪电被分解成闪电段,根据segment结构,以上、下节点连线为中心向两边扩展形成segment面,扩张宽度大小由上、下节点的能量power控制,这样就体现了分支层次结构以及闪电能量的递减。在每个扩展的segment面上都映射[9]2.3节生成的纹理,这样就完成了闪电本身的渲染。
对闪电周围环境的渲染,包括闪电对场景和云层的照亮效果,通过Dobashi[5]提出的在主要的闪电分支上增加数个点光源实现。
2.5 实验结果 #p#page_title#e#
与Vega中的播放纹理技术相比,本文提出的方法灵活,闪电的形状变化丰富,而且实现闪电本身渲染、闪电对周围环境的光照特效。
与其他的闪电渲染算法相比,此方法最大的特点就是简单,如果传统方法产生的闪电有10 000个闪电段,那么就必须计算10 000个闪电段,渲染10 000个闪电段,而如果采用此方法,制作的闪电纹理里面包含20个闪电段,整体闪电的闪电分支就只需要500个,那么程序就只需要计算520个闪电段,渲染20个闪电段就可以达到同样逼真的效果。
图5是近距离的效果,闪电主枝上的小分支非常多,采用纹理映射技术,只需要不到8个闪电就实现了。图6是远距离的效果,闪电形状更加真实,而且闪电对周围环境的光照特效也非常明显。
图5 近距离效果
图6 远距离效果
2.6 使用新方法需要注意的问题
Vega并不是所有的回调函数都可以使用OpenGL指令绘图,准确地说只有在属于Draw绘制进程[8]的回调函数中才可以调用OpenGL指令绘图。
由于在绘图通道中状态改变耗费严重,为了高性能运行,Vega和OpenGL都采用惰性状态评价。因此,在程序中使用OpenGL命令改变状态之前,一定要保存以前的状态,命令运行结束之后,也要及时恢复这些状态。
3 结束语
本文提出了一种新的闪电渲染方法,该方法与传统方法相比,实现简单,模拟效果真实,运算量小,尤其是在闪电本身渲染方面,适合实时性要求高的仿真系统;同时,结合Vega的特点,成功地将此方法应用到Vega环境中,解决了基于Vega的视景仿真中无法实现随机闪电渲染的难题,该方法产生的闪电模拟应用到民用飞机模拟器视景仿真中,取得了良好的仿真演示效果,此外其对于Vega中复杂模型的建立也有重要意义。
上一篇:虚拟样机应用于汽车发动机盖锁设计
下一篇:美国空军开发建模与仿真工具