unity-shader-SignedDistanceField(SDF)

SignedDistanceField 常与 RayMarching ( 参考总结: unity-shader-光线步进RayMarching.md ) 联系在一起.


前篇

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
    66
    Shader "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
    #pragma vertex vert
    #pragma fragment frag

    #include "UnityCG.cginc"
    // #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范围内) 到 某些像素 的世界空间下的坐标