3de3709871
A playing pill cost the iGPU about 2.9 ms and the CPU 3.7 ms a frame, 60 frames a second: two vello passes over the whole island window for the surface's shadow and gradients (vello keeps no gradient between frames) and one for the bars. Now 0.4 ms GPU and 1.2 ms CPU. The open card went from 5.2 to 2.6 ms GPU and 6.2 to 3.4 ms CPU a frame. surface.wgsl draws every design's shadow, fill and inner gradient in one pass, with vello's own blurred-rectangle formula
91 lines
3.0 KiB
WebGPU Shading Language
91 lines
3.0 KiB
WebGPU Shading Language
// the ring of candles around the cd, one candle per angle step, each a line from the
|
|
// cover's edge outward as far as its level reaches, with a glow under the loud ones and
|
|
// a flame at the tip of the loudest. island.rs fills the values:
|
|
// 0: centre, inner radius, candles, levels given
|
|
// 1: reach at full level, glow width, body width, flame width
|
|
// 2: glow colour, 3: body colour, 4: flame colour (plain, 0 to 1)
|
|
// 5-15: levels for half the ring (the other half mirrors it), already eased, 0 to 1
|
|
const TAU: f32 = 6.28318530718;
|
|
|
|
fn level(step: u32, half: u32) -> f32 {
|
|
let count = u32(values[0].w);
|
|
|
|
if count == 0u {
|
|
return 0.0;
|
|
}
|
|
|
|
let index = min(step * count / max(half, 1u), count - 1u);
|
|
|
|
return values[5u + index / 4u][index % 4u];
|
|
}
|
|
|
|
// distance from p to the line from `start` to `end` along `direction`, out from the centre
|
|
fn line(p: vec2<f32>, direction: vec2<f32>, start: f32, end: f32) -> f32 {
|
|
let along = dot(p, direction);
|
|
let across = dot(p, vec2<f32>(-direction.y, direction.x));
|
|
|
|
return length(vec2<f32>(across, along - clamp(along, start, end)));
|
|
}
|
|
|
|
// how much of a pixel a round-capped stroke `width` wide covers, at distance d from its line
|
|
fn stroke(d: f32, width: f32, pixel: f32) -> f32 {
|
|
return clamp((width / 2.0 - d) / pixel + 0.5, 0.0, 1.0);
|
|
}
|
|
|
|
fn over(top: vec4<f32>, under: vec4<f32>) -> vec4<f32> {
|
|
return top + under * (1.0 - top.a);
|
|
}
|
|
|
|
@fragment
|
|
fn main(@location(0) uv: vec2<f32>) -> @location(0) vec4<f32> {
|
|
let ring = values[0];
|
|
let widths = values[1];
|
|
let candles = u32(ring.z);
|
|
let half = candles / 2u;
|
|
let inner = ring.y;
|
|
|
|
let p = uv * size - vec2<f32>(ring.x);
|
|
let pixel = max(fwidth(p.x), 1e-4);
|
|
|
|
// the nearest candle by angle, 0 at the top going clockwise, and its neighbours
|
|
let angle = atan2(p.x, -p.y);
|
|
let nearest = i32(round(select(angle, angle + TAU, angle < 0.0) / TAU * f32(candles)));
|
|
|
|
var glow = 0.0;
|
|
var body = 0.0;
|
|
var flame = 0.0;
|
|
|
|
for (var offset = -1; offset <= 1; offset++) {
|
|
let index = u32((nearest + offset + i32(candles)) % i32(candles));
|
|
let step = select(candles - 1u - index, index, index < half);
|
|
let loud = level(step, half);
|
|
|
|
let a = f32(index) / f32(candles) * TAU;
|
|
let direction = vec2<f32>(sin(a), -cos(a));
|
|
let reach = inner + 3.0 + loud * widths.x;
|
|
|
|
let d = line(p, direction, inner, reach);
|
|
|
|
body = max(body, stroke(d, widths.z, pixel));
|
|
|
|
if loud > 0.25 {
|
|
glow = max(glow, stroke(d, widths.y, pixel));
|
|
}
|
|
|
|
if loud > 0.45 {
|
|
flame = max(flame, stroke(line(p, direction, reach - 0.5, reach), widths.w, pixel));
|
|
}
|
|
}
|
|
|
|
var color = vec4<f32>(values[2].rgb * values[2].a, values[2].a) * glow;
|
|
|
|
color = over(vec4<f32>(values[3].rgb * values[3].a, values[3].a) * body, color);
|
|
color = over(vec4<f32>(values[4].rgb * values[4].a, values[4].a) * flame, color);
|
|
|
|
if color.a <= 0.0 {
|
|
return vec4<f32>(0.0);
|
|
}
|
|
|
|
return vec4<f32>(color.rgb / color.a, color.a);
|
|
}
|