Files
2025-06-09 23:23:13 +08:00

257 lines
7.4 KiB
Plaintext

Shader "Hidden/MicroVerse/JumpFloodSDF"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_Zoom ("Zoom", Float) = 1
}
SubShader
{
Tags { "PreviewType" = "Plane" }
Cull Off ZWrite Off ZTest Always
CGINCLUDE
#define FLOOD_NULL_POS -1.0
#define FLOOD_NULL_POS_FLOAT2 float2(FLOOD_NULL_POS, FLOOD_NULL_POS)
ENDCG
Pass // 0
{
Name "JUMPFLOODINIT"
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include_with_pragmas "UnityCG.cginc"
#pragma target 4.5
int _Channel;
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
};
sampler2D _MainTex;
float4 _MainTex_TexelSize;
v2f vert (appdata v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = v.uv;
return o;
}
float2 frag (v2f i) : SV_Target {
// sample silhouette texture for sobel
half3x3 values;
UNITY_UNROLL
for(int u=0; u<3; u++)
{
UNITY_UNROLL
for(int v=0; v<3; v++)
{
float2 sampleUV = clamp(i.uv + _MainTex_TexelSize.xy * float2(u-1, v-1), float2(0,0), float2(1,1));
values[u][v] = tex2D(_MainTex, sampleUV)[_Channel];
}
}
// calculate output position for this pixel
float2 outPos = i.uv;
// interior, return position
if (values._m11 > 0.99)
return outPos;
// exterior, return no position
if (values._m11 < 0.01)
return FLOOD_NULL_POS_FLOAT2;
// sobel to estimate edge direction
float2 dir = -float2(
values[0][0] + values[0][1] * 2.0 + values[0][2] - values[2][0] - values[2][1] * 2.0 - values[2][2],
values[0][0] + values[1][0] * 2.0 + values[2][0] - values[0][2] - values[1][2] * 2.0 - values[2][2]
);
// if dir length is small, this is either a sub pixel dot or line
// no way to estimate sub pixel edge, so output position
if (abs(dir.x) <= 0.005 && abs(dir.y) <= 0.005)
return outPos;
// normalize direction
dir = normalize(dir);
// sub pixel offset
float2 offset = dir * (1.0 - values._m11);
// output encoded offset position
return (i.uv.xy + offset * _MainTex_TexelSize.xy);
}
ENDCG
}
Pass // 1
{
Name "JUMPFLOOD"
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include_with_pragmas "UnityCG.cginc"
#pragma target 4.5
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
};
sampler _MainTex;
float4 _MainTex_TexelSize;
int _StepWidth;
v2f vert (appdata v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = v.uv;
return o;
}
float2 frag (v2f i) : SV_Target
{
// initialize best distance at infinity
float bestDist = 1.#INF;
float2 bestCoord;
// jump samples
UNITY_UNROLL
for(int u=-1; u<=1; u++)
{
UNITY_UNROLL
for(int v=-1; v<=1; v++)
{
// calculate offset sample position
float2 offsetUV = i.uv + int2(u, v) * _MainTex_TexelSize.xy * _StepWidth;
// .Load() acts funny when sampling outside of bounds, so don't
offsetUV = clamp(offsetUV, 0, 1);
// decode position from buffer
float2 offsetPos = tex2D(_MainTex, offsetUV).rg * _MainTex_TexelSize.zw;
// the offset from current position
float2 disp = i.uv * _MainTex_TexelSize.zw - offsetPos;
// square distance
float dist = dot(disp, disp);
// if offset position isn't a null position or is closer than the best
// set as the new best and store the position
if (offsetPos.y != FLOOD_NULL_POS && dist < bestDist)
{
bestDist = dist;
bestCoord = offsetPos;
}
}
}
// if not valid best distance output null position, otherwise output encoded position
return isinf(bestDist) ? FLOOD_NULL_POS_FLOAT2 : bestCoord * _MainTex_TexelSize.xy;
}
ENDCG
}
// technically optional, as you can just decode distance in the shader and not do this pass.
Pass // 2
{
Name "JUMPFLOODSDF"
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include_with_pragmas "UnityCG.cginc"
#pragma target 4.5
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
};
Texture2D _MainTex;
float4 _MainTex_TexelSize;
float _Zoom;
v2f vert (appdata v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = v.uv;
return o;
}
float frag (v2f i) : SV_Target
{
float fac = (1 + (_Zoom-1)*2);
i.uv /= fac;
i.uv += (1.0 / fac) * (_Zoom-1);
// integer pixel position
int2 uvInt = i.uv * _MainTex_TexelSize.zw;
// load encoded position
float2 encodedPos = _MainTex.Load(int3(uvInt, 0)).rg;
// early out if null position
if (encodedPos.y == -1)
return half4(99999,99999,99999,99999);
// decode closest position
float2 nearestPos = encodedPos * _MainTex_TexelSize.zw;
// current pixel position
float2 currentPos = i.uv.xy * _MainTex_TexelSize.zw;
// distance in pixels to closest position
half dist = distance(nearestPos, currentPos);
return dist / 256;
}
ENDCG
}
}
}