您的位置:首页 > Web前端

Water-drop 节选翻译 ----Dynamic rain and its effects

2016-07-14 02:28 351 查看
Water-drop 节选翻译

—-Dynamic rain and its effects(图片稍后补上)

(https://seblagarde.wordpress.com/2012/12/27/water-drop-2a-dynamic-rain-and-its-effects/)

一、RainEffect

1、Rain splashes /Falling drops splashes

在现实世界中,“滴落(raindrop)”就意味着溅射的生成。当雨水在屋顶或者大树上留下来时就会生成“滴落”。我们先考虑空中的降雨。溅射可以很简单地通过生成一个碰撞地表的水花四溅的粒子系统来模拟。跟踪每个粒子的碰撞是十分消耗性能的,当很多“滴落”造成溅射的时候,分辨哪个“滴落”对应哪个溅射是不可能的。基于这个事实,和性能的考虑,使用两个独立的系统分别却处理“滴落”和溅射将会更加简单。绝大部分的游戏,利用在空中一系列随机且从上到下的光线跟地表的简单代表体碰撞,从而在碰撞区域生成溅射。这里有一个优化点是,溅射只需要在靠近屏幕的地方生成即可。如果你拥有一个复杂的地表以至于无法简单去模拟时,可以将溅射粒子都放在地表的边缘上了。这种情况下就不会另一种解决方案的溅射具备随机性,不过就是需要这样的效果。

我们采用另一种方法。我们简单地从与下降的角度渲染一个深度图(depthmap),而不是尝试去让这些光线跟场景物件碰撞。Depthmap给予我们在场景的随机位置生成溅射所需要的任何数据。步骤如下:

1、渲染一个深度图

2、将深度图从GPU移动到CPU

3、使用深度图去生成随机位置

4、在这些随机位置发射粒子

为了渲染深度图,我们弄一个替代品在摄像机的前面且稍微上面一点的地方,然后以这个替代品的视角渲染深度图。任何标准的ShadowMap算法优化方案都可以运用到这儿。(任何提出算法,Z double speed,to render masked after opaque,having a stream with position only,不要渲染小物件,强制渲染lod的mesh)。由于不是所有的地方都需要生成溅射,我们让美术自己选择哪些区域需要渲染深度图,我们还支持在深度图中渲染,而不再实际场景中渲染(汗…),这个很有用,因为比如玻璃这样的物件,你不可能渲染他们到深度图,然而他们又确切地将雨滴挡住了,所以你要建一个虚拟体,或者渲染一个高模的模型是很好性能的,此时可以用低模来代替。下图是正常场景 和 depthmap会产生溅射地方的对比。

从深度图生成的位置的精确度取决于深度图的分辨率和视锥体的大小。使用256*256的深度图和20m*20m的2D视锥体,我们可以得到在游戏中7.8平方厘米,高度在深度图的高度,的区域。光栅化会将高度保存到深度图中,这表示如果你在该区域有一个拥有很大高度差的物件,错误的溅射有可能在在错误的高度生成。这是内存和性能间的衡量。

为了渲染深度图,我们使用垂直投影矩阵或者透视投影矩阵。我们从来没遇到过需要使用透视投影矩阵的情况,但是接下来我还是假设我们都需要用到。而且,在DX10或者console,我们可以直接获取DepthBuffer的数据,因此我们会直接将其函数化。在PC上的DX9,我们只能将深度保存到color buffer的alpha通道上。为了兼容其他平台,我们保存的是NDC上的值。为了以防我们会用到透视投影矩阵,我们将保存一个深度的倒数(float值),用来提高精确度。下面是PC DX9的伪代码:

这是深度图:

一旦我们的深度图创建好,我们就可以在CPU获取他。所以我们需要将他从GPU移到CPU,PC上,我们会用GetRenderTargetData()来获取,或者直接使用上一帧生成的深度图。Console就更简单(懒得翻译了)最后,小小的误差是会出现的,但是对于每帧这么对的溅射,这并不能算得上什么。

从CPU获得的位置信息将会用于生成溅射。所以我们设置了一个粒子发射器在这个点的附近。这个位置必须在depthmap的范围内。在我们的第三人称游戏中,我们在想生成溅射的地方放一个可变化的球体发射器在摄像机面前。当粒子被发射,我们使用ViewProjectMatrix将它的位置投影到depthmap上,重新设置NDC上的z值,然后反投影回来,得到最后的位置。

一般来说,溅射会发生地面上,地面上有一层很薄的水,大约10-20ms。溅射的动态表现依赖于好多因素,主要分成两类:材质的属性(粗糙的,固体,潮湿的,倾斜的,防水的) 和降雨的属性(大小,速度)。粗糙的材质更加会影响溅射。顶(后面会说到)半径和高度是降雨属性的直接影响。溅射的数量跟速度有关,溅射的区域的分配可以用一个随机模型描述,更详细的介绍可以参考这个【7】

当然,对于游戏来说,考虑太多的细节是很困难的。例如,The Toy Shop Demo使用一个简单的已经贴图的四边形动画来模拟高速的牛奶滴落。四边形是被缩放来生成差异的。坚持该例子的两个主要特性似乎是个好方法:一个生成好的顶,通过高度,半径和其他的参数来缩放。然而,这种效果一般是由FX人员制作,他们做的东西一般是不管物理特性的。在这个问题上,John David ThornThon 想到一个改进的方法,允许美术人员控制溅射【9】我们采用该方法,,用一个mesh来代表顶,一个精灵图来模拟溅射的动画。下图展示了一个溅射的顶的线框(白色),和精灵图的线框(红色),他们都采用uv动画。

为了性能,我们没有在溅射加入光照,但是Toy Shop Demo使用lightmap来模拟天空和路灯的光,并在顶点shader上计算一些平均值【2】

2、Rain /Rain drops

懒得翻译了(不是废话,是描述降水的物理特性,对于理解还是很有帮助的)

有两种方法在游戏中实现降雨,粒子系统或者一张大纹理。

粒子系统一般由一堆斑纹的简单形状(如长方形)组成。粒子系统经常产生真实的运动,他们可以被风吹而且可以在GPU上很高效地实现。然而,为了性能的考虑,粒子系统一般会绑在摄像机上,尽可能地生成最少的粒子。粒子系统不好的地方在于他不能缩放,强降雨的时候需要生成大量的粒子,这显然会降低帧率。

大纹理的方法则是采用uv动画(程序的或者手工的)去模仿斑纹。对于强降水或者轻量的降水,大纹理的方法都有同样的性能表现,这个跟粒子系统是不同的。但是他们缺乏现实中的深度和动感。Toy shop demo用一个屏幕那么大的四边形(postprocess)。Demo尝试去效仿多层次的降水,不同层次的纹理,用不同的速度,深度来运动。不同的输入生成的 w是为了投影到不同的层上【3】。然而这种方法在摄像机移动的时候会有弱点。Postprocess使得当我们看着地面时,会看到雨是平行于地面的。

“Flight simulator 2004”将这种四个动态纹理的方法映射到双椎体中。使用一个椎体的mesh,并且根据摄像机的变化,旋转该mesh。这种方法可以实现摄像机俯冲时候的降水效果。他们将四个纹理缩放并且慢慢地滚动他们,以此来实现更小的和更慢的雨滴来模拟深度。

为了性能,我们最后选择了双椎体的方法。

我们确定了摄像机的四个层次:

每个层使用相同的提前经过动态模糊处理的“滴落”纹理

我们将双椎体绑定在摄像机上,并让其位置跟摄像机一致。我们在双椎体的内部进行渲染,我们可以在往上或者往下看的时候,很平滑地改变下雨,通过将他们的透明度存放在顶点颜色上。然而我们并不这么做。

我们对于不同的层,以不同的速度和比率来变化和不规则地缩放双椎体的纹理坐标。变换模拟了滴落的动态,远一点的层采用更大的比率来增加雨滴的数量。为了模拟风的感觉和雨滴不会一直沿着一个直至下落的方向,我们对他们提供一个额外的循环旋转。在实际应用中,我们并没有急于风的方向令其旋转,而是让美术自己确定某些混乱的旋转。下图是经过变换(旋转…)后的两个不同层的纹理坐标:

\

下面4张纹理显示了各自的最终结果:

对于开始两层的纹理坐标变换代码:

为了获得降雨的深度和平行的感觉,我们想去使用深度缓冲来遮挡雨滴。因此每个雨滴都拥有他们的虚拟坐标。我们已经确定了每个层对应不同区域。在每个层中,我们使用高度图加上层对应的区域偏差来给每个雨滴提供深度信息。我们使用从视点到椎体像素的方向和深度来检索雨滴的虚拟坐标。生成高度图并不简单,建议用程序生成。

对于粒子系统,一个轻度的深度测试可以逐渐降低雨滴的不透明度。这种深度遮挡将会提高雨的真实性,尤其在一些特殊的场景(摄像机面前有很多遮挡物)。像我们这种第三人称游戏,一些雨滴将会在摄像机和玩家之间。这种测试还会带来一个很好的额外效果,就是当我们看向地面时,雨滴将会消失。因为雨滴的深度在地表的后面。另一个可以考虑的遮挡是从天空看下来的深度遮挡。当我们在一个遮挡物下面,我们是不希望他会下雨的。这个可以很简单地利用生成溅射时候的深度图。就像shadowmap,我们将雨滴的虚拟坐标投影到和做个深度比较,就能知道该雨滴是否应该停止下降。这种测试可以使用硬件的PCF,如果depthmap支持这种格式的话。

实际上,出于性能的考虑,我们做了几点简化。我们选择仅仅在前两层做这种雨滴的遮挡,并且以一个低分辨率的depthmap进行。我们用于生成溅射的深度图有有限的范围,因此我们将前两层的距离安排在一个恰当的距离上,以便他会被深度图完全覆盖(深度图是在摄像机稍微前一点的地方生成的哦~)。对于超出该距离的雨滴,我们将不会投影他的虚拟坐标。相反,会在视图空间上做一个简单的深度测试。下面是对于前两层的伪代码:

对于第三第四层,我们根据该层的距离和深度缓冲添加一个全分辨率的mask。这种方法同样会应用到前两层上,为了修正由于低分辨率所造成的误差。

下图是对于前三层的mask:

对于3,4层,我们添加一些艺术的效果去提高雨滴的多变。我们生成两张平滑变化的纹理,并使用blend mask去减弱雨滴的重复下降。该纹理采用比刚才更低的分辨率:

低分辨率和全分辨率的代码:

当用全分辨率渲染双椎体以获得最终效果是,在低分辨率通道生成的纹理将会被用到…

在代码中,会看到RainOpacities属性,这是让关卡设计人员根据游戏内容去mask某些层。比如,对于远的层,我们没有深度图去遮蔽他们,这时候关卡设计人员就可以帮助我们关掉他们。这对像Toy shop demo上所说的控制雨滴的透明度也很有帮助。该代码也表达了雨滴的颜色深度(instensity)。雨滴的强度允许我们动态确定降雨的强度。为了性能,我们简单地调整雨滴的颜色深度。雨滴纹理包括了不同的颜色深度,这允许我们将雨滴的颜色深度调高后,看到更多的雨滴。这里有个地方要注意的是,你不可能展现比纹理上拥有的雨滴数量更多的雨滴。所有的代码都是为了将雨滴减少,所以在设计纹理时要考虑最多雨滴的情况。

下图是整体效果:没雨,少雨,大雨

处于性能影响,我们采用两个pass,一个是低分辨率的pass,使用四分之一的分辨率。一个是高分辨率的pass,使用全分辨率来渲染双椎体。为了避免额外的消耗和重复的工作,高分辨率的pass将集合所有的postprocess(动态模糊,DOF,色阶调整,ToneMapping…),因此我们使用同一个双椎体去有效处理他们。.

不足:无法处理透明的,当然完全透明的是没有问题的,然而对于半透明的,还是会看到雨滴。

使用一个特定的纹理去对应雨滴纹理,来给每个雨滴提供instensity信息是可以的。当instensity增加时,我们将会将他跟一个闕值进行比较,以此决定是否将雨滴关闭。这个纹理跟高度图很像,不过保存的是颜色深度信息。。现在的结果已经满足我们的需要了,并且我们还是想节省一些指令。这里,我还想补充从shaderX5中的toy shop demo看到的一段有趣的代码【3】。我们没有谈论光照是因为光照对于我们来说太消耗了。然后他是得到一个更好的更真实的降雨效果,这是很重要的。正如一开始说的理论部分(没翻译),反射,折射,内部反射都是需要考虑的。Toy shop demo选择对环境贴图(Environment map)进行采样来实现反射和折射。如果你的引擎支持,这是很适合任何区域或者局部视差立方图(Cubemap)算法的。(详细参考Image-based Lighting approaches and parallax-corrected cubemap)

其他:(懒得翻译了,跟光照有关)

[20] also have a lightweight model to simulate refraction, reflection and internal reflection. The nice idea here is the use of a precomputed texture mask that determines the direction of the refracted viewing vector for a quasi spherical raindrop. The refractionvector is later used to index a texture storing a wide field of view render of the background. The internal reflection is simulated for direct light with simple factor.

A technique of interest to light raindrops at low cost is describe in [18] with an “inferred renderer”. Raindrops are light like other objects.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: