请选择 进入手机版 | 继续访问电脑版

查看: 5695|回复: 4

[原创] 教程 | 在Unity中实现逼真的下雨效果

[复制链接]

1088

主题

1774

帖子

2万

贡献

管理员

Rank: 9Rank: 9Rank: 9

积分
23135
QQ
发表于 2017-12-3 01:01:04 | 显示全部楼层 |阅读模式
《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的计算公式稍微化简后得到下面的公式

[C#] 纯文本查看 复制代码
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)!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x

0

主题

29

帖子

570

贡献

中级UU族—1级

Rank: 4

积分
565
发表于 2017-12-3 13:40:08 | 显示全部楼层
工程里别忘Postprocessvolume中profile中添加一个并且在overrides中添加PP Rain Fx要不然你会看不到效果

0

主题

2

帖子

55

贡献

初级UU族—2级

Rank: 2

积分
55
发表于 2017-12-15 07:12:11 | 显示全部楼层
你好,我在自己的项目里的main camera中添加 volumeric fog这个脚本,但是会有报错
Volumetric Fog requires compute shaders and this platform doesn't support them. Disabling
请问这个问题的原因是什么呢

2

主题

25

帖子

240

贡献

初级UU族—3级

Rank: 3Rank: 3

积分
240
发表于 2017-12-20 07:49:18 | 显示全部楼层
请问:
public sealed class PPRainFXRenderer : PostProcessEffectRenderer<PPRainFX>
{
    public override void Render(PostProcessRenderContext context)
    {      
        PPRain.Instance.Render(context.camera, context.source, context.destination, context.command);
    }
}

这里PostProcessRenderContext context参数是从哪里传入的呢?是不是从PostProcessVolumn 脚本设置里设置的参数呢?

0

主题

1

帖子

15

贡献

初级UU族—1级

Rank: 1

积分
15
发表于 2017-12-28 07:03:06 | 显示全部楼层
seekeer 发表于 2017-12-20 07:49
请问:
public sealed class PPRainFXRenderer : PostProcessEffectRenderer
{

Render函数是重载函数,是被系统调用的。参数context并没有暴露给用户填写。不过其中的Camera是在Post Processing Layer中指定的。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

快速回复 返回顶部 返回列表