diff options
| author | 2023-09-08 04:55:16 +0200 | |
|---|---|---|
| committer | 2023-09-08 04:55:16 +0200 | |
| commit | 89d9c45d25e9bf89e48858b135bc7a2d41c757a8 (patch) | |
| tree | 3d1c109d1e92f822cb841797788cccf55ce8b09c /wgpu/src/shader/quad | |
| parent | b5e7fb240cda919b66fb17a18cedd6d2ec064eaf (diff) | |
| parent | 90cbab18b95a2a90a6a527280a6ca461203ea1b3 (diff) | |
| download | iced-89d9c45d25e9bf89e48858b135bc7a2d41c757a8.tar.gz iced-89d9c45d25e9bf89e48858b135bc7a2d41c757a8.tar.bz2 iced-89d9c45d25e9bf89e48858b135bc7a2d41c757a8.zip | |
Merge pull request #2055 from matze/perceptual-gradient
Compute gradients in Oklab color space
Diffstat (limited to '')
| -rw-r--r-- | wgpu/src/shader/quad.wgsl | 306 | ||||
| -rw-r--r-- | wgpu/src/shader/quad/gradient.wgsl | 205 | ||||
| -rw-r--r-- | wgpu/src/shader/quad/solid.wgsl | 99 | 
3 files changed, 304 insertions, 306 deletions
| diff --git a/wgpu/src/shader/quad.wgsl b/wgpu/src/shader/quad.wgsl index 023b5a6d..f919cfe2 100644 --- a/wgpu/src/shader/quad.wgsl +++ b/wgpu/src/shader/quad.wgsl @@ -37,309 +37,3 @@ fn select_border_radius(radi: vec4<f32>, position: vec2<f32>, center: vec2<f32>)      rx = select(rx, ry, position.y > center.y);      return rx;  } - -fn unpack_u32(color: vec2<u32>) -> vec4<f32> { -    let rg: vec2<f32> = unpack2x16float(color.x); -    let ba: vec2<f32> = unpack2x16float(color.y); - -    return vec4<f32>(rg.y, rg.x, ba.y, ba.x); -} - -struct SolidVertexInput { -    @location(0) v_pos: vec2<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 SolidVertexOutput { -    @builtin(position) position: vec4<f32>, -    @location(0) color: vec4<f32>, -    @location(1) border_color: vec4<f32>, -    @location(2) pos: vec2<f32>, -    @location(3) scale: vec2<f32>, -    @location(4) border_radius: vec4<f32>, -    @location(5) border_width: f32, -} - -@vertex -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; - -    var min_border_radius = min(input.scale.x, input.scale.y) * 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 = 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; - -    return out; -} - -@fragment -fn solid_fs_main( -    input: SolidVertexOutput -) -> @location(0) vec4<f32> { -    var mixed_color: vec4<f32> = input.color; - -    var border_radius = select_border_radius( -        input.border_radius, -        input.position.xy, -        (input.pos + input.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, -            input.pos + vec2<f32>(input.border_width, input.border_width), -            input.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(input.color, input.border_color, vec4<f32>(border_mix, border_mix, border_mix, border_mix)); -    } - -    var dist: f32 = distance_alg( -        vec2<f32>(input.position.x, input.position.y), -        input.pos, -        input.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); -} - -struct GradientVertexInput { -    @location(0) v_pos: vec2<f32>, -    @location(1) @interpolate(flat) colors_1: vec4<u32>, -    @location(2) @interpolate(flat) colors_2: vec4<u32>, -    @location(3) @interpolate(flat) colors_3: vec4<u32>, -    @location(4) @interpolate(flat) colors_4: vec4<u32>, -    @location(5) @interpolate(flat) offsets: vec4<u32>, -    @location(6) direction: vec4<f32>, -    @location(7) position_and_scale: vec4<f32>, -    @location(8) border_color: vec4<f32>, -    @location(9) border_radius: vec4<f32>, -    @location(10) border_width: f32, -} - -struct GradientVertexOutput { -    @builtin(position) position: vec4<f32>, -    @location(1) @interpolate(flat) colors_1: vec4<u32>, -    @location(2) @interpolate(flat) colors_2: vec4<u32>, -    @location(3) @interpolate(flat) colors_3: vec4<u32>, -    @location(4) @interpolate(flat) colors_4: vec4<u32>, -    @location(5) @interpolate(flat) offsets: vec4<u32>, -    @location(6) direction: vec4<f32>, -    @location(7) position_and_scale: vec4<f32>, -    @location(8) border_color: vec4<f32>, -    @location(9) border_radius: vec4<f32>, -    @location(10) 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.colors_1 = input.colors_1; -    out.colors_2 = input.colors_2; -    out.colors_3 = input.colors_3; -    out.colors_4 = input.colors_4; -    out.offsets = input.offsets; -    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>( -        unpack_u32(input.colors_1.xy), -        unpack_u32(input.colors_1.zw), -        unpack_u32(input.colors_2.xy), -        unpack_u32(input.colors_2.zw), -        unpack_u32(input.colors_3.xy), -        unpack_u32(input.colors_3.zw), -        unpack_u32(input.colors_4.xy), -        unpack_u32(input.colors_4.zw), -    ); - -    let offsets_1: vec4<f32> = unpack_u32(input.offsets.xy); -    let offsets_2: vec4<f32> = unpack_u32(input.offsets.zw); - -    var offsets = array<f32, 8>( -        offsets_1.x, -        offsets_1.y, -        offsets_1.z, -        offsets_1.w, -        offsets_2.x, -        offsets_2.y, -        offsets_2.z, -        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/quad/gradient.wgsl b/wgpu/src/shader/quad/gradient.wgsl new file mode 100644 index 00000000..0754e97f --- /dev/null +++ b/wgpu/src/shader/quad/gradient.wgsl @@ -0,0 +1,205 @@ +struct GradientVertexInput { +    @location(0) v_pos: vec2<f32>, +    @location(1) @interpolate(flat) colors_1: vec4<u32>, +    @location(2) @interpolate(flat) colors_2: vec4<u32>, +    @location(3) @interpolate(flat) colors_3: vec4<u32>, +    @location(4) @interpolate(flat) colors_4: vec4<u32>, +    @location(5) @interpolate(flat) offsets: vec4<u32>, +    @location(6) direction: vec4<f32>, +    @location(7) position_and_scale: vec4<f32>, +    @location(8) border_color: vec4<f32>, +    @location(9) border_radius: vec4<f32>, +    @location(10) border_width: f32, +} + +struct GradientVertexOutput { +    @builtin(position) position: vec4<f32>, +    @location(1) @interpolate(flat) colors_1: vec4<u32>, +    @location(2) @interpolate(flat) colors_2: vec4<u32>, +    @location(3) @interpolate(flat) colors_3: vec4<u32>, +    @location(4) @interpolate(flat) colors_4: vec4<u32>, +    @location(5) @interpolate(flat) offsets: vec4<u32>, +    @location(6) direction: vec4<f32>, +    @location(7) position_and_scale: vec4<f32>, +    @location(8) border_color: vec4<f32>, +    @location(9) border_radius: vec4<f32>, +    @location(10) 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.colors_1 = input.colors_1; +    out.colors_2 = input.colors_2; +    out.colors_3 = input.colors_3; +    out.colors_4 = input.colors_4; +    out.offsets = input.offsets; +    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) { +            let from_ = colors_arr[i]; +            let to_ = colors_arr[i+1]; +            let factor = smoothstep(curr_offset, next_offset, coord_offset); + +            color = interpolate_color(from_, to_, factor); +        } + +        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>( +        unpack_u32(input.colors_1.xy), +        unpack_u32(input.colors_1.zw), +        unpack_u32(input.colors_2.xy), +        unpack_u32(input.colors_2.zw), +        unpack_u32(input.colors_3.xy), +        unpack_u32(input.colors_3.zw), +        unpack_u32(input.colors_4.xy), +        unpack_u32(input.colors_4.zw), +    ); + +    let offsets_1: vec4<f32> = unpack_u32(input.offsets.xy); +    let offsets_2: vec4<f32> = unpack_u32(input.offsets.zw); + +    var offsets = array<f32, 8>( +        offsets_1.x, +        offsets_1.y, +        offsets_1.z, +        offsets_1.w, +        offsets_2.x, +        offsets_2.y, +        offsets_2.z, +        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); +} + +fn unpack_u32(color: vec2<u32>) -> vec4<f32> { +    let rg: vec2<f32> = unpack2x16float(color.x); +    let ba: vec2<f32> = unpack2x16float(color.y); + +    return vec4<f32>(rg.y, rg.x, ba.y, ba.x); +} diff --git a/wgpu/src/shader/quad/solid.wgsl b/wgpu/src/shader/quad/solid.wgsl new file mode 100644 index 00000000..ebd6d877 --- /dev/null +++ b/wgpu/src/shader/quad/solid.wgsl @@ -0,0 +1,99 @@ +struct SolidVertexInput { +    @location(0) v_pos: vec2<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 SolidVertexOutput { +    @builtin(position) position: vec4<f32>, +    @location(0) color: vec4<f32>, +    @location(1) border_color: vec4<f32>, +    @location(2) pos: vec2<f32>, +    @location(3) scale: vec2<f32>, +    @location(4) border_radius: vec4<f32>, +    @location(5) border_width: f32, +} + +@vertex +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; + +    var min_border_radius = min(input.scale.x, input.scale.y) * 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 = 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; + +    return out; +} + +@fragment +fn solid_fs_main( +    input: SolidVertexOutput +) -> @location(0) vec4<f32> { +    var mixed_color: vec4<f32> = input.color; + +    var border_radius = select_border_radius( +        input.border_radius, +        input.position.xy, +        (input.pos + input.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, +            input.pos + vec2<f32>(input.border_width, input.border_width), +            input.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(input.color, input.border_color, vec4<f32>(border_mix, border_mix, border_mix, border_mix)); +    } + +    var dist: f32 = distance_alg( +        vec2<f32>(input.position.x, input.position.y), +        input.pos, +        input.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); +} | 
