struct GradientUniforms { transform: mat4x4, //xy = start, wz = end position: vec4, //x = start stop, y = end stop, zw = padding stop_range: vec4, } struct Stop { color: vec4, offset: f32, }; @group(0) @binding(0) var uniforms: GradientUniforms; @group(0) @binding(1) var color_stops: array; struct VertexOutput { @builtin(position) position: vec4, @location(0) raw_position: vec2 } @vertex fn vs_main(@location(0) input: vec2) -> VertexOutput { var output: VertexOutput; output.position = uniforms.transform * vec4(input.xy, 0.0, 1.0); output.raw_position = input; return output; } //TODO: rewrite without branching @fragment fn fs_gradient(input: VertexOutput) -> @location(0) vec4 { 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; 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; }