Notifications
Article
[教程] 在Unity中实现逼真的下雨效果
Updated 6 months ago
877
0
作者:Unity技术经理 成亮
在《Unity 2017中Cinemachine新功能案例详解》这场技术直播课程中,我们曾经为大家介绍过小型项目《Neon》的场景搭建,并展示了Cinemachine在过场动画以及游戏玩法方面的强大能力。不少开发者询问场景中逼真的下雨效果是如何实现的。今天Unity技术经理成亮将会为大家详解如何在Unity中实现逼真的下雨效果。
《Neon》项目中逼真的下雨效果实际上是一个自定义的后处理效果,不过和通常的平面后处理效果有所不同的是,雨水有距离感,有光感,也有从天而降的倾泻感 ,所以显得很真实。本文就会通过一个简单的示例,介绍这个效果的实现原理。

说明:为了方便大家学习,我们将为本文提供相应案例下载,下载地址请在本文末资源“相关资源”查询。

场景搭建

在《Neon》项目的场景中,我们看到当雨水穿越光线的效果。为了模拟出这样的效果,我们在示例场景中也需要添加相应的光源和雾效。需要说明的是,该场景中使用的体积光使用了Unity提供的开源组件VolumeLighting。
场景搭建的主要步骤如下:
  • 在MainCamera中添加VolumetricFog组件:首先添加VolumeFog组件,可以方便查看后面添加的体积光的范围。添加好后,可以在组件中调整全局雾密度等相关参数。
  • 添加区域光和管状光,并调整灯光的范围:在VolumetricLighting文件夹里面可以找到AreaLight和TubeLight的预制件,可以直接加入到场景中。下图是一个AreaLight的例子,我们可以调整他的范围以及光线强度等参数。
  • 场景搭建完成后,我们就可以看到下图所示的效果了。
实现自定义后期处理类
Unity最新的后处理栈V2版本提供了多种后期处理效果,包括Bloom,AutoExposure,MotionBlur,ColorGrading等多种特效。使用也非常的方便,主要就是PostProcessVolume,和PostProcessLayer二个组件。具体使用可以参考快速指南。除此之外,我们还可以很方便的添加自定义的后处理效果。
下面我们就来实现我们的后期处理类吧,主要有下面二步。
  • 实现自定义特效的设置
首先需要创建一个类PPRainFX 并继承于 PostProcessEffectSettings。然后重载函数IsEnableAndSupported,主要是为了确保在场景中存在PPRain实例的时候特效才能被使用。
PPRain是特效的主要实现脚本。由于该下雨特效的参数我们都放到PPRain中去配置了,所以在此处,我们并没有定义设置相关的参数。
  • 实现自定义特效的渲染
先创建一个类PPRainFXRenderer,继承于PostProcessEffectRenderer<PPRainFX>。然后重载Renderer函数,在该函数中,我们主要是把PostProcessRendererContext中保存的摄像机,渲染源,渲染目标,以及命令缓存传递给PPRain的Render函数。后面我们会具体解释Render函数的实现。
下面这张图是PPRainFX.cs的内容,包含了后期处理类的完整实现。

渲染下雨动画

下雨动画的实现,很自然的想法就是把雨水画到屏幕上,然后利用UV动画实现下雨效果。
可是在3D的场景中,随着镜头的旋转,雨水的视觉效果会相应的变化,尤其是当镜头向上的时候,你会觉得雨水是从一个很远的点发射出来,如下图所示。
为了实现这样雨水从天而降的倾泻感,我们把雨水的贴图贴到一个类似于纺锤体的模型上。
说完基本原理,接下来我们就讲一下具体的实现,主要有以下二步。
  • 创建PPRain类,实现参数配置以及模型绘制功能
先看下目前所需要的一些设置参数,如下图所示,变量rainShader, rainMesh, rainTexture用于绑定着色器,模型,贴图,rainData0是用于调整UV动画的参数。
再看一下Render函数,该函数实现了模型的绘制,会被之前说的后处理栈的render函数调用。在该函数中,首先会设置shader的参数,然后通过drawMesh绘制模型。xform定义了模型的坐标变换,可以看到模型会随着摄像机移动,也就是说可以一直在镜头中看到下雨到效果。
  • 创建PPRainShader,实现UV动画。
下图是PPRainShader的frag函数,注意uv坐标计算部分的代码,我们使用_UVData0对uv实现比例和位移变换,从而控制雨滴的大小以及x和y方向的位移速度。经过变换后的uv0就是实际用来贴图采样的坐标。

实现雨水的距离感

现在我们已经基本实现了下雨的动画效果。但是为了更加真实,我们还需要更近一步,让雨水有距离感。也就是说近距离的雨水会更加明亮,远距离的会变暗,而且会被近距离的物体所遮挡。为了实现这样的效果,我们主要需要以下三步:
  • 获得当前像素点的深度信息
在shader中获得当前像素点的深度信息用于判断遮挡关系。如下图所示,这是frag函数中的一段代码,通过内置的_CameraDepthTexture变量可以获取到当前Camera的深度信息,并使用UnityCG中定义的Linear01Depth函数将其转换为线性范围,然后再乘以远剪裁面的值进行放大便于后续使用。最后得到的linearViewDepth就是我们需要的当前像素的深度值。
  • 计算雨水的深度
如下图所示,我们其实在制做贴图的时候通过g分量来表示了雨水的深度,layerDistances.x 和layerDistances.y分别表示雨水的最近和最远距离。所以最后我们可以计算出雨水的深度值layerDistance。
  • 计算雨水明暗度以及处理遮挡
继续看上图中depthscale的计算,当物体的深度(sceneViewDepth)超过了雨水的深度(layerDistance)一定范围后,depthscale为1,显示雨水,反之为0,雨水被遮挡。在明暗变化方面,我们用了贴图的r分量(rainAndDepth.r),实际意义和之前的rainAndDepth.g是一样的。最后我们得到的output.a实际上就是包含了雨水的明暗变化以及遮挡关系。

实现光感

最后,我们还需要考虑当雨水穿过场景中的体积光时,颜色的变化。这部分的实现在InjectedLight函数中,如下图所示。
首先,我们需要计算在雨水在体积雾中的深度值z。在z的计算公式中,我们看到了_CameraFarOverMaxFar, _NearOverFarClip这个几个变量,它们是在VolumetricFog中设置到shader中的全局变量。我们把z的计算公式稍微化简后得到下面的公式
z = (linear01Depth * Camera.farClipPlane - VolumetricFog.nearClip) / (VolumetricFog.farClip - VolumetricFog.nearClip)
从化简后的公式中,我们可以看出z最后被转化到在VolumetricFog剪裁空间坐标。有了坐标后,我们就可以通过Tex3D函数取得体积光中的颜色值了。_VolumeScatter也是在VolumetricFog中定义的保存体积光的3D贴图。

实现效果

通过上面几个步骤,我们大概了解了如何在后处理栈中实现较为真实的下雨效果。我们看到了Unity的后处理栈不光有很多内置的效果,也可以很方便的加入自定义效果。也看到了Unity 提供的开源插件VolumetricLight可以帮助我们实现多种体积光效果。在雨水效果的真实感方面,我们考虑了视角,距离,光感等因素,但其实还可以做更多,比如更多层次感,风力的模拟等等。

资源下载与参考

本文示例项目下载:
https://github.com/chengliang-u3d/PPRain
VolumeLighting
https://github.com/Unity-Technologies/VolumetricLighting
PostProcessing
https://github.com/Unity-Technologies/PostProcessing/tree/v2

Tags:
Unity China
207
Comments