UE中的各种depth的解释及应用

背景

有时候我们在绘制透明的人物角色时,既想要其透明,又想要有一个基于fresnel的边缘光。直接写出来的材质会遇到透明面的穿插交叠的问题。如

本文会在解决这个问题的同时,对UE4中的scene depth, pixel depth和custom depth这三个容易混淆的概念做个解释和备注。

三种depth比较

Custom Depth

默认情况下不会启用,但是用户有选择性的向其写入深度,从而实现一些复杂的效果。
首先在对象面板上勾选Render Custom Depth Pass,这样就能将该物体的深度信息写入custom depth buffer了。

这里需要注意,对于透明物体,如果直接开启Render CustomDepth Pass是没有作用的,还需要在对应的材质编辑器的属性面板上勾选

查看custom buffer如下图

它的工作原理我们可以做个试验,在透明的人物前面放一个不透明的cube,用的是默认opaque的材质,但同样写入custom depth。
然后设置透明的人物材质,设定一个CloseDepthThreshold,当深度小于这个值的时候,显示白色,大于这个值的时候显示黑色。将其连接到Emissive color,并将Opacity设为1这样比较方便观察。

另外记得要禁用透明材质的深度测试,这样我们可以强制透明材质最后绘制的时候覆盖已有的custom depth buffer.否则因为遮挡关系,我们没法看到cube背后的部分。


因为关闭了translucent材质的深度测试,所以透明人物绘制在最顶层。可以看到,头部和手部因为比较靠近相机,所以绘制了出来。但本来较远的腹部也显示了出来,这是因为透明材质shader中使用的是cube的custom depth信息,所以深度小于显示的阈值。
也即说明,custom depth是一个buffer,较小的值会覆盖较大的值。

Pixel Depth

作为对比,如果将比较的对象设置为PixelDepth,则会有不一样的结果

可以看到到,透明人物采用的还是其自己的深度信息,也即是说因为关闭了depth test,所以它覆盖了本来的cube在pixel depth buffer中的信息。如果开启了depth test且将cube关闭render in main pass(防止不透明的物体挡住后面的人物),则会得到相同的结果。

Scene Depth

首先cube将自己的深度写入了scene dpeth buffer,而透明人物因为是translucent的缘故,zwrite关闭,所以depth buffer中完全没有人物的深度信息。因此只能绘制出被cube遮挡处的部分。
Tip:对于opaque的物体,它无法读取scene depth,所以只能直接用pixel depth。
对于post-processing,它不能使用pixel depth,使用scene depth和SceneTexture:SceneDepth基本等价,除了后者可能会因为抗锯齿而有抖动

总结

首先默认物体都打开了render to custom depth,透明物体也打开了allow custom depth write。
如果场景内只有一个物体,且物体本身没有自我交叠,则custom depth == pixel depth。若物体是opaque的,则scene depth未定义;若物体是translucent,则scene depth为原始状态
如果场景中只有一个物体,物体本身有交叠。若物体是opaque的,则custom depth == pixel depth;若是translucent的,则custom depth <= pixel depth,其中靠近摄像机的一面custom depth == pixel depth,背面的custom depth < pixel depth。scene depth情况同上。
如果场景中有两个物体,不透明的物体挡在另一个不透明物体前面。如果不透明物体没有写入custom depth,则custom depth <= pixel depth。
如果是不透明的物体挡在透明物体前面,因为后期特效中无法使用pixel depth,scene depth因为是透明物体,也没有写入,所以能比较的只有不透明物体,也就没有意义了。
还有其他很多中情况这里就不一一列举了。需要仔细思考情况再加以判断。

测试中,我们将整体透明度改为0.5,这样可以看到背面的部分

可以看到后侧的mesh因为pixel depth超过阈值,所以显示黑色,最终显示出来的是前半部分,深度较小的部分。这也可以说明,pixel depth可以采样到被遮挡的部分,并不是使用一个类似buffer的机制来存储的(可能是利用顶点插值的深度?)

避免重复绘制半透部分

有了前面的结论,我们首先对custom depth 和pixel depth进行比较。

如果这里depth bias为0,根据我们上面的结论,custom depth <= pixel depth,所以应该不透明度始终为0。结果也的确如此

因此,我们需要将pixel增加一个小的负的偏移值,即相当于将pixel depth朝相机方向移动一点。对于正面的部分来说,就是可以将其显示出来。而对于背面来说,因为移动了之后还是没法比正面更靠近相机(背面部分pixel depth > custom depth),所以不透明度依然为0。从而我们就达到了去掉重叠部分的目的。

可以看到有些分割处还是会有重叠的显现。这是因为他们的前后面的深度差太小,depth bias已经大于他们的深度差了,所以背面也显示出来了,即更亮了。
当这个bias大到一定程度之后,就是完全的重叠显现了

绘制半透勾边人物

上面一步操作可以让我们避免重复绘制mesh,我们回到一开始的地方,再次尝试绘制透明角色。
我们这里的描边是通过修改不透明度opacity来实现的(越边缘处,不透明度越高)

这时只想显示透明物体的轮廓时也不奏效了。因为我们用到了fresnel,虽然它只画了一次,但是因为背面没有被剔除,所以背面的转折处依旧被赋予了一个高opacity,从而显示了出来。(我们第一步做的操作只是让前后的mesh“不会”绘制两次,但是计算fresnel的时候,我们并没有办法剔除它们)因此,我们还要想办法把背面剔除
解决方法是我们需要用另一个不透明物体放置在同一个位置上,render custom depth且关闭render in main pass。这样不透明的物体就能即写入深度,从而让透明物体背面的部分被剔除。
最终的效果图如下

参考

[1]Custom Depth in Unreal Engine 4
[2]UE4 custom depth 自定义深度
[3]开发者遇到的典型美术相关问题回顾
[4]虚幻4中SceneDepth , PixelDepth ,Customdepth,CustomDepth StencilValue的区别
[5]两个重叠模型模拟双pass,顺便解决透明乱序问题
[6]Custom Depth and Custom Depth Stencil in UE4
[7]UE4材质中的PixelDepth和SceneDepth