返回作品索引WORK / CASE STUDY
工具

URP - RendererFeature :Depth of Field(gather)

景深效果

独立作品

让周围轻一点,文字近一点。
URP - RendererFeature :Depth of Field(gather)
作品主视觉点击画面,查看细节
ENGINE
Unity · URP
TECH
RenderFeature · rendering · Shader · 后处理 · Pipeline · URP · unity
ROLE
工具
YEAR
2026
案例目录23 小节

效果理解

对于⼀个给定设置的相机镜头,它有⼀个物体聚焦的范围,即它的景深(depth of field)。任何超出这个范围的物体都是模糊的,距离越远越模糊

DoF本质是:离焦平面越远的像素,模糊半径(CoC)越大。CoC也可以叫容许弥散圆

参考视频

焦距,光圈

https://www.bilibili.com/video/BV19W411M7Jx?vd_source=6519de45bf96ecc7dc76d9b36fe3cdcd

焦点、焦距、主光轴

https://www.bilibili.com/video/BV1Gu4y127mU?vd_source=6519de45bf96ecc7dc76d9b36fe3cdcd

景深光圈等介绍,同时解释了弥散圆概念

https://www.bilibili.com/video/BV1t24y1k7Ye?vd_source=6519de45bf96ecc7dc76d9b36fe3cdcd

相机的物理属性

焦距和焦点

对于现实中的相机,控制视野范是焦距,这个焦距由透镜的本身物理属性决定,人眼对应的焦距是50mm

焦距短 → 视角宽,能看到很大范围,物体显得小而远
焦距长 → 视角窄,只能看到一小块,物体显得大而近

景深和焦深

同光圈下,焦距越长,同样距离的物体,背景模糊越强烈。长焦镜头收集光线的方式导致CoC半径更大。大致是长焦拍人像,短焦拍大场景。

相关方案

RTR4中提到的以及AI总结的几种方案如下:

引擎方案
URP Gaussian DoF可分离高斯,移动端
URP Bokeh DoFScatter-as-Gather,高质量
HDRP DoFGather+Scatter混合,物理相机参数
UE(旧)Hexagonal Bokeh
UE(新)Scatter-as-Gather,Circle DoF

Accumulation Buffer(累积缓冲区)

RTR4中提到可以使用累积缓冲区的方法模拟景深,原理是在相机镜头处取一个圆形区域模拟光圈,对圆盘上的多个位置分别渲染,每次渲染时视线方向都朝向focal plane上的同一个焦点。焦平面上的物体每次位置一致,叠加后清晰;焦平面外的物体每次位置略有偏移,叠加后自然模糊。这种方法物理上更准确,但需要渲染N张图再平均,性能开销极大,不适合实时渲染。

CoC后处理(gather)

假设像素周围的局部邻域都具有相同的深度,可以进行聚集(gather)操作。对同一深度区间的像素进行模糊,高斯模糊是圆形的,那么这样可以去模拟弥散圆,缺陷是在背景高光很明显的情况下会向前方物体的边缘溢色,同时不能满足前景向背景溢色

Scatter(散射)

CoC后处理默认是Gather思路,每个像素去采集周围的贡献。但前景虚化有个问题:前景模糊的像素应该”溢出”覆盖到后面的清晰区域,Gather做不到这点。 Scatter反过来,每个大CoC的像素主动把自己”扩散”到周围像素上,用Compute Shader把像素当作小精灵(sprite)渲染出去。质量好但实现复杂。 实践中常见的是Scatter-as-Gather:用Tile预处理找出每个区域最大CoC,再用Gather模拟Scatter的效果,避免真正的Scatter开销。

分布式光线追踪

就是Accumulation Buffer的光追版本,在光圈圆盘上随机采样光线方向,天然支持正确的DoF。离线渲染(电影)的标准方案,实时不适用。

DoF(gather)实现

直觉参数设计

最简单的参数就是定一个平面作为焦平面,然后设置一个阈值,在这个差值范围内就是清晰的,其余区域是模糊的,模糊程度再用另一参数控制

对应参数是这样的

物理参数的转化

因为现实中物理相机的景深与光圈有关,而且景深并非是关于焦平面对称的,如果更符合现实的话就需要转换一下参数

透镜的成像公式,焦距和物距像距的关系

1f=1do+1di\frac{1}{f} = \frac{1}{d_o} + \frac{1}{d_i}
  • ff = 焦距
  • dod_o ​ = 物距(物体到透镜的距离)
  • did_i = 像距(透镜到感光元件的距离)

那么这样可以得到像距的表达式,像距就是对焦距离,也就是物体成像最清晰的位置,也就是感光元件的位置

di=f⋅dodo−fd_i = \frac{f \cdot d_o}{d_o - f}

根据感光元件位置,再来对比真正的物体成像位置,可以得到物体成像位置和对焦位置两者的差值,如果两个差值较小,那么就认为是清晰的,反之就需要模糊

Δdi=di,depth−di,focus\Delta d_i = d_{i,depth} - d_{i,focus}

光圈直径 =f/Aperturef / Aperture, 从几何关系(相似三角形)推出CoC:

CoC=fAperture⋅∣Δdi∣di,depthCoC = \frac{f}{Aperture} \cdot \frac{|\Delta d_i|}{d_{i,depth}}

最终展开化简后就得到:

CoC=f2Aperture⋅(focusDistance−f)⋅∣1−focusDistancedepth∣CoC = \frac{f^2}{Aperture \cdot (focusDistance - f)} \cdot \left|1 - \frac{focusDistance}{depth}\right|

CoC计算

第一个Pass,计算CoC后直接存入RT的A通道中后面备用,场景颜色也存入RGB通道

高斯模糊计算

第二个Pass,就是计算模糊了,圆形渐变自然是起手高斯模糊,强度则由CoC控制,这里是水平模糊

合成

第三个Pass,还是同样读取CoC然后进行垂直高斯模糊

然后合成,计算CoC的时候保留正负值,这样就可以针对前景背景设置不同强度的模糊

最简的景深这三个Pass就足够了,高斯的效果如下,效果其实还是可以的

关于模糊的优化

因为模糊不太需要高频信息,为了减少像素的处理量,可以考虑先对RT进行降分辨率在进行模糊,虽然质量会有所下降,但是这样可以少处理很多像素

同样参数下效果如下,因为降分辨率,所以GaussianHalf参数需要降低才能匹配差不多的效果

关于散景Bokeh

在物理相机中,因为光圈数量不同,模糊出现形状也不一样,这种模糊效果被称为Bokeh,如果要实现的话,其实本质上就是用不同的模糊算法

BokehCircle圆形

关于算法可以去看这个网站,直接让ai帮忙写也可以 https://mini.gmshaders.com/p/phi 大致的分布就是这样

代码如下*

效果如下,方便观察我把强度设定的很高,高光成圆形,适合较亮的灯光和高光效果

BokehHexagon 六边形

因为六边形计算其实很费性能,所以有个简化版

高质量

对于第二中就是六边形的散布,最简单的就是直接在原有基础上剔除掉就行

效果如下

简化版

简化版使用3Pass,向三个方向拉伸,然后组合得到六边形,下面是代码,注释内容是借用了高斯的权重,这样会非常平滑,六边形特征有些微弱,我觉得效果不好换成了平均权重 垂直方向

30度方向拉伸

-30度方向拉伸

效果如下,效果稍差

当前效果和缺陷

因为gather算法的问题,在亮度差异比较大的情况下,背景模糊溢色会污染前面物体的边缘

同时前景模糊并不能向后晕染,尽管对前景的模糊程度进行了强化,也只是模拟一点效果

不过这个缺陷其实不是很严重,从效果说来说已经可以使用了,如果需要彻底解决就需要使用Scatter(散射)的方案去解决了,这个后续有时间我再去学习研究研究 DOF加上之前Bloom,FXAA,我觉得还不错