This commit is contained in:
Andras Schmelczer 2026-05-17 14:12:01 +01:00
commit 80ed37298b
73 changed files with 3619 additions and 1295 deletions

View file

@ -1,8 +1,12 @@
struct Settings {
eraserRadiusSquared: f32,
lineDistanceEpsilon: f32,
clearRed: f32,
clearGreen: f32,
clearBlue: f32,
clearAlpha: f32,
padding0: f32,
padding1: f32,
padding2: f32,
};
@group(1) @binding(0) var<uniform> settings: Settings;
@ -10,8 +14,8 @@ struct Settings {
struct VertexOutput {
@builtin(position) position: vec4<f32>,
@location(0) screenPosition: vec2<f32>,
@location(1) start: vec2<f32>,
@location(2) end: vec2<f32>
@location(1) @interpolate(flat) start: vec2<f32>,
@location(2) @interpolate(flat) end: vec2<f32>
}
struct EraserTextureTargets {
@ -20,12 +24,20 @@ struct EraserTextureTargets {
@location(2) trail: vec4<f32>,
}
struct EraserCombinedTargets {
@location(0) mask: vec4<f32>,
@location(1) source: vec4<f32>,
@location(2) influence: vec4<f32>,
@location(3) trail: vec4<f32>,
}
@vertex
fn vertex(
@location(0) screenPosition: vec2<f32>,
@location(1) @interpolate(flat) start: vec2<f32>,
@location(2) @interpolate(flat) end: vec2<f32>
@builtin(vertex_index) vertexIndex: u32,
@location(0) start: vec2<f32>,
@location(1) end: vec2<f32>
) -> VertexOutput {
let screenPosition = segment_vertex_position(vertexIndex, start, end, sqrt(settings.eraserRadiusSquared));
let uv = screenPosition / state.size;
let position = vec2(uv.x * 2.0 - 1.0, 1.0 - uv.y * 2.0);
return VertexOutput(vec4(position, 0.0, 1.0), screenPosition, start, end);
@ -34,30 +46,53 @@ fn vertex(
@fragment
fn fragment(
@location(0) screenPosition: vec2<f32>,
@location(1) start: vec2<f32>,
@location(2) end: vec2<f32>
@location(1) @interpolate(flat) start: vec2<f32>,
@location(2) @interpolate(flat) end: vec2<f32>
) -> @location(0) vec4<f32> {
if shouldDiscardEraserFragment(screenPosition, start, end) {
discard;
}
return vec4<f32>(0.0, 0.0, 0.0, 0.0);
return getEraserClearValue();
}
@fragment
fn fragmentMrt(
@location(0) screenPosition: vec2<f32>,
@location(1) start: vec2<f32>,
@location(2) end: vec2<f32>
@location(1) @interpolate(flat) start: vec2<f32>,
@location(2) @interpolate(flat) end: vec2<f32>
) -> EraserTextureTargets {
if shouldDiscardEraserFragment(screenPosition, start, end) {
discard;
}
let cleared = vec4<f32>(0.0, 0.0, 0.0, 0.0);
let cleared = getEraserClearValue();
return EraserTextureTargets(cleared, cleared, cleared);
}
@fragment
fn fragmentCombined(
@location(0) screenPosition: vec2<f32>,
@location(1) @interpolate(flat) start: vec2<f32>,
@location(2) @interpolate(flat) end: vec2<f32>
) -> EraserCombinedTargets {
if shouldDiscardEraserFragment(screenPosition, start, end) {
discard;
}
let cleared = getEraserClearValue();
return EraserCombinedTargets(cleared, cleared, cleared, cleared);
}
fn getEraserClearValue() -> vec4<f32> {
return vec4<f32>(
settings.clearRed,
settings.clearGreen,
settings.clearBlue,
settings.clearAlpha
);
}
fn shouldDiscardEraserFragment(
screenPosition: vec2<f32>,
start: vec2<f32>,
@ -71,7 +106,7 @@ fn distanceSquaredFromLine(position: vec2<f32>, start: vec2<f32>, end: vec2<f32>
let direction = end - start;
let denominator = dot(direction, direction);
if denominator <= 0.0001 {
if denominator <= settings.lineDistanceEpsilon {
return dot(pa, pa);
}
@ -79,3 +114,34 @@ fn distanceSquaredFromLine(position: vec2<f32>, start: vec2<f32>, end: vec2<f32>
let nearestOffset = pa - direction * q;
return dot(nearestOffset, nearestOffset);
}
fn segment_vertex_position(
vertexIndex: u32,
start: vec2<f32>,
end: vec2<f32>,
radius: f32
) -> vec2<f32> {
let directionVector = end - start;
let segmentLength = length(directionVector);
var direction = vec2<f32>(1.0, 0.0);
if segmentLength > 0.0 {
direction = directionVector / segmentLength;
}
let perpendicular = vec2<f32>(direction.y, -direction.x);
let corner = segment_vertex_corner(vertexIndex % 6u);
let center = mix(start, end, (corner.x + 1.0) * 0.5);
return center + direction * corner.x * radius + perpendicular * corner.y * radius;
}
fn segment_vertex_corner(index: u32) -> vec2<f32> {
if index == 0u {
return vec2<f32>(-1.0, 1.0);
}
if index == 1u || index == 3u {
return vec2<f32>(-1.0, -1.0);
}
if index == 2u || index == 4u {
return vec2<f32>(1.0, 1.0);
}
return vec2<f32>(1.0, -1.0);
}