git.lucas.co / cce-ui
GPU-accelerated UI toolkit (Vulkan)
git clone https://git.lucas.co/cce-ui.git

commit6fe182313d95c225a66ee331491eb09372cc58a0
parente253fe2c4f
authorLucas Galante <[email protected]>
date2026-07-31 13:08
feat: per-draw opacity on SceneDraw

A whole-draw alpha multiplier through the scene uniform (straight-alpha
blend, already enabled): 1.0 = today's opaque draws. First consumer is
the designer's Render-node Opacity slider.

Co-Authored-By: Claude Fable 5 <[email protected]>

 src/vk/scene.rs     | 7 +++++++
 src/vk/scene3d.wgsl | 4 +++-
 2 files changed, 10 insertions(+), 1 deletion(-)

diff --git a/src/vk/scene.rs b/src/vk/scene.rs
index 542616b..f046acf 100644
--- a/src/vk/scene.rs
+++ b/src/vk/scene.rs
@@ -41,6 +41,9 @@ pub struct SceneDraw {
     /// lines inherit the mesh's colors and would otherwise vanish into the
     /// identical fill beneath.
     pub wire_tint: [f32; 4],
+    /// Whole-draw alpha multiplier (1.0 = opaque). The pass blends with
+    /// straight alpha, so translucent draws show whatever rendered beneath.
+    pub opacity: f32,
 }
 
 /// shader_3d.wgsl's uniform block.
@@ -52,6 +55,8 @@ struct SceneUniforms {
     window_radius: f32,
     corner_shape: f32,
     wire_tint: [f32; 4],
+    opacity: f32,
+    _pad: [f32; 3],
 }
 
 const UNIFORM_SIZE: vk::DeviceSize = std::mem::size_of::<SceneUniforms>() as vk::DeviceSize;
@@ -686,6 +691,8 @@ impl SceneStage {
                 window_radius: corner_radius_px,
                 corner_shape,
                 wire_tint: draw.wire_tint,
+                opacity: draw.opacity,
+                _pad: [0.0; 3],
             };
             let offset = (self.uniform_stride as usize) * i;
             mapped[offset..offset + UNIFORM_SIZE as usize]
diff --git a/src/vk/scene3d.wgsl b/src/vk/scene3d.wgsl
index 64cbc66..a16bc46 100644
--- a/src/vk/scene3d.wgsl
+++ b/src/vk/scene3d.wgsl
@@ -9,6 +9,8 @@ struct Uniforms {
     // colors untouched; a wireframe pass overlaid on its own filled mesh
     // sets it so the lines separate from the identical fill beneath.
     wire_tint: vec4<f32>,
+    // Whole-draw alpha multiplier (straight-alpha blend): 1 = opaque.
+    opacity: f32,
 }
 
 @group(0) @binding(0) var<uniform> uniforms: Uniforms;
@@ -100,5 +102,5 @@ fn fs_main(in: VertexOutput) -> @location(0) vec4f {
         let d = clamp(dot(n, l) * 0.5 + 0.5, 0.0, 1.0);
         rgb = rgb * (0.55 + 0.45 * d);
     }
-    return vec4f(rgb, cov);
+    return vec4f(rgb, cov * uniforms.opacity);
 }