分享下最近通过论文研究基于SDF的Anti-aliasing。
-
什么是SDF?
SDF=signed distance field,其实就是判断一个点是否在区域内。 (通过signed distance function)
Signed Distance Field=distance(target,targetNearestForeground)-distance(target,targetNearestBackground)
SDF只做介绍,详细可以Google。
shader SDFAnti-aliasing原理(参考于https://www.zhihu.com/question/267382412/answer/338371372):
- 利用的是 uv值做的SDF,多边形中心点,uv值为(0,0),边上的点uv值为(1,0)。这时候,从中心到边缘,uv的x值边缘为1,非边缘在0~1之间。这时候只要利用 uv.x在x和y方向上的偏导数来取出 几个像素做下边缘alpha模糊即可。
首先在构建mesh uv的时候,uv是从本身中心点(0,0)到各边缘(1,0)。(也就是此shader的适用范围,需要使用到可以让美术,或者自己修改uv值)
如下图:
shader 实现:Alpha Blend+Smoothstep实现半透明渐变
- 计算当前像素点在x,y方向上的偏导值,其值等于临近像素上的变化率,因为纹理坐标的梯度可以用来确定纹理当前被缩放的程度,作为需要渐变像素的offset(DX11 上,fwidth())。
原理:
- ddx=p(x+1,y)-p(x,y) p点像素与它x方向上相邻像素颜色的差。
- ddy=p(x+1,y)-p(x,y) p点像素与它y方向上相邻像素颜色的差。
- delta=|ddx|+|ddy|
由于uv coordinate [0,1] rgba 也是[0,1]。 则所求的delta是以uv coordinate内与目标有颜色差异的单个像素的宽度(从颜色到uv的mapping过程)。 而_Delta则是需要参与计算的像素个数(uv变化是线性的,所以每个像素之间的差异都是一样的)。
简单的偏移计算 : float u = i.uv.x+delta
y=x+a;
x=y-a;
i.uv.x=u-delta (在i.uv.x位置时,距离u点的delta距离内的范围)
(也就是当u为1时,i.uv.x等于为1-delta)
所求范围的像素,需要进行接下来的alpha 渐变。
接着通过smoothstep实现非线性的渐变递增。(alpha渐变)
- 从uv coordinate 的[1-delta,1]区间向[0,1]区间的线性映射(用于插值计算)。
原理:线性插值
“(u-1)/delta ” 线性映射(方便插值运算,插值一般都是0-1):
目的为了将由上图的 [1-delta,1]的区间映射到[0,1]的区间用于插值运算。
公式变换:
则:
由于:u=i.uv.x+delta则:
线性映射为:(u-1)/delta
通过 (u-1)/delta 完成线性映射:
最后进行smoothstep插值计算(非线性的渐变):
- smoothstep 函数将用于在一段时间范围内逐渐但非线性地增加属性,例如,“不透明度”(Opacity)从 0 增加到 1。
- a=smoothstep(a,0.0f,smoothValue)
-
由于我们是向0插值,则函数曲线大概是这样(随便画的):
image.png
Smoothstep函数原型:
最终效果:
-
抗锯齿前:
image.png -
抗锯齿后:
image.png
相关文献地址:
An introduction to shader derivative functions(fwidth()):
http://www.aclockworkberry.com/shader-derivative-functions/
signed distance function:
https://en.wikipedia.org/wiki/Signed_distance_function
smoothstep:
https://en.wikipedia.org/wiki/Smoothstep