Merge pull request #2055 from matze/perceptual-gradient
Compute gradients in Oklab color space
This commit is contained in:
commit
89d9c45d25
17 changed files with 705 additions and 485 deletions
|
|
@ -1 +1,2 @@
|
|||
too-many-arguments-threshold = 20
|
||||
enum-variant-name-threshold = 10
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ bitflags.workspace = true
|
|||
log.workspace = true
|
||||
thiserror.workspace = true
|
||||
twox-hash.workspace = true
|
||||
num-traits.workspace = true
|
||||
|
||||
palette.workspace = true
|
||||
palette.optional = true
|
||||
|
|
|
|||
|
|
@ -1,32 +1,72 @@
|
|||
use crate::{Point, Rectangle, Vector};
|
||||
use std::f32::consts::PI;
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
use std::f32::consts::{FRAC_PI_2, PI};
|
||||
use std::ops::RangeInclusive;
|
||||
|
||||
/// Degrees
|
||||
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
|
||||
pub struct Degrees(pub f32);
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
/// Radians
|
||||
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
|
||||
pub struct Radians(pub f32);
|
||||
|
||||
impl Radians {
|
||||
/// The range of radians of a circle.
|
||||
pub const RANGE: RangeInclusive<Radians> = Radians(0.0)..=Radians(2.0 * PI);
|
||||
}
|
||||
|
||||
impl From<Degrees> for Radians {
|
||||
fn from(degrees: Degrees) -> Self {
|
||||
Radians(degrees.0 * PI / 180.0)
|
||||
Self(degrees.0 * PI / 180.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<f32> for Radians {
|
||||
fn from(radians: f32) -> Self {
|
||||
Self(radians)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u8> for Radians {
|
||||
fn from(radians: u8) -> Self {
|
||||
Self(f32::from(radians))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Radians> for f64 {
|
||||
fn from(radians: Radians) -> Self {
|
||||
Self::from(radians.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl num_traits::FromPrimitive for Radians {
|
||||
fn from_i64(n: i64) -> Option<Self> {
|
||||
Some(Self(n as f32))
|
||||
}
|
||||
|
||||
fn from_u64(n: u64) -> Option<Self> {
|
||||
Some(Self(n as f32))
|
||||
}
|
||||
|
||||
fn from_f64(n: f64) -> Option<Self> {
|
||||
Some(Self(n as f32))
|
||||
}
|
||||
}
|
||||
|
||||
impl Radians {
|
||||
/// Calculates the line in which the [`Angle`] intercepts the `bounds`.
|
||||
pub fn to_distance(&self, bounds: &Rectangle) -> (Point, Point) {
|
||||
let v1 = Vector::new(f32::cos(self.0), f32::sin(self.0));
|
||||
let angle = self.0 - FRAC_PI_2;
|
||||
let r = Vector::new(f32::cos(angle), f32::sin(angle));
|
||||
|
||||
let distance_to_rect = f32::min(
|
||||
f32::abs((bounds.y - bounds.center().y) / v1.y),
|
||||
f32::abs(((bounds.x + bounds.width) - bounds.center().x) / v1.x),
|
||||
let distance_to_rect = f32::max(
|
||||
f32::abs(r.x * bounds.width / 2.0),
|
||||
f32::abs(r.y * bounds.height / 2.0),
|
||||
);
|
||||
|
||||
let start = bounds.center() + v1 * distance_to_rect;
|
||||
let end = bounds.center() - v1 * distance_to_rect;
|
||||
let start = bounds.center() - r * distance_to_rect;
|
||||
let end = bounds.center() + r * distance_to_rect;
|
||||
|
||||
(start, end)
|
||||
}
|
||||
|
|
|
|||
8
examples/gradient/Cargo.toml
Normal file
8
examples/gradient/Cargo.toml
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
[package]
|
||||
name = "gradient"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
publish = false
|
||||
|
||||
[dependencies]
|
||||
iced = { path = "../.." }
|
||||
99
examples/gradient/src/main.rs
Normal file
99
examples/gradient/src/main.rs
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
use iced::gradient;
|
||||
use iced::widget::{column, container, horizontal_space, row, slider, text};
|
||||
use iced::{
|
||||
Alignment, Background, Color, Element, Length, Radians, Sandbox, Settings,
|
||||
};
|
||||
|
||||
pub fn main() -> iced::Result {
|
||||
Gradient::run(Settings::default())
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
struct Gradient {
|
||||
start: Color,
|
||||
end: Color,
|
||||
angle: Radians,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
enum Message {
|
||||
StartChanged(Color),
|
||||
EndChanged(Color),
|
||||
AngleChanged(Radians),
|
||||
}
|
||||
|
||||
impl Sandbox for Gradient {
|
||||
type Message = Message;
|
||||
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
start: Color::WHITE,
|
||||
end: Color::new(0.0, 0.0, 1.0, 1.0),
|
||||
angle: Radians(0.0),
|
||||
}
|
||||
}
|
||||
|
||||
fn title(&self) -> String {
|
||||
String::from("Gradient")
|
||||
}
|
||||
|
||||
fn update(&mut self, message: Message) {
|
||||
match message {
|
||||
Message::StartChanged(color) => self.start = color,
|
||||
Message::EndChanged(color) => self.end = color,
|
||||
Message::AngleChanged(angle) => self.angle = angle,
|
||||
}
|
||||
}
|
||||
|
||||
fn view(&self) -> Element<Message> {
|
||||
let Self { start, end, angle } = *self;
|
||||
|
||||
let gradient_box = container(horizontal_space(Length::Fill))
|
||||
.width(Length::Fill)
|
||||
.height(Length::Fill)
|
||||
.style(move |_: &_| {
|
||||
let gradient = gradient::Linear::new(angle)
|
||||
.add_stop(0.0, start)
|
||||
.add_stop(1.0, end)
|
||||
.into();
|
||||
|
||||
container::Appearance {
|
||||
background: Some(Background::Gradient(gradient)),
|
||||
..Default::default()
|
||||
}
|
||||
});
|
||||
|
||||
let angle_picker = row![
|
||||
text("Angle").width(64),
|
||||
slider(Radians::RANGE, self.angle, Message::AngleChanged)
|
||||
.step(0.01)
|
||||
]
|
||||
.spacing(8)
|
||||
.padding(8)
|
||||
.align_items(Alignment::Center);
|
||||
|
||||
column![
|
||||
color_picker("Start", self.start).map(Message::StartChanged),
|
||||
color_picker("End", self.end).map(Message::EndChanged),
|
||||
angle_picker,
|
||||
gradient_box
|
||||
]
|
||||
.into()
|
||||
}
|
||||
}
|
||||
|
||||
fn color_picker(label: &str, color: Color) -> Element<'_, Color> {
|
||||
row![
|
||||
text(label).width(64),
|
||||
slider(0.0..=1.0, color.r, move |r| { Color { r, ..color } })
|
||||
.step(0.01),
|
||||
slider(0.0..=1.0, color.g, move |g| { Color { g, ..color } })
|
||||
.step(0.01),
|
||||
slider(0.0..=1.0, color.b, move |b| { Color { b, ..color } })
|
||||
.step(0.01),
|
||||
]
|
||||
.spacing(8)
|
||||
.padding(8)
|
||||
.align_items(Alignment::Center)
|
||||
.into()
|
||||
}
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
use crate::graphics::color;
|
||||
use crate::graphics::gradient;
|
||||
use crate::quad::{self, Quad};
|
||||
use crate::Buffer;
|
||||
|
|
@ -78,7 +79,23 @@ impl Pipeline {
|
|||
device.create_shader_module(wgpu::ShaderModuleDescriptor {
|
||||
label: Some("iced_wgpu.quad.gradient.shader"),
|
||||
source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed(
|
||||
include_str!("../shader/quad.wgsl"),
|
||||
if color::GAMMA_CORRECTION {
|
||||
concat!(
|
||||
include_str!("../shader/quad.wgsl"),
|
||||
"\n",
|
||||
include_str!("../shader/quad/gradient.wgsl"),
|
||||
"\n",
|
||||
include_str!("../shader/color/oklab.wgsl")
|
||||
)
|
||||
} else {
|
||||
concat!(
|
||||
include_str!("../shader/quad.wgsl"),
|
||||
"\n",
|
||||
include_str!("../shader/quad/gradient.wgsl"),
|
||||
"\n",
|
||||
include_str!("../shader/color/linear_rgb.wgsl")
|
||||
)
|
||||
},
|
||||
)),
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -72,7 +72,11 @@ impl Pipeline {
|
|||
device.create_shader_module(wgpu::ShaderModuleDescriptor {
|
||||
label: Some("iced_wgpu.quad.solid.shader"),
|
||||
source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed(
|
||||
include_str!("../shader/quad.wgsl"),
|
||||
concat!(
|
||||
include_str!("../shader/quad.wgsl"),
|
||||
"\n",
|
||||
include_str!("../shader/quad/solid.wgsl"),
|
||||
),
|
||||
)),
|
||||
});
|
||||
|
||||
|
|
|
|||
3
wgpu/src/shader/color/linear_rgb.wgsl
Normal file
3
wgpu/src/shader/color/linear_rgb.wgsl
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
fn interpolate_color(from_: vec4<f32>, to_: vec4<f32>, factor: f32) -> vec4<f32> {
|
||||
return mix(from_, to_, factor);
|
||||
}
|
||||
26
wgpu/src/shader/color/oklab.wgsl
Normal file
26
wgpu/src/shader/color/oklab.wgsl
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
const to_lms = mat3x4<f32>(
|
||||
vec4<f32>(0.4121656120, 0.2118591070, 0.0883097947, 0.0),
|
||||
vec4<f32>(0.5362752080, 0.6807189584, 0.2818474174, 0.0),
|
||||
vec4<f32>(0.0514575653, 0.1074065790, 0.6302613616, 0.0),
|
||||
);
|
||||
|
||||
const to_rgb = mat3x4<f32>(
|
||||
vec4<f32>( 4.0767245293, -3.3072168827, 0.2307590544, 0.0),
|
||||
vec4<f32>(-1.2681437731, 2.6093323231, -0.3411344290, 0.0),
|
||||
vec4<f32>(-0.0041119885, -0.7034763098, 1.7068625689, 0.0),
|
||||
);
|
||||
|
||||
fn interpolate_color(from_: vec4<f32>, to_: vec4<f32>, factor: f32) -> vec4<f32> {
|
||||
// To Oklab
|
||||
let lms_a = pow(from_ * to_lms, vec3<f32>(1.0 / 3.0, 1.0 / 3.0, 1.0 / 3.0));
|
||||
let lms_b = pow(to_ * to_lms, vec3<f32>(1.0 / 3.0, 1.0 / 3.0, 1.0 / 3.0));
|
||||
let mixed = mix(lms_a, lms_b, factor);
|
||||
|
||||
// Back to linear RGB
|
||||
var color = to_rgb * (mixed * mixed * mixed);
|
||||
|
||||
// Alpha interpolation
|
||||
color.a = mix(from_.a, to_.a, factor);
|
||||
|
||||
return color;
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
205
wgpu/src/shader/quad/gradient.wgsl
Normal file
205
wgpu/src/shader/quad/gradient.wgsl
Normal file
|
|
@ -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);
|
||||
}
|
||||
99
wgpu/src/shader/quad/solid.wgsl
Normal file
99
wgpu/src/shader/quad/solid.wgsl
Normal file
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -3,163 +3,3 @@ struct Globals {
|
|||
}
|
||||
|
||||
@group(0) @binding(0) var<uniform> globals: Globals;
|
||||
|
||||
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) position: vec2<f32>,
|
||||
@location(1) color: vec4<f32>,
|
||||
}
|
||||
|
||||
struct SolidVertexOutput {
|
||||
@builtin(position) position: vec4<f32>,
|
||||
@location(0) color: vec4<f32>,
|
||||
}
|
||||
|
||||
@vertex
|
||||
fn solid_vs_main(input: SolidVertexInput) -> SolidVertexOutput {
|
||||
var out: SolidVertexOutput;
|
||||
|
||||
out.color = input.color;
|
||||
out.position = globals.transform * vec4<f32>(input.position, 0.0, 1.0);
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
@fragment
|
||||
fn solid_fs_main(input: SolidVertexOutput) -> @location(0) vec4<f32> {
|
||||
return input.color;
|
||||
}
|
||||
|
||||
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>,
|
||||
}
|
||||
|
||||
struct GradientVertexOutput {
|
||||
@builtin(position) position: vec4<f32>,
|
||||
@location(0) raw_position: 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>,
|
||||
}
|
||||
|
||||
@vertex
|
||||
fn gradient_vs_main(input: GradientVertexInput) -> GradientVertexOutput {
|
||||
var output: GradientVertexOutput;
|
||||
|
||||
output.position = globals.transform * vec4<f32>(input.v_pos, 0.0, 1.0);
|
||||
output.raw_position = input.v_pos;
|
||||
output.colors_1 = input.colors_1;
|
||||
output.colors_2 = input.colors_2;
|
||||
output.colors_3 = input.colors_3;
|
||||
output.colors_4 = input.colors_4;
|
||||
output.offsets = input.offsets;
|
||||
output.direction = input.direction;
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
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,
|
||||
);
|
||||
|
||||
var last_index = 7;
|
||||
for (var i: i32 = 0; i <= 7; i++) {
|
||||
if (offsets[i] >= 1.0) {
|
||||
last_index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return gradient(input.raw_position, input.direction, colors, offsets, last_index);
|
||||
}
|
||||
|
|
|
|||
134
wgpu/src/shader/triangle/gradient.wgsl
Normal file
134
wgpu/src/shader/triangle/gradient.wgsl
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
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>,
|
||||
}
|
||||
|
||||
struct GradientVertexOutput {
|
||||
@builtin(position) position: vec4<f32>,
|
||||
@location(0) raw_position: 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>,
|
||||
}
|
||||
|
||||
@vertex
|
||||
fn gradient_vs_main(input: GradientVertexInput) -> GradientVertexOutput {
|
||||
var output: GradientVertexOutput;
|
||||
|
||||
output.position = globals.transform * vec4<f32>(input.v_pos, 0.0, 1.0);
|
||||
output.raw_position = input.v_pos;
|
||||
output.colors_1 = input.colors_1;
|
||||
output.colors_2 = input.colors_2;
|
||||
output.colors_3 = input.colors_3;
|
||||
output.colors_4 = input.colors_4;
|
||||
output.offsets = input.offsets;
|
||||
output.direction = input.direction;
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/// 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,
|
||||
);
|
||||
|
||||
var last_index = 7;
|
||||
for (var i: i32 = 0; i <= 7; i++) {
|
||||
if (offsets[i] >= 1.0) {
|
||||
last_index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return gradient(input.raw_position, input.direction, colors, offsets, last_index);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
fn random(coords: vec2<f32>) -> f32 {
|
||||
return fract(sin(dot(coords, vec2(12.9898,78.233))) * 43758.5453);
|
||||
}
|
||||
24
wgpu/src/shader/triangle/solid.wgsl
Normal file
24
wgpu/src/shader/triangle/solid.wgsl
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
struct SolidVertexInput {
|
||||
@location(0) position: vec2<f32>,
|
||||
@location(1) color: vec4<f32>,
|
||||
}
|
||||
|
||||
struct SolidVertexOutput {
|
||||
@builtin(position) position: vec4<f32>,
|
||||
@location(0) color: vec4<f32>,
|
||||
}
|
||||
|
||||
@vertex
|
||||
fn solid_vs_main(input: SolidVertexInput) -> SolidVertexOutput {
|
||||
var out: SolidVertexOutput;
|
||||
|
||||
out.color = input.color;
|
||||
out.position = globals.transform * vec4<f32>(input.position, 0.0, 1.0);
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
@fragment
|
||||
fn solid_fs_main(input: SolidVertexOutput) -> @location(0) vec4<f32> {
|
||||
return input.color;
|
||||
}
|
||||
|
|
@ -487,8 +487,10 @@ mod solid {
|
|||
device.create_shader_module(wgpu::ShaderModuleDescriptor {
|
||||
label: Some("iced_wgpu.triangle.solid.shader"),
|
||||
source: wgpu::ShaderSource::Wgsl(
|
||||
std::borrow::Cow::Borrowed(include_str!(
|
||||
"shader/triangle.wgsl"
|
||||
std::borrow::Cow::Borrowed(concat!(
|
||||
include_str!("shader/triangle.wgsl"),
|
||||
"\n",
|
||||
include_str!("shader/triangle/solid.wgsl"),
|
||||
)),
|
||||
),
|
||||
});
|
||||
|
|
@ -537,6 +539,7 @@ mod solid {
|
|||
}
|
||||
|
||||
mod gradient {
|
||||
use crate::graphics::color;
|
||||
use crate::graphics::mesh;
|
||||
use crate::graphics::Antialiasing;
|
||||
use crate::triangle;
|
||||
|
|
@ -633,9 +636,31 @@ mod gradient {
|
|||
device.create_shader_module(wgpu::ShaderModuleDescriptor {
|
||||
label: Some("iced_wgpu.triangle.gradient.shader"),
|
||||
source: wgpu::ShaderSource::Wgsl(
|
||||
std::borrow::Cow::Borrowed(include_str!(
|
||||
"shader/triangle.wgsl"
|
||||
)),
|
||||
std::borrow::Cow::Borrowed(
|
||||
if color::GAMMA_CORRECTION {
|
||||
concat!(
|
||||
include_str!("shader/triangle.wgsl"),
|
||||
"\n",
|
||||
include_str!(
|
||||
"shader/triangle/gradient.wgsl"
|
||||
),
|
||||
"\n",
|
||||
include_str!("shader/color/oklab.wgsl")
|
||||
)
|
||||
} else {
|
||||
concat!(
|
||||
include_str!("shader/triangle.wgsl"),
|
||||
"\n",
|
||||
include_str!(
|
||||
"shader/triangle/gradient.wgsl"
|
||||
),
|
||||
"\n",
|
||||
include_str!(
|
||||
"shader/color/linear_rgb.wgsl"
|
||||
)
|
||||
)
|
||||
},
|
||||
),
|
||||
),
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -137,8 +137,8 @@ where
|
|||
}
|
||||
|
||||
/// Sets the step size of the [`Slider`].
|
||||
pub fn step(mut self, step: T) -> Self {
|
||||
self.step = step;
|
||||
pub fn step(mut self, step: impl Into<T>) -> Self {
|
||||
self.step = step.into();
|
||||
self
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue