diff options
Diffstat (limited to 'wgpu/src/shader')
-rw-r--r-- | wgpu/src/shader/gradient.wgsl | 88 | ||||
-rw-r--r-- | wgpu/src/shader/quad.wgsl | 298 | ||||
-rw-r--r-- | wgpu/src/shader/solid.wgsl | 30 | ||||
-rw-r--r-- | wgpu/src/shader/triangle.wgsl | 168 |
4 files changed, 422 insertions, 162 deletions
diff --git a/wgpu/src/shader/gradient.wgsl b/wgpu/src/shader/gradient.wgsl deleted file mode 100644 index 63825aec..00000000 --- a/wgpu/src/shader/gradient.wgsl +++ /dev/null @@ -1,88 +0,0 @@ -struct Uniforms { - transform: mat4x4<f32>, - //xy = start, wz = end - position: vec4<f32>, - //x = start stop, y = end stop, zw = padding - stop_range: vec4<i32>, -} - -struct Stop { - color: vec4<f32>, - offset: f32, -}; - -@group(0) @binding(0) -var<uniform> uniforms: Uniforms; - -@group(0) @binding(1) -var<storage, read> color_stops: array<Stop>; - -struct VertexOutput { - @builtin(position) position: vec4<f32>, - @location(0) raw_position: vec2<f32> -} - -@vertex -fn vs_main(@location(0) input: vec2<f32>) -> VertexOutput { - var output: VertexOutput; - output.position = uniforms.transform * vec4<f32>(input.xy, 0.0, 1.0); - output.raw_position = input; - - return output; -} - -//TODO: rewrite without branching -@fragment -fn fs_main(input: VertexOutput) -> @location(0) vec4<f32> { - let start = uniforms.position.xy; - let end = uniforms.position.zw; - let start_stop = uniforms.stop_range.x; - let end_stop = uniforms.stop_range.y; - - let v1 = end - start; - let v2 = input.raw_position.xy - start; - let unit = normalize(v1); - let offset = dot(unit, v2) / length(v1); - - let min_stop = color_stops[start_stop]; - let max_stop = color_stops[end_stop]; - - var color: vec4<f32>; - - if (offset <= min_stop.offset) { - color = min_stop.color; - } else if (offset >= max_stop.offset) { - color = max_stop.color; - } else { - var min = min_stop; - var max = max_stop; - var min_index = start_stop; - var max_index = end_stop; - - loop { - if (min_index >= max_index - 1) { - break; - } - - let index = min_index + (max_index - min_index) / 2; - - let stop = color_stops[index]; - - if (offset <= stop.offset) { - max = stop; - max_index = index; - } else { - min = stop; - min_index = index; - } - } - - color = mix(min.color, max.color, smoothstep( - min.offset, - max.offset, - offset - )); - } - - return color; -} diff --git a/wgpu/src/shader/quad.wgsl b/wgpu/src/shader/quad.wgsl index cf4f7e4d..3232bdbe 100644 --- a/wgpu/src/shader/quad.wgsl +++ b/wgpu/src/shader/quad.wgsl @@ -5,17 +5,50 @@ struct Globals { @group(0) @binding(0) var<uniform> globals: Globals; -struct VertexInput { +fn distance_alg( + frag_coord: vec2<f32>, + position: vec2<f32>, + size: vec2<f32>, + radius: f32 +) -> f32 { + var inner_size: vec2<f32> = size - vec2<f32>(radius, radius) * 2.0; + var top_left: vec2<f32> = position + vec2<f32>(radius, radius); + var bottom_right: vec2<f32> = top_left + inner_size; + + var top_left_distance: vec2<f32> = top_left - frag_coord; + var bottom_right_distance: vec2<f32> = frag_coord - bottom_right; + + var dist: vec2<f32> = vec2<f32>( + max(max(top_left_distance.x, bottom_right_distance.x), 0.0), + max(max(top_left_distance.y, bottom_right_distance.y), 0.0) + ); + + return sqrt(dist.x * dist.x + dist.y * dist.y); +} + +// Based on the fragement position and the center of the quad, select one of the 4 radi. +// Order matches CSS border radius attribute: +// radi.x = top-left, radi.y = top-right, radi.z = bottom-right, radi.w = bottom-left +fn select_border_radius(radi: vec4<f32>, position: vec2<f32>, center: vec2<f32>) -> f32 { + var rx = radi.x; + var ry = radi.y; + rx = select(radi.x, radi.y, position.x > center.x); + ry = select(radi.w, radi.z, position.x > center.x); + rx = select(rx, ry, position.y > center.y); + return rx; +} + +struct SolidVertexInput { @location(0) v_pos: vec2<f32>, - @location(1) pos: vec2<f32>, - @location(2) scale: vec2<f32>, - @location(3) color: vec4<f32>, + @location(1) color: vec4<f32>, + @location(2) pos: vec2<f32>, + @location(3) scale: vec2<f32>, @location(4) border_color: vec4<f32>, @location(5) border_radius: vec4<f32>, @location(6) border_width: f32, } -struct VertexOutput { +struct SolidVertexOutput { @builtin(position) position: vec4<f32>, @location(0) color: vec4<f32>, @location(1) border_color: vec4<f32>, @@ -26,8 +59,8 @@ struct VertexOutput { } @vertex -fn vs_main(input: VertexInput) -> VertexOutput { - var out: VertexOutput; +fn solid_vs_main(input: SolidVertexInput) -> SolidVertexOutput { + var out: SolidVertexOutput; var pos: vec2<f32> = input.pos * globals.scale; var scale: vec2<f32> = input.scale * globals.scale; @@ -47,54 +80,20 @@ fn vs_main(input: VertexInput) -> VertexOutput { vec4<f32>(pos - vec2<f32>(0.5, 0.5), 0.0, 1.0) ); + out.position = globals.transform * transform * vec4<f32>(input.v_pos, 0.0, 1.0); out.color = input.color; out.border_color = input.border_color; out.pos = pos; out.scale = scale; out.border_radius = border_radius * globals.scale; out.border_width = input.border_width * globals.scale; - out.position = globals.transform * transform * vec4<f32>(input.v_pos, 0.0, 1.0); return out; } -fn distance_alg( - frag_coord: vec2<f32>, - position: vec2<f32>, - size: vec2<f32>, - radius: f32 -) -> f32 { - var inner_size: vec2<f32> = size - vec2<f32>(radius, radius) * 2.0; - var top_left: vec2<f32> = position + vec2<f32>(radius, radius); - var bottom_right: vec2<f32> = top_left + inner_size; - - var top_left_distance: vec2<f32> = top_left - frag_coord; - var bottom_right_distance: vec2<f32> = frag_coord - bottom_right; - - var dist: vec2<f32> = vec2<f32>( - max(max(top_left_distance.x, bottom_right_distance.x), 0.0), - max(max(top_left_distance.y, bottom_right_distance.y), 0.0) - ); - - return sqrt(dist.x * dist.x + dist.y * dist.y); -} - -// Based on the fragement position and the center of the quad, select one of the 4 radi. -// Order matches CSS border radius attribute: -// radi.x = top-left, radi.y = top-right, radi.z = bottom-right, radi.w = bottom-left -fn select_border_radius(radi: vec4<f32>, position: vec2<f32>, center: vec2<f32>) -> f32 { - var rx = radi.x; - var ry = radi.y; - rx = select(radi.x, radi.y, position.x > center.x); - ry = select(radi.w, radi.z, position.x > center.x); - rx = select(rx, ry, position.y > center.y); - return rx; -} - - @fragment -fn fs_main( - input: VertexOutput +fn solid_fs_main( + input: SolidVertexOutput ) -> @location(0) vec4<f32> { var mixed_color: vec4<f32> = input.color; @@ -138,3 +137,214 @@ fn fs_main( return vec4<f32>(mixed_color.x, mixed_color.y, mixed_color.z, mixed_color.w * radius_alpha); } + +struct GradientVertexInput { + @location(0) v_pos: vec2<f32>, + @location(1) color_1: vec4<f32>, + @location(2) color_2: vec4<f32>, + @location(3) color_3: vec4<f32>, + @location(4) color_4: vec4<f32>, + @location(5) color_5: vec4<f32>, + @location(6) color_6: vec4<f32>, + @location(7) color_7: vec4<f32>, + @location(8) color_8: vec4<f32>, + @location(9) offsets_1: vec4<f32>, + @location(10) offsets_2: vec4<f32>, + @location(11) direction: vec4<f32>, + @location(12) position_and_scale: vec4<f32>, + @location(13) border_color: vec4<f32>, + @location(14) border_radius: vec4<f32>, + @location(15) border_width: f32 +} + +struct GradientVertexOutput { + @builtin(position) position: vec4<f32>, + @location(1) color_1: vec4<f32>, + @location(2) color_2: vec4<f32>, + @location(3) color_3: vec4<f32>, + @location(4) color_4: vec4<f32>, + @location(5) color_5: vec4<f32>, + @location(6) color_6: vec4<f32>, + @location(7) color_7: vec4<f32>, + @location(8) color_8: vec4<f32>, + @location(9) offsets_1: vec4<f32>, + @location(10) offsets_2: vec4<f32>, + @location(11) direction: vec4<f32>, + @location(12) position_and_scale: vec4<f32>, + @location(13) border_color: vec4<f32>, + @location(14) border_radius: vec4<f32>, + @location(15) border_width: f32 +} + +@vertex +fn gradient_vs_main(input: GradientVertexInput) -> GradientVertexOutput { + var out: GradientVertexOutput; + + var pos: vec2<f32> = input.position_and_scale.xy * globals.scale; + var scale: vec2<f32> = input.position_and_scale.zw * globals.scale; + + var min_border_radius = min(input.position_and_scale.z, input.position_and_scale.w) * 0.5; + var border_radius: vec4<f32> = vec4<f32>( + min(input.border_radius.x, min_border_radius), + min(input.border_radius.y, min_border_radius), + min(input.border_radius.z, min_border_radius), + min(input.border_radius.w, min_border_radius) + ); + + var transform: mat4x4<f32> = mat4x4<f32>( + vec4<f32>(scale.x + 1.0, 0.0, 0.0, 0.0), + vec4<f32>(0.0, scale.y + 1.0, 0.0, 0.0), + vec4<f32>(0.0, 0.0, 1.0, 0.0), + vec4<f32>(pos - vec2<f32>(0.5, 0.5), 0.0, 1.0) + ); + + out.position = globals.transform * transform * vec4<f32>(input.v_pos, 0.0, 1.0); + out.color_1 = input.color_1; + out.color_2 = input.color_2; + out.color_3 = input.color_3; + out.color_4 = input.color_4; + out.color_5 = input.color_5; + out.color_6 = input.color_6; + out.color_7 = input.color_7; + out.color_8 = input.color_8; + out.offsets_1 = input.offsets_1; + out.offsets_2 = input.offsets_2; + out.direction = input.direction * globals.scale; + out.position_and_scale = vec4<f32>(pos, scale); + out.border_color = input.border_color; + out.border_radius = border_radius * globals.scale; + out.border_width = input.border_width * globals.scale; + + return out; +} + +fn random(coords: vec2<f32>) -> f32 { + return fract(sin(dot(coords, vec2(12.9898,78.233))) * 43758.5453); +} + +/// Returns the current interpolated color with a max 8-stop gradient +fn gradient( + raw_position: vec2<f32>, + direction: vec4<f32>, + colors: array<vec4<f32>, 8>, + offsets: array<f32, 8>, + last_index: i32 +) -> vec4<f32> { + let start = direction.xy; + let end = direction.zw; + + let v1 = end - start; + let v2 = raw_position - start; + let unit = normalize(v1); + let coord_offset = dot(unit, v2) / length(v1); + + //need to store these as a var to use dynamic indexing in a loop + //this is already added to wgsl spec but not in wgpu yet + var colors_arr = colors; + var offsets_arr = offsets; + + var color: vec4<f32>; + + let noise_granularity: f32 = 0.3/255.0; + + for (var i: i32 = 0; i < last_index; i++) { + let curr_offset = offsets_arr[i]; + let next_offset = offsets_arr[i+1]; + + if (coord_offset <= offsets_arr[0]) { + color = colors_arr[0]; + } + + if (curr_offset <= coord_offset && coord_offset <= next_offset) { + color = mix(colors_arr[i], colors_arr[i+1], smoothstep( + curr_offset, + next_offset, + coord_offset, + )); + } + + if (coord_offset >= offsets_arr[last_index]) { + color = colors_arr[last_index]; + } + } + + return color + mix(-noise_granularity, noise_granularity, random(raw_position)); +} + +@fragment +fn gradient_fs_main(input: GradientVertexOutput) -> @location(0) vec4<f32> { + let colors = array<vec4<f32>, 8>( + input.color_1, + input.color_2, + input.color_3, + input.color_4, + input.color_5, + input.color_6, + input.color_7, + input.color_8, + ); + + var offsets = array<f32, 8>( + input.offsets_1.x, + input.offsets_1.y, + input.offsets_1.z, + input.offsets_1.w, + input.offsets_2.x, + input.offsets_2.y, + input.offsets_2.z, + input.offsets_2.w, + ); + + //TODO could just pass this in to the shader but is probably more performant to just check it here + var last_index = 7; + for (var i: i32 = 0; i <= 7; i++) { + if (offsets[i] > 1.0) { + last_index = i - 1; + break; + } + } + + var mixed_color: vec4<f32> = gradient(input.position.xy, input.direction, colors, offsets, last_index); + + let pos = input.position_and_scale.xy; + let scale = input.position_and_scale.zw; + + var border_radius = select_border_radius( + input.border_radius, + input.position.xy, + (pos + scale * 0.5).xy + ); + + if (input.border_width > 0.0) { + var internal_border: f32 = max(border_radius - input.border_width, 0.0); + + var internal_distance: f32 = distance_alg( + input.position.xy, + pos + vec2<f32>(input.border_width, input.border_width), + scale - vec2<f32>(input.border_width * 2.0, input.border_width * 2.0), + internal_border + ); + + var border_mix: f32 = smoothstep( + max(internal_border - 0.5, 0.0), + internal_border + 0.5, + internal_distance + ); + + mixed_color = mix(mixed_color, input.border_color, vec4<f32>(border_mix, border_mix, border_mix, border_mix)); + } + + var dist: f32 = distance_alg( + input.position.xy, + pos, + scale, + border_radius + ); + + var radius_alpha: f32 = 1.0 - smoothstep( + max(border_radius - 0.5, 0.0), + border_radius + 0.5, + dist); + + return vec4<f32>(mixed_color.x, mixed_color.y, mixed_color.z, mixed_color.w * radius_alpha); +} diff --git a/wgpu/src/shader/solid.wgsl b/wgpu/src/shader/solid.wgsl deleted file mode 100644 index b24402f8..00000000 --- a/wgpu/src/shader/solid.wgsl +++ /dev/null @@ -1,30 +0,0 @@ -struct Globals { - transform: mat4x4<f32>, -} - -@group(0) @binding(0) var<uniform> globals: Globals; - -struct VertexInput { - @location(0) position: vec2<f32>, - @location(1) color: vec4<f32>, -} - -struct VertexOutput { - @builtin(position) position: vec4<f32>, - @location(0) color: vec4<f32>, -} - -@vertex -fn vs_main(input: VertexInput) -> VertexOutput { - var out: VertexOutput; - - out.color = input.color; - out.position = globals.transform * vec4<f32>(input.position, 0.0, 1.0); - - return out; -} - -@fragment -fn fs_main(input: VertexOutput) -> @location(0) vec4<f32> { - return input.color; -} diff --git a/wgpu/src/shader/triangle.wgsl b/wgpu/src/shader/triangle.wgsl new file mode 100644 index 00000000..625fa46e --- /dev/null +++ b/wgpu/src/shader/triangle.wgsl @@ -0,0 +1,168 @@ +struct Globals { + transform: mat4x4<f32>, +} + +@group(0) @binding(0) var<uniform> globals: Globals; + +struct SolidVertexInput { + @location(0) position: vec2<f32>, + @location(1) color: vec4<f32>, +} + +struct SolidVertexOutput { + @builtin(position) position: vec4<f32>, + @location(0) color: vec4<f32>, +} + +@vertex +fn solid_vs_main(input: SolidVertexInput) -> SolidVertexOutput { + var out: SolidVertexOutput; + + out.color = input.color; + out.position = globals.transform * vec4<f32>(input.position, 0.0, 1.0); + + return out; +} + +@fragment +fn solid_fs_main(input: SolidVertexOutput) -> @location(0) vec4<f32> { + return input.color; +} + +struct GradientVertexOutput { + @builtin(position) position: vec4<f32>, + @location(0) raw_position: vec2<f32>, + @location(1) color_1: vec4<f32>, + @location(2) color_2: vec4<f32>, + @location(3) color_3: vec4<f32>, + @location(4) color_4: vec4<f32>, + @location(5) color_5: vec4<f32>, + @location(6) color_6: vec4<f32>, + @location(7) color_7: vec4<f32>, + @location(8) color_8: vec4<f32>, + @location(9) offsets_1: vec4<f32>, + @location(10) offsets_2: vec4<f32>, + @location(11) direction: vec4<f32>, +} + +@vertex +fn gradient_vs_main( + @location(0) input: vec2<f32>, + @location(1) color_1: vec4<f32>, + @location(2) color_2: vec4<f32>, + @location(3) color_3: vec4<f32>, + @location(4) color_4: vec4<f32>, + @location(5) color_5: vec4<f32>, + @location(6) color_6: vec4<f32>, + @location(7) color_7: vec4<f32>, + @location(8) color_8: vec4<f32>, + @location(9) offsets_1: vec4<f32>, + @location(10) offsets_2: vec4<f32>, + @location(11) direction: vec4<f32>, +) -> GradientVertexOutput { + var output: GradientVertexOutput; + + output.position = globals.transform * vec4<f32>(input.xy, 0.0, 1.0); + output.raw_position = input; + output.color_1 = color_1; + output.color_2 = color_2; + output.color_3 = color_3; + output.color_4 = color_4; + output.color_5 = color_5; + output.color_6 = color_6; + output.color_7 = color_7; + output.color_8 = color_8; + output.offsets_1 = offsets_1; + output.offsets_2 = offsets_2; + output.direction = direction; + + return output; +} + +fn random(coords: vec2<f32>) -> f32 { + return fract(sin(dot(coords, vec2(12.9898,78.233))) * 43758.5453); +} + +/// Returns the current interpolated color with a max 8-stop gradient +fn gradient( + raw_position: vec2<f32>, + direction: vec4<f32>, + colors: array<vec4<f32>, 8>, + offsets: array<f32, 8>, + last_index: i32 +) -> vec4<f32> { + let start = direction.xy; + let end = direction.zw; + + let v1 = end - start; + let v2 = raw_position - start; + let unit = normalize(v1); + let coord_offset = dot(unit, v2) / length(v1); + + //need to store these as a var to use dynamic indexing in a loop + //this is already added to wgsl spec but not in wgpu yet + var colors_arr = colors; + var offsets_arr = offsets; + + var color: vec4<f32>; + + let noise_granularity: f32 = 0.3/255.0; + + for (var i: i32 = 0; i < last_index; i++) { + let curr_offset = offsets_arr[i]; + let next_offset = offsets_arr[i+1]; + + if (coord_offset <= offsets_arr[0]) { + color = colors_arr[0]; + } + + if (curr_offset <= coord_offset && coord_offset <= next_offset) { + color = mix(colors_arr[i], colors_arr[i+1], smoothstep( + curr_offset, + next_offset, + coord_offset, + )); + } + + if (coord_offset >= offsets_arr[last_index]) { + color = colors_arr[last_index]; + } + } + + return color + mix(-noise_granularity, noise_granularity, random(raw_position)); +} + +@fragment +fn gradient_fs_main(input: GradientVertexOutput) -> @location(0) vec4<f32> { + let colors = array<vec4<f32>, 8>( + input.color_1, + input.color_2, + input.color_3, + input.color_4, + input.color_5, + input.color_6, + input.color_7, + input.color_8, + ); + + var offsets = array<f32, 8>( + input.offsets_1.x, + input.offsets_1.y, + input.offsets_1.z, + input.offsets_1.w, + input.offsets_2.x, + input.offsets_2.y, + input.offsets_2.z, + input.offsets_2.w, + ); + + var last_index = 7; + for (var i: i32 = 0; i <= 7; i++) { + if (offsets[i] >= 1.0) { + last_index = i; + break; + } + } + + return gradient(input.raw_position, input.direction, colors, offsets, last_index); +} |