unity-shader-SignedDistanceField(SDF)
SignedDistanceField 常与 RayMarching ( 参考总结: unity-shader-光线步进RayMarching.md ) 联系在一起.
前篇
- Signed Distance Field - https://zhuanlan.zhihu.com/p/26217154
- unity画个多边形如何用shader抗锯齿? - https://www.zhihu.com/question/267382412
- Unity Anti-aliasing shader (SDF) - https://www.jianshu.com/p/2171db34ce58
- Volumetric Rendering - https://www.alanzucconi.com/2016/07/01/volumetric-rendering/
Signed Distance Field : Signed,正负号,Distance,点到点的距离,Field,区域,其实就是 判断一个点是否在一个区域内
抗锯齿方面的应用
原理
利用的是 uv 值做的 SDF,多边形中心点,uv值为(0,0),边上的点uv值为(1,0)。这时候,从中心到边缘,uv的x值边缘为1,非边缘在0~1之间。这时候只要利用 uv.x在x和y方向上的偏导数来取出 几个像素做下边缘 alpha 模糊即可。
应为利用了 alpha 做混合, 所以渲染队列必须在 Transparent
参考测试工程中的 SignedDistanceField02.shader
模型 uv 分布
所以可以考虑用多一套 uv 去存储这个值, 这里我直接用第一套
效果
左下角的为 unity 内置的 quad, 正常的 方形uv分布.
shader 代码
需要注意的是 fwidth 是 dx11 的函数, 可以用
abs(ddx(x)) + abs(ddy(x))
适用到其它平台1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66Shader "test/sdf/SignedDistanceField02"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_Width ("Width", Range(0.0001, 15)) = 5
}
SubShader
{
Tags {
"Queue"="Transparent"
"RenderType"="Transparent"
}
ZWrite Off
Blend SrcAlpha OneMinusSrcAlpha
Pass
{
CGPROGRAM
// #define fwidth(x) (abs(ddx(x)) + abs(ddy(x))) // fwidth 是 dx11 的函数, 可以这样定义才能使用到其它平台
struct appdata
{
float4 vertex : POSITION;
float2 texcoord : TEXCOORD0;
// float2 texcoord2 : TEXCOORD1; 可以考虑用多一套uv
};
struct v2f
{
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
};
sampler2D _MainTex;
float4 _MainTex_ST;
float _Width;
v2f vert (appdata v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
return o;
}
fixed4 frag (v2f IN) : COLOR {
float smoothValue = 0;
//fwidth 必须dx11. 因为是UI,可以用CPU算好一个像素的_Delta,用uniform传给GPU;
float delta = _Width * fwidth(IN.uv.x);//fwidth(IN.uv.x);
float v = IN.uv.x + delta;
//if(v >= 1.0f){
// smoothValue = (v -1) / delta;
//}
smoothValue = step(1.0, v) * (v -1) / delta;
float a = smoothstep(1, 0.0f, smoothValue);
return float4(1, 1, 1, a);
}
ENDCG
}
}
}
RayMarching 方面的应用
每一次步进后判断一下是否碰撞 (sdf范围内) 到 某些像素 的世界空间下的坐标