#pragma once #include #include #include namespace mbgl { namespace shaders { constexpr auto symbolShaderPrelude = R"( // Symbol shader UBO indices const idGlobalPaintParamsUBO = 0u; const idGlobalUBOIndex = 1u; const idSymbolDrawableUBO = 2u; const idSymbolTilePropsUBO = 3u; const idSymbolEvaluatedPropsUBO = 4u; const symbolUBOCount = 5u; struct GlobalPaintParamsUBO { pattern_atlas_texsize: vec2, units_to_pixels: vec2, world_size: vec2, camera_to_center_distance: f32, symbol_fade_change: f32, aspect_ratio: f32, pixel_ratio: f32, map_zoom: f32, pad1: f32, }; struct SymbolDrawableUBO { matrix: mat4x4, label_plane_matrix: mat4x4, coord_matrix: mat4x4, texsize: vec2, texsize_icon: vec2, is_text_prop: u32, rotate_symbol: u32, pitch_with_map: u32, is_size_zoom_constant: u32, is_size_feature_constant: u32, is_offset: u32, size_t: f32, size: f32, // Interpolations fill_color_t: f32, halo_color_t: f32, opacity_t: f32, halo_width_t: f32, halo_blur_t: f32, }; struct SymbolTilePropsUBO { is_text: u32, is_halo: u32, gamma_scale: f32, pad1: f32, }; struct SymbolEvaluatedPropsUBO { text_fill_color: vec4, text_halo_color: vec4, text_opacity: f32, text_halo_width: f32, text_halo_blur: f32, pad1: f32, icon_fill_color: vec4, icon_halo_color: vec4, icon_opacity: f32, icon_halo_width: f32, icon_halo_blur: f32, pad2: f32, }; struct GlobalIndexUBO { value: u32, pad0: vec3, }; const c_offscreen_degenerate_triangle_location: f32 = -2.0; )"; template <> struct ShaderSource { static constexpr const char* name = "SymbolIconShader"; static const std::array attributes; static constexpr std::array instanceAttributes{}; static const std::array textures; static constexpr auto prelude = symbolShaderPrelude; static constexpr auto vertex = R"()" R"( struct VertexInput { @location(3) pos_offset: vec4, @location(4) data: vec4, @location(5) pixeloffset: vec4, @location(6) projected_pos: vec3, @location(7) fade_opacity: f32, #ifndef HAS_UNIFORM_u_opacity @location(8) opacity: f32, #endif }; struct VertexOutput { @builtin(position) position: vec4, @location(0) tex: vec2, @location(1) fade_opacity: f32, #ifndef HAS_UNIFORM_u_opacity @location(2) opacity: f32, #endif }; @group(0) @binding(0) var paintParams: GlobalPaintParamsUBO; @group(0) @binding(1) var globalIndex: GlobalIndexUBO; @group(0) @binding(2) var drawableVector: array; @vertex fn main(in: VertexInput) -> VertexOutput { var out: VertexOutput; let drawable = drawableVector[globalIndex.value]; let raw_fade_opacity = unpack_opacity(in.fade_opacity); let fade_change = select(-paintParams.symbol_fade_change, paintParams.symbol_fade_change, raw_fade_opacity.y > 0.5); let fade_opacity = max(0.0, min(1.0, raw_fade_opacity.x + fade_change)); #ifdef HAS_UNIFORM_u_opacity let final_opacity = fade_opacity; #else let paint_opacity = unpack_mix_float(vec2(in.opacity, in.opacity), drawable.opacity_t); let final_opacity = paint_opacity * fade_opacity; #endif // Cull vertices with zero opacity if (final_opacity == 0.0) { out.position = vec4(c_offscreen_degenerate_triangle_location, c_offscreen_degenerate_triangle_location, c_offscreen_degenerate_triangle_location, 1.0); return out; } let a_pos = vec2(f32(in.pos_offset.x), f32(in.pos_offset.y)); let a_offset = vec2(f32(in.pos_offset.z), f32(in.pos_offset.w)); let a_tex = vec2(f32(in.data.x), f32(in.data.y)); let a_size = vec2(f32(in.data.z), f32(in.data.w)); let a_size_min = floor(a_size.x * 0.5); let a_pxoffset = vec2(f32(in.pixeloffset.x), f32(in.pixeloffset.y)); let a_minFontScale = vec2(f32(in.pixeloffset.z), f32(in.pixeloffset.w)) / 256.0; let segment_angle = -in.projected_pos.z; let size_zoom_constant = drawable.is_size_zoom_constant != 0u; let size_feature_constant = drawable.is_size_feature_constant != 0u; var size: f32; if (!size_zoom_constant && !size_feature_constant) { size = mix(a_size_min, a_size.y, drawable.size_t) / 128.0; } else if (size_zoom_constant && !size_feature_constant) { size = a_size_min / 128.0; } else { size = drawable.size; } let projectedPoint = drawable.matrix * vec4(a_pos, 0.0, 1.0); let camera_to_anchor_distance = projectedPoint.w; let pitch_with_map = drawable.pitch_with_map != 0u; let distance_ratio = select( paintParams.camera_to_center_distance / camera_to_anchor_distance, camera_to_anchor_distance / paintParams.camera_to_center_distance, pitch_with_map ); let perspective_ratio = clamp(0.5 + 0.5 * distance_ratio, 0.0, 4.0); if (drawable.is_offset == 0u) { size *= perspective_ratio; } let is_text = drawable.is_text_prop != 0u; let fontScale = select(size, size / 24.0, is_text); var symbol_rotation = 0.0; if (drawable.rotate_symbol != 0u) { let offsetProjectedPoint = drawable.matrix * vec4(a_pos + vec2(1.0, 0.0), 0.0, 1.0); let a = projectedPoint.xy / projectedPoint.w; let b = offsetProjectedPoint.xy / offsetProjectedPoint.w; symbol_rotation = atan2((b.y - a.y) / paintParams.aspect_ratio, b.x - a.x); } let angle_sin = sin(segment_angle + symbol_rotation); let angle_cos = cos(segment_angle + symbol_rotation); let rotation_matrix = mat2x2(angle_cos, -angle_sin, angle_sin, angle_cos); let projected_pos = drawable.label_plane_matrix * vec4(in.projected_pos.xy, 0.0, 1.0); let pos0 = projected_pos.xy / projected_pos.w; let posOffset = a_offset * max(a_minFontScale, vec2(fontScale)) / 32.0 + a_pxoffset / 16.0; let position = drawable.coord_matrix * vec4(pos0 + rotation_matrix * posOffset, 0.0, 1.0); out.position = position; out.tex = a_tex / drawable.texsize; out.fade_opacity = fade_opacity; #ifndef HAS_UNIFORM_u_opacity out.opacity = final_opacity; #endif return out; } )"; static constexpr auto fragment = R"( struct FragmentInput { @location(0) tex: vec2, @location(1) fade_opacity: f32, #ifndef HAS_UNIFORM_u_opacity @location(2) opacity: f32, #endif }; @group(0) @binding(3) var tilePropsVector: array; @group(0) @binding(1) var globalIndex: GlobalIndexUBO; @group(0) @binding(4) var props: SymbolEvaluatedPropsUBO; @group(1) @binding(0) var texture_sampler: sampler; @group(1) @binding(1) var image: texture_2d; @fragment fn main(in: FragmentInput) -> @location(0) vec4 { let tileProps = tilePropsVector[globalIndex.value]; #ifdef HAS_UNIFORM_u_opacity let base_opacity = select(props.icon_opacity, props.text_opacity, tileProps.is_text != 0u); let opacity = base_opacity * in.fade_opacity; #else let opacity = select(props.icon_opacity, props.text_opacity, tileProps.is_text != 0u) * in.opacity; #endif return textureSample(image, texture_sampler, in.tex) * opacity; } )"; }; template <> struct ShaderSource { static constexpr const char* name = "SymbolSDFShader"; static const std::array attributes; static constexpr std::array instanceAttributes{}; static const std::array textures; static constexpr auto prelude = symbolShaderPrelude; static constexpr auto vertex = R"()" R"( struct VertexInput { @location(3) pos_offset: vec4, @location(4) data: vec4, @location(5) pixeloffset: vec4, @location(6) projected_pos: vec3, @location(7) fade_opacity: f32, #ifndef HAS_UNIFORM_u_fill_color @location(8) fill_color: vec4, #endif #ifndef HAS_UNIFORM_u_halo_color @location(9) halo_color: vec4, #endif #ifndef HAS_UNIFORM_u_opacity @location(10) opacity: f32, #endif #ifndef HAS_UNIFORM_u_halo_width @location(11) halo_width: f32, #endif #ifndef HAS_UNIFORM_u_halo_blur @location(12) halo_blur: f32, #endif }; struct VertexOutput { @builtin(position) position: vec4, @location(0) tex: vec2, @location(1) fade_opacity: f32, @location(2) gamma_scale: f32, @location(3) fontScale: f32, #ifndef HAS_UNIFORM_u_fill_color @location(4) fill_color: vec4, #endif #ifndef HAS_UNIFORM_u_halo_color @location(5) halo_color: vec4, #endif #ifndef HAS_UNIFORM_u_opacity @location(6) opacity: f32, #endif #ifndef HAS_UNIFORM_u_halo_width @location(7) halo_width: f32, #endif #ifndef HAS_UNIFORM_u_halo_blur @location(8) halo_blur: f32, #endif }; @group(0) @binding(0) var paintParams: GlobalPaintParamsUBO; @group(0) @binding(1) var globalIndex: GlobalIndexUBO; @group(0) @binding(2) var drawableVector: array; @group(0) @binding(4) var props: SymbolEvaluatedPropsUBO; @vertex fn main(in: VertexInput) -> VertexOutput { var out: VertexOutput; let drawable = drawableVector[globalIndex.value]; let fade_opacity = unpack_opacity(in.fade_opacity); let fade_change = select(-paintParams.symbol_fade_change, paintParams.symbol_fade_change, fade_opacity.y > 0.5); let fo = max(0.0, min(1.0, fade_opacity.x + fade_change)); // Cull vertices with zero opacity if (fo == 0.0) { out.position = vec4(c_offscreen_degenerate_triangle_location, c_offscreen_degenerate_triangle_location, c_offscreen_degenerate_triangle_location, 1.0); return out; } let a_pos = vec2(f32(in.pos_offset.x), f32(in.pos_offset.y)); let a_offset = vec2(f32(in.pos_offset.z), f32(in.pos_offset.w)); let a_tex = vec2(f32(in.data.x), f32(in.data.y)); let a_size = vec2(f32(in.data.z), f32(in.data.w)); let a_size_min = floor(a_size.x * 0.5); let a_pxoffset = vec2(f32(in.pixeloffset.x), f32(in.pixeloffset.y)); let segment_angle = -in.projected_pos.z; var size: f32; let size_zoom_constant = drawable.is_size_zoom_constant != 0u; let size_feature_constant = drawable.is_size_feature_constant != 0u; if (!size_zoom_constant && !size_feature_constant) { size = mix(a_size_min, a_size.y, drawable.size_t) / 128.0; } else if (size_zoom_constant && !size_feature_constant) { size = a_size_min / 128.0; } else { size = drawable.size; } let projectedPoint = drawable.matrix * vec4(a_pos, 0.0, 1.0); let camera_to_anchor_distance = projectedPoint.w; let pitch_with_map = drawable.pitch_with_map != 0u; let distance_ratio = select( paintParams.camera_to_center_distance / camera_to_anchor_distance, camera_to_anchor_distance / paintParams.camera_to_center_distance, pitch_with_map ); let perspective_ratio = clamp(0.5 + 0.5 * distance_ratio, 0.0, 4.0); if (drawable.is_offset == 0u) { size *= perspective_ratio; } let is_text = drawable.is_text_prop != 0u; let fontScale = select(size, size / 24.0, is_text); var symbol_rotation = 0.0; if (drawable.rotate_symbol != 0u) { let offsetProjectedPoint = drawable.matrix * vec4(a_pos + vec2(1.0, 0.0), 0.0, 1.0); let a = projectedPoint.xy / projectedPoint.w; let b = offsetProjectedPoint.xy / offsetProjectedPoint.w; symbol_rotation = atan2((b.y - a.y) / paintParams.aspect_ratio, b.x - a.x); } let angle_sin = sin(segment_angle + symbol_rotation); let angle_cos = cos(segment_angle + symbol_rotation); let rotation_matrix = mat2x2(angle_cos, -angle_sin, angle_sin, angle_cos); let projected_pos = drawable.label_plane_matrix * vec4(in.projected_pos.xy, 0.0, 1.0); let pos_rot = a_offset / 32.0 * fontScale + a_pxoffset; let pos0 = projected_pos.xy / projected_pos.w + rotation_matrix * pos_rot; let position = drawable.coord_matrix * vec4(pos0, 0.0, 1.0); out.position = position; out.tex = a_tex / drawable.texsize; out.gamma_scale = position.w; out.fontScale = fontScale; out.fade_opacity = fo; #ifndef HAS_UNIFORM_u_fill_color out.fill_color = unpack_mix_color(in.fill_color, drawable.fill_color_t); #endif #ifndef HAS_UNIFORM_u_halo_color out.halo_color = unpack_mix_color(in.halo_color, drawable.halo_color_t); #endif #ifndef HAS_UNIFORM_u_halo_width out.halo_width = unpack_mix_float(vec2(in.halo_width, in.halo_width), drawable.halo_width_t); #endif #ifndef HAS_UNIFORM_u_halo_blur out.halo_blur = unpack_mix_float(vec2(in.halo_blur, in.halo_blur), drawable.halo_blur_t); #endif #ifndef HAS_UNIFORM_u_opacity out.opacity = unpack_mix_float(vec2(in.opacity, in.opacity), drawable.opacity_t); #endif return out; } )"; static constexpr auto fragment = R"( struct FragmentInput { @location(0) tex: vec2, @location(1) fade_opacity: f32, @location(2) gamma_scale: f32, @location(3) fontScale: f32, #ifndef HAS_UNIFORM_u_fill_color @location(4) fill_color: vec4, #endif #ifndef HAS_UNIFORM_u_halo_color @location(5) halo_color: vec4, #endif #ifndef HAS_UNIFORM_u_opacity @location(6) opacity: f32, #endif #ifndef HAS_UNIFORM_u_halo_width @location(7) halo_width: f32, #endif #ifndef HAS_UNIFORM_u_halo_blur @location(8) halo_blur: f32, #endif }; @group(0) @binding(0) var paintParams: GlobalPaintParamsUBO; @group(0) @binding(3) var tilePropsVector: array; @group(0) @binding(1) var globalIndex: GlobalIndexUBO; @group(0) @binding(4) var props: SymbolEvaluatedPropsUBO; @group(1) @binding(0) var texture_sampler: sampler; @group(1) @binding(1) var glyph_atlas: texture_2d; @fragment fn main(in: FragmentInput) -> @location(0) vec4 { let tileProps = tilePropsVector[globalIndex.value]; #ifndef HAS_UNIFORM_u_fill_color let fill_color = in.fill_color; #else let fill_color = select(props.icon_fill_color, props.text_fill_color, tileProps.is_text != 0u); #endif #ifndef HAS_UNIFORM_u_halo_color let halo_color = in.halo_color; #else let halo_color = select(props.icon_halo_color, props.text_halo_color, tileProps.is_text != 0u); #endif #ifndef HAS_UNIFORM_u_opacity let opacity = in.opacity; #else let opacity = select(props.icon_opacity, props.text_opacity, tileProps.is_text != 0u); #endif #ifndef HAS_UNIFORM_u_halo_width let halo_width = in.halo_width; #else let halo_width = select(props.icon_halo_width, props.text_halo_width, tileProps.is_text != 0u); #endif #ifndef HAS_UNIFORM_u_halo_blur let halo_blur = in.halo_blur; #else let halo_blur = select(props.icon_halo_blur, props.text_halo_blur, tileProps.is_text != 0u); #endif let EDGE_GAMMA = 0.105 / paintParams.pixel_ratio; let fontGamma = in.fontScale * tileProps.gamma_scale; let color = select(fill_color, halo_color, tileProps.is_halo != 0u); let gamma = (select(0.0, halo_blur * 1.19 / SDF_PX, tileProps.is_halo != 0u) + EDGE_GAMMA) / fontGamma; let gamma_scaled = gamma * in.gamma_scale; // For halos, offset the inner edge outward to create the ring var inner_edge = (256.0 - 64.0) / 256.0; if (tileProps.is_halo != 0u) { inner_edge = inner_edge + gamma_scaled; } let sample = textureSample(glyph_atlas, texture_sampler, in.tex); let dist = select(sample.a, sample.r, tileProps.is_text != 0u); var alpha = smoothstep(inner_edge - gamma_scaled, inner_edge + gamma_scaled, dist); // When drawing halos, make the inside transparent so the fill can show through if (tileProps.is_halo != 0u) { let halo_edge = (6.0 - halo_width / in.fontScale) / SDF_PX; alpha = min(smoothstep(halo_edge - gamma_scaled, halo_edge + gamma_scaled, dist), 1.0 - alpha); } let coverage = alpha * opacity * in.fade_opacity; let outAlpha = color.a * coverage; return vec4(color.rgb * coverage, outAlpha); } )"; }; template <> struct ShaderSource { static constexpr const char* name = "SymbolTextAndIconShader"; static const std::array attributes; static constexpr std::array instanceAttributes{}; static const std::array textures; static constexpr auto prelude = symbolShaderPrelude; static constexpr auto vertex = R"()" R"( struct VertexInput { @location(3) pos_offset: vec4, @location(4) data: vec4, @location(5) projected_pos: vec3, @location(6) fade_opacity: f32, #ifndef HAS_UNIFORM_u_fill_color @location(7) fill_color: vec4, #endif #ifndef HAS_UNIFORM_u_halo_color @location(8) halo_color: vec4, #endif #ifndef HAS_UNIFORM_u_opacity @location(9) opacity: f32, #endif #ifndef HAS_UNIFORM_u_halo_width @location(10) halo_width: f32, #endif #ifndef HAS_UNIFORM_u_halo_blur @location(11) halo_blur: f32, #endif }; struct VertexOutput { @builtin(position) position: vec4, @location(0) tex: vec2, @location(1) fade_opacity: f32, @location(2) gamma_scale: f32, @location(3) fontScale: f32, @location(4) is_icon: f32, #ifndef HAS_UNIFORM_u_fill_color @location(5) fill_color: vec4, #endif #ifndef HAS_UNIFORM_u_halo_color @location(6) halo_color: vec4, #endif #ifndef HAS_UNIFORM_u_opacity @location(7) opacity: f32, #endif #ifndef HAS_UNIFORM_u_halo_width @location(8) halo_width: f32, #endif #ifndef HAS_UNIFORM_u_halo_blur @location(9) halo_blur: f32, #endif }; @group(0) @binding(0) var paintParams: GlobalPaintParamsUBO; @group(0) @binding(1) var globalIndex: GlobalIndexUBO; @group(0) @binding(2) var drawableVector: array; const SDF = 1.0; const ICON = 0.0; @vertex fn main(in: VertexInput) -> VertexOutput { var out: VertexOutput; let drawable = drawableVector[globalIndex.value]; let fade_opacity = unpack_opacity(in.fade_opacity); let fade_change = select(-paintParams.symbol_fade_change, paintParams.symbol_fade_change, fade_opacity.y > 0.5); let fo = max(0.0, min(1.0, fade_opacity.x + fade_change)); // Cull vertices with zero opacity if (fo == 0.0) { out.position = vec4(c_offscreen_degenerate_triangle_location, c_offscreen_degenerate_triangle_location, c_offscreen_degenerate_triangle_location, 1.0); return out; } let a_pos = vec2(f32(in.pos_offset.x), f32(in.pos_offset.y)); let a_offset = vec2(f32(in.pos_offset.z), f32(in.pos_offset.w)); let a_tex = vec2(f32(in.data.x), f32(in.data.y)); let a_size = vec2(f32(in.data.z), f32(in.data.w)); let a_size_min = floor(a_size.x * 0.5); let is_sdf = a_size.x - 2.0 * a_size_min; let segment_angle = -in.projected_pos.z; let size_zoom_constant = drawable.is_size_zoom_constant != 0u; let size_feature_constant = drawable.is_size_feature_constant != 0u; var size: f32; if (!size_zoom_constant && !size_feature_constant) { size = mix(a_size_min, a_size.y, drawable.size_t) / 128.0; } else if (size_zoom_constant && !size_feature_constant) { size = a_size_min / 128.0; } else { size = drawable.size; } let projectedPoint = drawable.matrix * vec4(a_pos, 0.0, 1.0); let camera_to_anchor_distance = projectedPoint.w; let pitch_with_map = drawable.pitch_with_map != 0u; let distance_ratio = select( paintParams.camera_to_center_distance / camera_to_anchor_distance, camera_to_anchor_distance / paintParams.camera_to_center_distance, pitch_with_map ); let perspective_ratio = clamp(0.5 + 0.5 * distance_ratio, 0.0, 4.0); if (drawable.is_offset == 0u) { size *= perspective_ratio; } let fontScale = size / 24.0; var symbol_rotation = 0.0; if (drawable.rotate_symbol != 0u) { let offsetProjectedPoint = drawable.matrix * vec4(a_pos + vec2(1.0, 0.0), 0.0, 1.0); let a = projectedPoint.xy / projectedPoint.w; let b = offsetProjectedPoint.xy / offsetProjectedPoint.w; symbol_rotation = atan2((b.y - a.y) / paintParams.aspect_ratio, b.x - a.x); } let angle_sin = sin(segment_angle + symbol_rotation); let angle_cos = cos(segment_angle + symbol_rotation); let rotation_matrix = mat2x2(angle_cos, -angle_sin, angle_sin, angle_cos); let projected_pos = drawable.label_plane_matrix * vec4(in.projected_pos.xy, 0.0, 1.0); let pos_rot = a_offset / 32.0 * fontScale; let pos0 = projected_pos.xy / projected_pos.w + rotation_matrix * pos_rot; let position = drawable.coord_matrix * vec4(pos0, 0.0, 1.0); let gamma_scale = position.w; let is_icon = select(1.0, 0.0, is_sdf == SDF); out.position = position; let inv_tex = 1.0 / select(drawable.texsize, drawable.texsize_icon, is_icon != 0.0); out.tex = a_tex * inv_tex; out.gamma_scale = gamma_scale; out.fontScale = fontScale; out.fade_opacity = fo; out.is_icon = is_icon; #ifndef HAS_UNIFORM_u_fill_color out.fill_color = unpack_mix_color(in.fill_color, drawable.fill_color_t); #endif #ifndef HAS_UNIFORM_u_halo_color out.halo_color = unpack_mix_color(in.halo_color, drawable.halo_color_t); #endif #ifndef HAS_UNIFORM_u_opacity out.opacity = unpack_mix_float(vec2(in.opacity, in.opacity), drawable.opacity_t); #endif #ifndef HAS_UNIFORM_u_halo_width out.halo_width = unpack_mix_float(vec2(in.halo_width, in.halo_width), drawable.halo_width_t); #endif #ifndef HAS_UNIFORM_u_halo_blur out.halo_blur = unpack_mix_float(vec2(in.halo_blur, in.halo_blur), drawable.halo_blur_t); #endif return out; } )"; static constexpr auto fragment = R"( struct FragmentInput { @location(0) tex: vec2, @location(1) fade_opacity: f32, @location(2) gamma_scale: f32, @location(3) fontScale: f32, @location(4) is_icon: f32, #ifndef HAS_UNIFORM_u_fill_color @location(5) fill_color: vec4, #endif #ifndef HAS_UNIFORM_u_halo_color @location(6) halo_color: vec4, #endif #ifndef HAS_UNIFORM_u_opacity @location(7) opacity: f32, #endif #ifndef HAS_UNIFORM_u_halo_width @location(8) halo_width: f32, #endif #ifndef HAS_UNIFORM_u_halo_blur @location(9) halo_blur: f32, #endif }; @group(0) @binding(0) var paintParams: GlobalPaintParamsUBO; @group(0) @binding(3) var tilePropsVector: array; @group(0) @binding(1) var globalIndex: GlobalIndexUBO; @group(0) @binding(4) var props: SymbolEvaluatedPropsUBO; @group(1) @binding(0) var glyph_sampler: sampler; @group(1) @binding(1) var glyph_image: texture_2d; @group(1) @binding(2) var icon_sampler: sampler; @group(1) @binding(3) var icon_image: texture_2d; @fragment fn main(in: FragmentInput) -> @location(0) vec4 { let tileProps = tilePropsVector[globalIndex.value]; #ifndef HAS_UNIFORM_u_fill_color let fill_color = in.fill_color; #else let fill_color = select(props.icon_fill_color, props.text_fill_color, tileProps.is_text != 0u); #endif #ifndef HAS_UNIFORM_u_halo_color let halo_color = in.halo_color; #else let halo_color = select(props.icon_halo_color, props.text_halo_color, tileProps.is_text != 0u); #endif #ifndef HAS_UNIFORM_u_opacity let opacity = in.opacity; #else let opacity = select(props.icon_opacity, props.text_opacity, tileProps.is_text != 0u); #endif #ifndef HAS_UNIFORM_u_halo_width let halo_width = in.halo_width; #else let halo_width = select(props.icon_halo_width, props.text_halo_width, tileProps.is_text != 0u); #endif #ifndef HAS_UNIFORM_u_halo_blur let halo_blur = in.halo_blur; #else let halo_blur = select(props.icon_halo_blur, props.text_halo_blur, tileProps.is_text != 0u); #endif if (in.is_icon != 0.0) { let alpha = opacity * in.fade_opacity; let iconSample = textureSampleLevel(icon_image, icon_sampler, in.tex, 0.0); return iconSample * alpha; } let EDGE_GAMMA = 0.105 / paintParams.pixel_ratio; let color = select(fill_color, halo_color, tileProps.is_halo != 0u); let fontGamma = in.fontScale * tileProps.gamma_scale; let gamma = (select(0.0, halo_blur * 1.19 / SDF_PX, tileProps.is_halo != 0u) + EDGE_GAMMA) / fontGamma; let gamma_scaled = gamma * in.gamma_scale; // For halos, offset the inner edge outward to create the ring var inner_edge = (256.0 - 64.0) / 256.0; if (tileProps.is_halo != 0u) { inner_edge = inner_edge + gamma_scaled; } let sample = textureSampleLevel(glyph_image, glyph_sampler, in.tex, 0.0); let dist = select(sample.a, sample.r, tileProps.is_text != 0u); var alpha = smoothstep(inner_edge - gamma_scaled, inner_edge + gamma_scaled, dist); // When drawing halos, make the inside transparent so the fill can show through if (tileProps.is_halo != 0u) { let halo_edge = (6.0 - halo_width / in.fontScale) / SDF_PX; alpha = min(smoothstep(halo_edge - gamma_scaled, halo_edge + gamma_scaled, dist), 1.0 - alpha); } let coverage = alpha * opacity * in.fade_opacity; let outAlpha = color.a * coverage; return vec4(color.rgb * coverage, outAlpha); } )"; }; } // namespace shaders } // namespace mbgl