Files
prosperon/shaders/msl/text_sdf.frag.msl
2026-02-25 16:58:06 -06:00

60 lines
2.1 KiB
Plaintext

#include <metal_stdlib>
using namespace metal;
struct VertexOut {
float4 position [[position]];
float2 uv;
float4 color;
};
struct Uniforms {
float outline_width; // Outline width in normalized SDF units (0.0 = no outline, 0.1-0.3 typical)
float sharpness; // Sharpness multiplier (1.0 = normal, higher = sharper)
float2 _pad; // Padding for alignment
float4 outline_color; // Outline color RGBA
};
fragment float4 fragment_main(VertexOut in [[stage_in]],
texture2d<float> tex [[texture(0)]],
sampler smp [[sampler(0)]],
constant Uniforms &u [[buffer(0)]]) {
// Sample distance from alpha channel (SDF fonts store distance in alpha)
float dist = tex.sample(smp, in.uv).a;
// Edge is at 0.5 (where distance = 0 in the original SDF)
float edge = 0.5;
// Calculate anti-aliasing width based on screen-space derivatives
// Sharpness multiplier controls how tight the AA band is
float aa = fwidth(dist);
float sharpness = max(u.sharpness, 0.1); // Prevent division issues
aa = aa / sharpness;
// Fill coverage: inside the glyph
float fill = smoothstep(edge - aa, edge + aa, dist);
// Outline coverage: extends outward from the edge
float outline_coverage = 0.0;
if (u.outline_width > 0.0) {
// Outline edge is further out (lower distance value = further from glyph center)
float outline_edge = edge - u.outline_width;
outline_coverage = smoothstep(outline_edge - aa, outline_edge + aa, dist);
}
// Total coverage is the union of fill and outline
float total_coverage = max(fill, outline_coverage);
// Blend colors: outline where not filled, fill color where filled
// fill goes from 0 (outside glyph) to 1 (inside glyph)
// outline_coverage goes from 0 (outside outline) to 1 (inside outline+glyph)
float3 rgb = in.color.rgb;
if (u.outline_width > 0.0) {
rgb = mix(u.outline_color.rgb, in.color.rgb, fill);
}
// Final alpha combines coverage with vertex alpha
float a = total_coverage * in.color.a;
return float4(rgb, a);
}