60 lines
2.1 KiB
Plaintext
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);
|
|
}
|