From 469261e5a68384fda36763839ed37519d719531b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Sat, 3 May 2025 22:22:23 +0200 Subject: [PATCH] Replace `mix` calls with proper alpha blending in `quad` shaders --- wgpu/src/quad.rs | 13 +----------- wgpu/src/shader/color/oklab.wgsl | 2 +- wgpu/src/shader/quad.wgsl | 4 ++++ wgpu/src/shader/quad/gradient.wgsl | 34 ++++++++++++++++++------------ wgpu/src/shader/quad/solid.wgsl | 24 ++++++++------------- 5 files changed, 35 insertions(+), 42 deletions(-) diff --git a/wgpu/src/quad.rs b/wgpu/src/quad.rs index 4c00945c..b3ac3f48 100644 --- a/wgpu/src/quad.rs +++ b/wgpu/src/quad.rs @@ -317,18 +317,7 @@ fn color_target_state( ) -> [Option; 1] { [Some(wgpu::ColorTargetState { format, - blend: Some(wgpu::BlendState { - color: wgpu::BlendComponent { - src_factor: wgpu::BlendFactor::SrcAlpha, - dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha, - operation: wgpu::BlendOperation::Add, - }, - alpha: wgpu::BlendComponent { - src_factor: wgpu::BlendFactor::One, - dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha, - operation: wgpu::BlendOperation::Add, - }, - }), + blend: Some(wgpu::BlendState::PREMULTIPLIED_ALPHA_BLENDING), write_mask: wgpu::ColorWrites::ALL, })] } diff --git a/wgpu/src/shader/color/oklab.wgsl b/wgpu/src/shader/color/oklab.wgsl index 0dc37ba6..343eaa15 100644 --- a/wgpu/src/shader/color/oklab.wgsl +++ b/wgpu/src/shader/color/oklab.wgsl @@ -20,7 +20,7 @@ fn interpolate_color(from_: vec4, to_: vec4, factor: f32) -> vec4 var color = to_rgb * (mixed * mixed * mixed); // Alpha interpolation - color.a = mix(from_.a, to_.a, factor); + color = to_ * factor + from_ * (1.0 - factor); return color; } diff --git a/wgpu/src/shader/quad.wgsl b/wgpu/src/shader/quad.wgsl index b213c8cf..0a4cd7b4 100644 --- a/wgpu/src/shader/quad.wgsl +++ b/wgpu/src/shader/quad.wgsl @@ -5,6 +5,10 @@ struct Globals { @group(0) @binding(0) var globals: Globals; +fn blend(over: vec4, under: vec4, alpha: f32) -> vec4 { + return over * alpha + under * (1.0 - alpha); +} + fn distance_alg( frag_coord: vec2, position: vec2, diff --git a/wgpu/src/shader/quad/gradient.wgsl b/wgpu/src/shader/quad/gradient.wgsl index 4ad2fea8..ddd7d1c4 100644 --- a/wgpu/src/shader/quad/gradient.wgsl +++ b/wgpu/src/shader/quad/gradient.wgsl @@ -56,7 +56,7 @@ fn gradient_vs_main(input: GradientVertexInput) -> GradientVertexOutput { out.offsets = input.offsets; out.direction = input.direction * globals.scale; out.position_and_scale = vec4(pos, scale); - out.border_color = input.border_color; + out.border_color = vec4(input.border_color.xyz * input.border_color.a, input.border_color.a); out.border_radius = border_radius * globals.scale; out.border_width = input.border_width * globals.scale; @@ -119,14 +119,14 @@ fn gradient( @fragment fn gradient_fs_main(input: GradientVertexOutput) -> @location(0) vec4 { let colors = array, 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), + unpack_color(input.colors_1.xy), + unpack_color(input.colors_1.zw), + unpack_color(input.colors_2.xy), + unpack_color(input.colors_2.zw), + unpack_color(input.colors_3.xy), + unpack_color(input.colors_3.zw), + unpack_color(input.colors_4.xy), + unpack_color(input.colors_4.zw), ); let offsets_1: vec4 = unpack_u32(input.offsets.xy); @@ -179,7 +179,7 @@ fn gradient_fs_main(input: GradientVertexOutput) -> @location(0) vec4 { internal_distance ); - mixed_color = mix(mixed_color, input.border_color, vec4(border_mix, border_mix, border_mix, border_mix)); + mixed_color = blend(input.border_color, mixed_color, border_mix); } var dist: f32 = distance_alg( @@ -194,12 +194,18 @@ fn gradient_fs_main(input: GradientVertexOutput) -> @location(0) vec4 { border_radius + 0.5, dist); - return vec4(mixed_color.x, mixed_color.y, mixed_color.z, mixed_color.w * radius_alpha); + return mixed_color * radius_alpha; } -fn unpack_u32(color: vec2) -> vec4 { - let rg: vec2 = unpack2x16float(color.x); - let ba: vec2 = unpack2x16float(color.y); +fn unpack_color(data: vec2) -> vec4 { + let color = unpack_u32(data); + + return vec4(color.xyz * color.a, color.a); +} + +fn unpack_u32(data: vec2) -> vec4 { + let rg: vec2 = unpack2x16float(data.x); + let ba: vec2 = unpack2x16float(data.y); return vec4(rg.y, rg.x, ba.y, ba.x); } diff --git a/wgpu/src/shader/quad/solid.wgsl b/wgpu/src/shader/quad/solid.wgsl index 8eee16bb..979d7174 100644 --- a/wgpu/src/shader/quad/solid.wgsl +++ b/wgpu/src/shader/quad/solid.wgsl @@ -56,13 +56,13 @@ fn solid_vs_main(input: SolidVertexInput) -> SolidVertexOutput { ); out.position = globals.transform * transform * vec4(vertex_position(input.vertex_index), 0.0, 1.0); - out.color = input.color; - out.border_color = input.border_color; + out.color = vec4(input.color.xyz * input.color.a, input.color.a); + out.border_color = vec4(input.border_color.xyz * input.border_color.a, input.border_color.a); out.pos = input.pos * globals.scale + snap; out.scale = input.scale * globals.scale; out.border_radius = border_radius * globals.scale; out.border_width = input.border_width * globals.scale; - out.shadow_color = input.shadow_color; + out.shadow_color = vec4(input.shadow_color.xyz, input.shadow_color.a); out.shadow_offset = input.shadow_offset * globals.scale; out.shadow_blur_radius = input.shadow_blur_radius * globals.scale; @@ -97,7 +97,7 @@ fn solid_fs_main( internal_distance ); - mixed_color = mix(input.color, input.border_color, vec4(border_mix, border_mix, border_mix, border_mix)); + mixed_color = blend(input.border_color, input.color, border_mix); } var dist: f32 = distance_alg( @@ -113,7 +113,7 @@ fn solid_fs_main( dist ); - let quad_color = vec4(mixed_color.x, mixed_color.y, mixed_color.z, mixed_color.w * radius_alpha); + let quad_color = mixed_color * radius_alpha; if input.shadow_color.a > 0.0 { let shadow_radius = select_border_radius( @@ -121,17 +121,11 @@ fn solid_fs_main( input.position.xy - input.shadow_offset, (input.pos + input.scale * 0.5).xy ); - let shadow_distance = max(rounded_box_sdf(input.position.xy - input.pos - input.shadow_offset - (input.scale / 2.0), input.scale / 2.0, shadow_radius), 0.); - - let shadow_alpha = 1.0 - smoothstep(-input.shadow_blur_radius, input.shadow_blur_radius, shadow_distance); - let shadow_color = input.shadow_color; - let base_color = mix( - vec4(shadow_color.x, shadow_color.y, shadow_color.z, 0.0), - quad_color, - quad_color.a - ); - return mix(base_color, shadow_color, (1.0 - radius_alpha) * shadow_alpha); + let shadow_distance = max(rounded_box_sdf(input.position.xy - input.pos - input.shadow_offset - (input.scale / 2.0), input.scale / 2.0, shadow_radius), 0.); + let shadow_alpha = 1.0 - smoothstep(-input.shadow_blur_radius, input.shadow_blur_radius, shadow_distance); + + return blend(input.shadow_color, quad_color, (1.0 - radius_alpha) * shadow_alpha); } else { return quad_color; }