
---- 狗妈模糊昨夜泪目。
大家好,我是昨夜丶,最近闲来无事用Unity渲染MMD,遇到一个很要命的问题,或者说想要使用Unity进行影视类创作的,应该都会遇到的问题。说到这个领域,就不得不提到Unity的Timeline系统,使用的话它可以快速正确的排列时间轴,可以说是创作的必备品。但是想必很多人都遇到过这样的情况。就是它....无 法 为 后 期 特 效 组 件 K 帧,就可能会导致出现下面这样的效果。

镜头拉远时,景深特效的焦距由于是固定值,画面直接模糊掉了
直接谷歌好像也没有太多关于这个问题的解决案例,经过一天研究,想了大概三个方法可以解决,写下文章做一下技术分享。
从结论上来说,Unity并不能直接通过现有Editor来实现PostProcess在Timline下的关键帧动画,高清渲染管线(HDRP)下的新版也不能。看似Unity似乎对此没有更好的办法,但是如果你并不惧怕代码,通过Unity良好的扩展性,是可以来实现我们的效果,本次我使用Unity2019.3.2f1版本,创建的是内置渲染管线(BRP)项目配合PostProcess V2进行演示。我将控制镜头景深的焦距参数为切入点,解释我是如何实现的。
方法一:通过Timeline控制Post-process的权重参数

Timeline可以为权重参数添加关键帧
如果你的镜头特效并不是特别复杂,可以直接在Timeline上对PostProcessVolume的Weight进行K帧。我的场景包含了一个全局特效和一个局部特效,由于我的局部特效仅有景深,我通过动画权重参数获取到了一个不错的效果。

Timeline控制Post-process的权重参数效果
很遗憾的是,这样的方法难以满足复杂的后期特效,其实本质上并没有改变焦距参数,如果摄像机位置变化到其他位置,也很难对该效果进行复用,所以使用场景非常有限。
方法二:通过编写Mono脚本和Editor实现
这是利用Animator可以对Mono脚本上的参数进行K帧的特性,用代码分别实现Game下和Editor下对特效组件的操作来实现效果,这个方法相对简单。

DepthOfFieldTool脚本
我创建了上面的脚本并且挂在了含有我需要控制的PostProcessVolume的物体上,在Timeline里使用Animation Track 进行K帧便可以在运行环境下实现效果。但是这对于制作者来说,只能在运行环境下预览是十分痛苦的,为此我们还需要编写对应的Editor,这样就可以在非运行环境下进行预览。

DepthOfFieldTool脚本的Editor
看一下效果:

运行状态和编辑状态都有良好表现
方法三:编写PlayableAssets脚本
该方法效果无限好,它实现了更加直观的Timeline编辑,但是由于该API平时用的不多,我也是学习了一下,现学现卖,如果有什么不对,希望能够指正。

通过该方法可以直接在Timeline上拖入你想操作的PostProcessVolume
通常情况下,你并不可以直接选择一个PostProcessVolume到一个track上,但是你可以编写相应的脚本实现。

TrackAsset
编写了上面的脚本你就可以在TimeLine右键创建一个可以拖入PostProcessVolume的Track

右键创建

创建结果
但是事情还远没有结束,首先定义你的播放行为,这一步最为复杂和关键,我将拆开几步说明。

可以使用这个方法快速创建
打开脚本可以看到它已经告诉你,你可以重写的一些函数。ProcessFrame即为每帧行为,让我们开始编写。

定义变量

重写ProcessFrame
接着你必须新写一个脚本定义你可以在这个track上创建什么样的cilp。并且在第一个写的TrackAsset脚本上告知。

PlayableAsset

TrackAsset
如此一来你便可以在自定义Track上定义自定义clip

可创建自定义Clip

效果已有但不完美
但是这并不完美,还有一些问题,比如当时间线进入你的cilp然后又出去时,它的值并没有被还原,Unity似乎没有为我们提供相应的钩子,所以我们只好自己记录初始值,并且在PlayableBehaviour的OnBehaviourPause()里执行还原。

定义变量接收初始值

PlayableBehaviourProcessFrame赋值前判断是否有初始值

PlayableBehaviour的OnBehaviourPause进行还原
做到此时,应该拥有的效果:

完美实现Clip区域内控制,出去释放还原
但是还没有完,最关键的动画还没有做出来,如果你融合两个clip 你会发现只是单纯的切换

没有任何动画效果
我们还需要重写TrackAssets下的CreateTrackMixer函数

这里我依旧使用DepthOfFieldTrackBehavior
然后在PlayableBehaviour的ProcessFrame里添加混合代码,我这边实例使用简单混合演示。

简单混合
这样一来效果基本实现

已经有混合动画效果了
贴一下最终的代码

DepthOfFieldTrackAssets

DepthOfFieldAssets

DepthOfFieldTrackBehavior
再来测试一开始的镜头

来测试一开始的镜头
PlayableAsset的方法确实也是现学现卖,也不一定完全正确,比如在CreateTrackMixer()里return我的DepthOfFieldTrackBehavior,在PlayableAsset里也return我的DepthOfFieldTrackBehavior,好像会导致ProcessFrame执行两遍。虽然不影响效果,但是很在意。
欢迎留言与我交流。