pub const MSDF_FRAGMENT_SOURCE: &str = "// MSDF (Multi-channel Signed Distance Field) text fragment shader.\n//\n// This is a standalone fragment shader intended for use with the custom shader system\n// (ShaderStore). The standard preamble (camera, texture, lighting, vertex shader) and\n// ShaderParams uniform are prepended automatically by ShaderStore::create().\n//\n// The fragment shader computes median(R, G, B) from the MSDF atlas, then uses\n// smoothstep for crisp anti-aliased edges at any scale.\n//\n// Uniform slots (shader_params.values[]):\n// [0]: [distance_range, font_size_px, screen_px_range, _pad]\n// [1]: outline [width, r, g, b]\n// [2]: outline [a, _, _, _]\n// [3]: shadow [offset_x, offset_y, softness, _]\n// [4]: shadow [r, g, b, a]\n//\n// Supports:\n// - Resolution-independent text rendering\n// - Configurable outline (width + color)\n// - Configurable shadow (offset + color + softness)\n\n/// Compute median of three values (the core MSDF operation).\nfn median3(r: f32, g: f32, b: f32) -> f32 {\n return max(min(r, g), min(max(r, g), b));\n}\n\n/// Sample the MSDF atlas and return the screen-space distance.\nfn msdf_sample_distance(uv: vec2<f32>, screen_px_range: f32) -> f32 {\n let msd = textureSample(t_diffuse, s_diffuse, uv);\n let sd = median3(msd.r, msd.g, msd.b);\n return (sd - 0.5) * screen_px_range;\n}\n\n@fragment\nfn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {\n let screen_px_range = shader_params.values[0].z;\n\n // --- Shadow pass ---\n let shadow_offset = shader_params.values[3].xy;\n let shadow_softness = shader_params.values[3].z;\n let shadow_color = shader_params.values[4];\n var shadow_alpha = 0.0;\n\n if (shadow_color.a > 0.0) {\n // Compute texel size from atlas dimensions for shadow offset\n let tex_size = vec2<f32>(textureDimensions(t_diffuse, 0));\n let shadow_uv = in.tex_coords + shadow_offset / tex_size;\n let shadow_dist = msdf_sample_distance(shadow_uv, screen_px_range);\n let softness_factor = max(shadow_softness, 1.0);\n shadow_alpha = smoothstep(-softness_factor, softness_factor, shadow_dist) * shadow_color.a;\n }\n\n // --- Outline pass ---\n let outline_width = shader_params.values[1].x;\n let outline_rgb = shader_params.values[1].yzw;\n let outline_a = shader_params.values[2].x;\n\n let dist = msdf_sample_distance(in.tex_coords, screen_px_range);\n\n var outline_alpha = 0.0;\n if (outline_width > 0.0 && outline_a > 0.0) {\n // Outline extends outward from the glyph edge\n outline_alpha = smoothstep(-outline_width - 0.5, -outline_width + 0.5, dist) * outline_a;\n }\n\n // --- Fill pass ---\n let fill_alpha = smoothstep(-0.5, 0.5, dist);\n\n // Composite: shadow behind outline behind fill\n var color = vec4<f32>(0.0);\n\n // Shadow layer\n if (shadow_alpha > 0.0) {\n color = vec4<f32>(shadow_color.rgb, shadow_alpha);\n }\n\n // Outline layer (over shadow)\n if (outline_alpha > 0.0) {\n let oa = outline_alpha * (1.0 - fill_alpha); // Only show outline where fill isn\'t\n color = vec4<f32>(\n mix(color.rgb, outline_rgb, oa),\n max(color.a, oa),\n );\n }\n\n // Fill layer (over outline and shadow)\n let fill_color = in.tint;\n if (fill_alpha > 0.0) {\n let fa = fill_alpha * fill_color.a;\n color = vec4<f32>(\n mix(color.rgb, fill_color.rgb, fa),\n max(color.a, fa),\n );\n }\n\n // Apply lighting\n var light_color = lighting.ambient;\n for (var i = 0u; i < lighting.light_count; i = i + 1u) {\n let light = lighting.lights[i];\n let light_pos = light.pos_radius.xy;\n let radius = light.pos_radius.z;\n let lcolor = light.color_intensity.rgb;\n let intensity = light.color_intensity.a;\n\n let d = length(in.world_position - light_pos);\n let atten = smoothstep(radius, 0.0, d) * intensity;\n light_color = light_color + lcolor * atten;\n }\n light_color = clamp(light_color, vec3<f32>(0.0), vec3<f32>(1.0));\n\n return vec4<f32>(color.rgb * light_color, color.a);\n}\n";Expand description
The MSDF fragment shader source (used with ShaderStore::create). This is the fragment portion only; the standard vertex preamble and ShaderParams uniform are prepended by the ShaderStore automatically.