

散景模糊+景深效果
散景模糊是一种很特殊的模糊,他会将背景的光点模糊成漂亮的圆斑,这种效果会营造一种更加漂亮的朦胧感来模拟大光圈的格调。而不是高斯模糊,均值模糊,或者其他类似模糊得到朴素的模糊感。但是在实时渲染领域,它仍然是一种昂贵的模糊,而关于它的核心算法,黄金角度与均匀排布,会是这篇的重点。
先普及一下黄金角度。黄金分割相信大家都不陌生,a/b=b/(a+b);其中a<b,a+b为总长度。类似地推导出黄金角度的定义,a/b=b/(a+b),a和b为角度;a+b之和为2Π周期(Π为圆周率)。把b=2Π-a代入,即可得a/(2Π-a)=(2Π-a)/2Π,求得a=0.763932Π,也就是 137.5°,而b=1.236038Π。
这个角度求出来有何用呢?大自然界用这个角度,造就了很多美妙的事物。大部分的花朵,他们的花瓣都是137.5°的角度旋转;因为按照这样的排列模式,可以最佳利用有限的空间,提高生存必须的阳光获取量,同时也能获得更多的雨水。

向日葵按照137.5°最佳排布跨葵花子
我们要如何得优雅利用这个“完美”的角度帮助我们能在unity里画出均匀的光斑呢?这里有篇文章介绍了一些相关内容https://zhuanlan.zhihu.com/p/64288140,我们需要的,就是根据任意一个像素,能画出以该像素为圆心的一个均匀分布的实心圆。假设一张200*200分辨率的图片,它有40000个像素,那我们需要绘制40000个圆出来,然后每个圆的颜色取原像素位置采样的颜色,这时候如果部分场景里的颜色比较暗的话,比如下图中的天空部分为比较暗的纯色,即使也画了圆但是也是看不出来圆的效果;而比较亮的部分,则因为不停叠加不停叠加最终画出一个朦胧的圆斑,这就是它的核心原理。

对比
现在的又来了新的一个问题;如何根据一个像素的位置和颜色,把以它为圆心一定范围为半径的所有像素都均匀画上同等颜色?均匀对于我们来说非常重要!因为这样才能保证我们得到的圆斑是非常整齐的颜色密度,如下图所示,就是利用137.5°得到的最完美均匀分布。

完美均匀分布
在shader里,先获得一个顶点绕着原点旋转137.5度的旋转矩阵。因为我们是对2d的uv进行操作,只需要获得一个2x2的矩阵即可。先简单推导一下旋转矩阵。

向量a的模长为r,它旋转θ角度后得到新向量a‘。对于向量a和 向量a‘,他们分布有如下等式。
x=r*cos(α)
y=r*sin(α)
x’=r*cos(α+θ)
y‘=r*sin(α+θ)
将x’和y‘的三角函数展开
x’=r*(cos(α)cos(θ)-sin(α)sin(θ))
y‘=r*(sin(α)cos(θ)+cos(α)sin(θ))
并将x和y带入x’和y‘,消掉α和r,可得
x’=cos(θ)x-sin(θ)y
y’=sin(θ)x+cos(θ)y
这样,我们就得到了我们想要的2维旋转矩阵M。

旋转矩以及对应坐标
而旋转137.5°,则以 新坐标=mul(旋转矩阵M,上一次坐标)的方式进行迭代。
基础的数学推论已经定义好了,如果你能成功理解上面的推论,后面的内容就会非常简单。现在,让我们像向日葵操控葵花子一样去控制像素吧。先构建旋转矩阵,其中的2.3398是对应137.5°的弧度,也就是137.5/360*2*Π=2.3398。

定义初始采样点的坐标偏移值,后续我们将会对偏移值围绕采样点进行旋转,并增大半径。

然后在for循环里,每次都重新计算一遍新的坐标偏移值:①增加半径。②进行旋转137.5°。

每次半径的为根号t,t为迭代次数,也为第t个定点数,如下图所示。它为什么等于根号t,限于篇幅这里就不展开讲解,这里有文章https://zhuanlan.zhihu.com/p/25998937,有兴趣的读者可以去看看。当然读者也可以这么理解,既然面积S是均匀线性分布,即半径R的平方是线性增加,则R的平方~t,即R~根号t。

示意图
然后进行采样,最后输出即可。
材质面板里,我们丢一张贴图进去,进行100次迭代,设置半径为1。漂亮的模糊圆斑就出现了,perfect!

效果图
然而当我们提高半径为3,就会进行得到漂亮的螺旋线的图,嗯?是不是有些眼熟,这就是花朵的扩散路径,也是我们想要的平均分布。

螺旋线图
这个图出自这篇文章里https://zhuanlan.zhihu.com/p/25998937。

下一篇,将会讲如何在unity里实现把景深和散景模糊结合在一起,并附上完整的代码,如果读者经验丰富,看到这里读者应该就可以完全靠自己去实现了,不需要第二篇的讲解了吧。