#pragma once #include #include #include #include namespace mbgl { namespace shaders { template <> struct ShaderSource { static constexpr auto name = "FillExtrusionPatternShader"; static constexpr auto vertexMainFunction = "vertexMain"; static constexpr auto fragmentMainFunction = "fragmentMain"; static const std::array uniforms; static const std::array attributes; static constexpr std::array instanceAttributes{}; static const std::array textures; static constexpr auto source = R"( struct VertexStage { short2 pos [[attribute(5)]]; short4 normal_ed [[attribute(6)]]; #if !defined(HAS_UNIFORM_u_base) float base [[attribute(7)]]; #endif #if !defined(HAS_UNIFORM_u_height) float height [[attribute(8)]]; #endif #if !defined(HAS_UNIFORM_u_pattern_from) ushort4 pattern_from [[attribute(9)]]; #endif #if !defined(HAS_UNIFORM_u_pattern_to) ushort4 pattern_to [[attribute(10)]]; #endif }; struct FragmentStage { float4 position [[position, invariant]]; float4 lighting; float2 pos_a; float2 pos_b; #if !defined(HAS_UNIFORM_u_pattern_from) half4 pattern_from; #endif #if !defined(HAS_UNIFORM_u_pattern_to) half4 pattern_to; #endif }; struct FragmentOutput { half4 color [[color(0)]]; //float depth [[depth(less)]]; // Write depth value if it's less than what's already there }; FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]], device const GlobalPaintParamsUBO& paintParams [[buffer(0)]], device const FillExtrusionDrawableUBO& drawable [[buffer(1)]], device const FillExtrusionTilePropsUBO& tileProps [[buffer(2)]], device const FillExtrusionInterpolateUBO& interp [[buffer(3)]], device const FillExtrusionPropsUBO& props [[buffer(4)]]) { #if defined(HAS_UNIFORM_u_base) const auto base = props.light_position_base.w; #else const auto base = max(unpack_mix_float(vertx.base, interp.base_t), 0.0); #endif #if defined(HAS_UNIFORM_u_height) const auto height = props.height; #else const auto height = max(unpack_mix_float(vertx.height, interp.height_t), 0.0); #endif const float3 normal = float3(vertx.normal_ed.xyz); const float edgedistance = vertx.normal_ed.w; const float t = glMod(normal.x, 2.0); const float z = (t != 0.0) ? height : base; // TODO: This would come out wrong on GL for negative values, check it... const float4 position = drawable.matrix * float4(float2(vertx.pos), z, 1); #if defined(OVERDRAW_INSPECTOR) return { .position = position, .lighting = float4(1.0), .pattern_from = float4(1.0), .pattern_to = float4(1.0), .pos_a = float2(1.0), .pos_b = float2(1.0), }; #endif #if defined(HAS_UNIFORM_u_pattern_from) const auto pattern_from = tileProps.pattern_from; #else const auto pattern_from = float4(vertx.pattern_from); #endif #if defined(HAS_UNIFORM_u_pattern_to) const auto pattern_to = tileProps.pattern_to; #else const auto pattern_to = float4(vertx.pattern_to); #endif const float2 pattern_tl_a = pattern_from.xy; const float2 pattern_br_a = pattern_from.zw; const float2 pattern_tl_b = pattern_to.xy; const float2 pattern_br_b = pattern_to.zw; const float pixelRatio = paintParams.pixel_ratio; const float tileZoomRatio = drawable.tile_ratio; const float fromScale = props.from_scale; const float toScale = props.to_scale; const float2 display_size_a = float2((pattern_br_a.x - pattern_tl_a.x) / pixelRatio, (pattern_br_a.y - pattern_tl_a.y) / pixelRatio); const float2 display_size_b = float2((pattern_br_b.x - pattern_tl_b.x) / pixelRatio, (pattern_br_b.y - pattern_tl_b.y) / pixelRatio); const float2 pos = normal.x == 1.0 && normal.y == 0.0 && normal.z == 16384.0 ? float2(vertx.pos) // extrusion top : float2(edgedistance, z * drawable.height_factor); // extrusion side float4 lighting = float4(0.0, 0.0, 0.0, 1.0); float directional = clamp(dot(normal / 16383.0, props.light_position_base.xyz), 0.0, 1.0); directional = mix((1.0 - props.light_intensity), max((0.5 + props.light_intensity), 1.0), directional); if (normal.y != 0.0) { // This avoids another branching statement, but multiplies by a constant of 0.84 if no vertical gradient, // and otherwise calculates the gradient based on base + height directional *= ( (1.0 - props.vertical_gradient) + (props.vertical_gradient * clamp((t + base) * pow(height / 150.0, 0.5), mix(0.7, 0.98, 1.0 - props.light_intensity), 1.0))); } lighting.rgb += clamp(directional * props.light_color_pad.rgb, mix(float3(0.0), float3(0.3), 1.0 - props.light_color_pad.rgb), float3(1.0)); lighting *= props.opacity; return { .position = position, .lighting = lighting, #if !defined(HAS_UNIFORM_u_pattern_from) .pattern_from = half4(pattern_from), #endif #if !defined(HAS_UNIFORM_u_pattern_from) .pattern_to = half4(pattern_to), #endif .pos_a = get_pattern_pos(drawable.pixel_coord_upper, drawable.pixel_coord_lower, fromScale * display_size_a, tileZoomRatio, pos), .pos_b = get_pattern_pos(drawable.pixel_coord_upper, drawable.pixel_coord_lower, toScale * display_size_b, tileZoomRatio, pos), }; } fragment FragmentOutput fragmentMain(FragmentStage in [[stage_in]], device const FillExtrusionDrawableUBO& drawable [[buffer(1)]], device const FillExtrusionTilePropsUBO& tileProps [[buffer(2)]], device const FillExtrusionPropsUBO& props [[buffer(4)]], texture2d image0 [[texture(0)]], sampler image0_sampler [[sampler(0)]]) { #if defined(OVERDRAW_INSPECTOR) return {half4(1.0)/*, in.position.z*/}; #endif #if defined(HAS_UNIFORM_u_pattern_from) const auto pattern_from = float4(tileProps.pattern_from); #else const auto pattern_from = float4(in.pattern_from); #endif #if defined(HAS_UNIFORM_u_pattern_to) const auto pattern_to = float4(tileProps.pattern_to); #else const auto pattern_to = float4(in.pattern_to); #endif const float2 pattern_tl_a = pattern_from.xy; const float2 pattern_br_a = pattern_from.zw; const float2 pattern_tl_b = pattern_to.xy; const float2 pattern_br_b = pattern_to.zw; const float2 imagecoord = glMod(in.pos_a, 1.0); const float2 pos = mix(pattern_tl_a / drawable.texsize, pattern_br_a / drawable.texsize, imagecoord); const float4 color1 = image0.sample(image0_sampler, pos); const float2 imagecoord_b = glMod(in.pos_b, 1.0); const float2 pos2 = mix(pattern_tl_b / drawable.texsize, pattern_br_b / drawable.texsize, imagecoord_b); const float4 color2 = image0.sample(image0_sampler, pos2); return {half4(mix(color1, color2, props.fade) * in.lighting)/*, in.position.z*/}; } )"; }; } // namespace shaders } // namespace mbgl