查看原文
其他

教程 | 在Unity中实现逼真的下雨效果

2017-12-03 成亮 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的内容,包含了后期处理类的完整实 46 32846 46 15262 0 0 3739 0 0:00:08 0:00:04 0:00:04 3739现。


渲染下雨动画

下雨动画的实现,很自然的想法就是把雨水画到屏幕上,然后利用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

小结

希望通过本文的抛砖引玉,让大家都有兴趣去实现自己的后处理效果。更多精彩案例与教程尽在Unity官方社区(unitychina.cn)!



更多教程

官方活动

为期1个半月,由Unity举办的《Neon Challenge》正式开始了。全球2万美元的奖金等待着开发者们,我们后续会详细介绍本次活动。感兴趣的朋友们请访问Unity Connect了解详情。

地址:https://connect.unity.com/challenges/neon


点击“阅读原文”访问Unity官方社区!

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存