├── README.md └── how-to-draw-rectangles-using-gpu.metal /README.md: -------------------------------------------------------------------------------- 1 | # samples 2 | 3 | Code samples used in our blog.warp.dev posts 4 | -------------------------------------------------------------------------------- /how-to-draw-rectangles-using-gpu.metal: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | using namespace metal; 4 | 5 | #include "shader_types.h" 6 | 7 | // Vertex shader outputs and fragment shader inputs 8 | struct RectFragmentData 9 | { 10 | float4 position [[position]]; 11 | float2 pixel_position [[pixel_position]]; 12 | float2 rect_origin; 13 | float2 rect_size; 14 | float2 rect_center; 15 | float2 rect_corner; 16 | float border_top; 17 | float border_right; 18 | float border_bottom; 19 | float border_left; 20 | float corner_radius_top; 21 | float corner_radius_bottom; 22 | float2 background_start; 23 | float2 background_end; 24 | float4 background_start_color; 25 | float4 background_end_color; 26 | float2 border_start; 27 | float2 border_end; 28 | float4 border_start_color; 29 | float4 border_end_color; 30 | }; 31 | 32 | float distance_from_rect(vector_float2 pixel_pos, vector_float2 rect_center, vector_float2 rect_corner, float corner_radius) { 33 | vector_float2 p = pixel_pos - rect_center; 34 | vector_float2 q = abs(p) - rect_corner + corner_radius; 35 | return length(max(q, 0.0)) + min(max(q.x, q.y), 0.0) - corner_radius; 36 | } 37 | 38 | float4 derive_color(float2 pixel_pos, float2 start, float2 end, float4 start_color, float4 end_color) { 39 | float2 adjusted_end = end - start; 40 | float h = dot(pixel_pos - start, adjusted_end) / dot(adjusted_end, adjusted_end); 41 | return mix(start_color, end_color, h); 42 | } 43 | 44 | vertex RectFragmentData 45 | rect_vertex_shader( 46 | uint vertex_id [[vertex_id]], 47 | ushort instance_id [[instance_id]], 48 | constant float2 *vertices [[buffer(0)]], 49 | constant PerRectUniforms *glyph_uniforms [[buffer(1)]], 50 | constant Uniforms *uniforms [[buffer(2)]]) 51 | { 52 | const constant PerRectUniforms *rect = &glyph_uniforms[instance_id]; 53 | 54 | float2 pixel_pos = vertices[vertex_id] * rect->size + rect->origin; 55 | float2 device_pos = pixel_pos / uniforms->viewport_size * float2(2.0, -2.0) + float2(-1.0, 1.0); 56 | 57 | RectFragmentData out; 58 | out.position = float4(device_pos, 0.0, 1.0); 59 | out.pixel_position = pixel_pos; 60 | out.rect_origin = rect->origin; 61 | out.rect_size = rect->size; 62 | out.rect_corner = rect->size / 2.0; 63 | out.rect_center = rect->origin + out.rect_corner; 64 | out.border_top = rect->border_top; 65 | out.border_right = rect->border_right; 66 | out.border_bottom = rect->border_bottom; 67 | out.border_left = rect->border_left; 68 | out.corner_radius_top = rect->corner_radius_top; 69 | out.corner_radius_bottom = rect->corner_radius_bottom; 70 | out.background_start = rect->background_start * rect->size + rect->origin; 71 | out.background_end = rect->background_end * rect->size + rect->origin; 72 | out.background_start_color = rect->background_start_color; 73 | out.background_end_color = rect->background_end_color; 74 | out.border_start = rect->border_start * rect->size + rect->origin; 75 | out.border_end = rect->border_end * rect->size + rect->origin; 76 | out.border_start_color = rect->border_start_color; 77 | out.border_end_color = rect->border_end_color; 78 | return out; 79 | } 80 | 81 | fragment float4 rect_fragment_shader( 82 | RectFragmentData in [[stage_in]], 83 | constant Uniforms *uniforms [[buffer(0)]]) 84 | { 85 | float shape_distance; 86 | float background_distance; 87 | float corner_radius; 88 | 89 | 90 | float2 border_corner = in.rect_corner; 91 | if (in.position.y >= in.rect_center.y) { 92 | border_corner.y -= in.border_bottom; 93 | corner_radius = in.corner_radius_bottom; 94 | } else { 95 | border_corner.y -= in.border_top; 96 | corner_radius = in.corner_radius_top; 97 | } 98 | if (in.position.x >= in.rect_center.x) { 99 | border_corner.x -= in.border_right; 100 | } else { 101 | border_corner.x -= in.border_left; 102 | } 103 | shape_distance = distance_from_rect(in.position.xy, in.rect_center, in.rect_corner, corner_radius); 104 | background_distance = distance_from_rect(in.position.xy, in.rect_center, border_corner, corner_radius); 105 | 106 | float4 color; 107 | float4 background_color = derive_color(in.position.xy, in.background_start, in.background_end, in.background_start_color, in.background_end_color); 108 | float4 border_color = derive_color(in.position.xy, in.border_start, in.border_end, in.border_start_color, in.border_end_color); 109 | color = background_color; 110 | 111 | // Only blend in the color with the border color if we're actually rendering a border color. 112 | if (border_color.a != 0) { 113 | color = mix(background_color, border_color, smoothstep(-0.5, 0.5, background_distance)); 114 | } 115 | 116 | // If there's a corner radius we need to do some anti aliasing to smooth out the rounded corner effect. 117 | if (corner_radius > 0) { 118 | color.a *= 1.0 - smoothstep(-0.75, -0.1, shape_distance); 119 | } 120 | return color; 121 | } --------------------------------------------------------------------------------