Rework garden simulation pipelines
This commit is contained in:
parent
c2efb33683
commit
018f8c9d4d
67 changed files with 6499 additions and 1682 deletions
|
|
@ -1,6 +1,18 @@
|
|||
const SEGMENT_LENGTH_EPSILON: f32 = 0.0001;
|
||||
|
||||
struct Settings {
|
||||
brushSize: f32,
|
||||
brushSizeVariation: f32
|
||||
brushRadius: f32,
|
||||
brushRadiusSquared: f32,
|
||||
// padding to 16-byte alignment for the following vec4
|
||||
_pad0: f32,
|
||||
_pad1: f32,
|
||||
brushValue: vec4<f32>,
|
||||
brushGrainNoiseScale: f32,
|
||||
brushGrainNoiseOffsetX: f32,
|
||||
brushGrainNoiseOffsetY: f32,
|
||||
brushDiscardThreshold: f32,
|
||||
brushGrainMinStrength: f32,
|
||||
brushGrainMaxStrength: f32,
|
||||
};
|
||||
|
||||
@group(1) @binding(0) var<uniform> settings: Settings;
|
||||
|
|
@ -8,41 +20,100 @@ 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) direction: vec2<f32>,
|
||||
@location(3) @interpolate(flat) inverseLengthSquared: f32,
|
||||
}
|
||||
|
||||
struct BrushTargets {
|
||||
@location(0) source: 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 direction = end - start;
|
||||
let denominator = dot(direction, direction);
|
||||
var inverseLengthSquared = 0.0;
|
||||
var normalizedDirection = vec2<f32>(1.0, 0.0);
|
||||
if denominator > SEGMENT_LENGTH_EPSILON {
|
||||
inverseLengthSquared = 1.0 / denominator;
|
||||
normalizedDirection = direction * inverseSqrt(denominator);
|
||||
}
|
||||
let screenPosition = segment_vertex_position(vertexIndex, start, end, normalizedDirection, settings.brushRadius);
|
||||
let uv = screenPosition / state.size;
|
||||
let position = uv * 2.0 - 1.0;
|
||||
return VertexOutput(vec4(position, 0.0, 1.0), screenPosition, start, end);
|
||||
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, direction, inverseLengthSquared);
|
||||
}
|
||||
|
||||
@fragment
|
||||
fn fragment(
|
||||
@location(0) screenPosition: vec2<f32>,
|
||||
@location(1) start: vec2<f32>,
|
||||
@location(2) end: vec2<f32>
|
||||
) -> @location(0) vec4<f32> {
|
||||
var distance = distanceFromLine(screenPosition, start, end);
|
||||
let noise = textureSample(noise, noiseSampler, screenPosition / state.size / 50);
|
||||
distance += noise.r * settings.brushSizeVariation;
|
||||
@location(1) @interpolate(flat) start: vec2<f32>,
|
||||
@location(2) @interpolate(flat) direction: vec2<f32>,
|
||||
@location(3) @interpolate(flat) inverseLengthSquared: f32
|
||||
) -> BrushTargets {
|
||||
let strength = brushStrength(screenPosition, start, direction, inverseLengthSquared);
|
||||
|
||||
if(distance > settings.brushSize) {
|
||||
discard;
|
||||
}
|
||||
if(strength < settings.brushDiscardThreshold) {
|
||||
discard;
|
||||
}
|
||||
|
||||
return vec4(0, 0, 0, 1);
|
||||
let color = brushOutput(strength);
|
||||
return BrushTargets(color);
|
||||
}
|
||||
|
||||
fn distanceFromLine(position: vec2<f32>, start: vec2<f32>, end: vec2<f32>) -> f32 {
|
||||
let pa = position - start;
|
||||
let direction = end - start;
|
||||
let q = clamp(dot(pa, direction) / dot(direction, direction), 0, 1);
|
||||
return length(pa - direction * q);
|
||||
fn brushStrength(
|
||||
screenPosition: vec2<f32>,
|
||||
start: vec2<f32>,
|
||||
direction: vec2<f32>,
|
||||
inverseLengthSquared: f32
|
||||
) -> f32 {
|
||||
let distanceSquared = distance_squared_from_segment(
|
||||
screenPosition,
|
||||
start,
|
||||
direction,
|
||||
inverseLengthSquared
|
||||
);
|
||||
if distanceSquared > settings.brushRadiusSquared {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
let maxGrainStrength = max(settings.brushGrainMinStrength, settings.brushGrainMaxStrength);
|
||||
if maxGrainStrength < settings.brushDiscardThreshold {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
// smoothstep(0.35, 1.0, sqrt(d²/r²)) reparameterized to squared distance:
|
||||
// squaring the edges gives smoothstep(0.1225·r², r², d²), avoiding the sqrt.
|
||||
let safeRadiusSquared = max(settings.brushRadiusSquared, 0.0001);
|
||||
let feather = 1.0 - smoothstep(0.1225 * safeRadiusSquared, safeRadiusSquared, distanceSquared);
|
||||
if feather <= 0.0 {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
if settings.brushGrainMinStrength == settings.brushGrainMaxStrength {
|
||||
return settings.brushGrainMinStrength * feather;
|
||||
}
|
||||
|
||||
let grainNoise = textureSampleLevel(
|
||||
noise,
|
||||
noiseSampler,
|
||||
screenPosition * settings.brushGrainNoiseScale +
|
||||
vec2(settings.brushGrainNoiseOffsetX, settings.brushGrainNoiseOffsetY),
|
||||
0.0
|
||||
).r;
|
||||
let grainStrength = mix(
|
||||
settings.brushGrainMinStrength,
|
||||
settings.brushGrainMaxStrength,
|
||||
grainNoise
|
||||
);
|
||||
return grainStrength * feather;
|
||||
}
|
||||
|
||||
fn brushOutput(strength: f32) -> vec4<f32> {
|
||||
return vec4(settings.brushValue.rgb * strength, settings.brushValue.a * strength);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue