feat: add particle visual runtime controls
Some checks failed
validate-scripts / validate-scripts (push) Has been cancelled
Some checks failed
validate-scripts / validate-scripts (push) Has been cancelled
This commit is contained in:
@@ -12,30 +12,54 @@ struct ShaderSource<BuiltIn::ParticleGpuValidationShader, gfx::Backend::Type::Op
|
||||
|
||||
out vec2 v_uv;
|
||||
out vec2 v_world;
|
||||
out vec2 v_particle_velocity;
|
||||
out float v_particle_valid;
|
||||
|
||||
layout (std140) uniform ParticleGpuValidationUBO {
|
||||
highp vec4 u_pass;
|
||||
highp vec4 u_marker;
|
||||
highp vec4 u_mask;
|
||||
highp vec4 u_visual;
|
||||
highp mat4 u_matrix;
|
||||
};
|
||||
|
||||
uniform sampler2D u_state_texture;
|
||||
uniform sampler2D u_field_texture;
|
||||
uniform sampler2D u_next_field_texture;
|
||||
|
||||
void main() {
|
||||
v_uv = a_pos;
|
||||
v_world = a_pos;
|
||||
v_particle_velocity = vec2(0.0);
|
||||
v_particle_valid = 1.0;
|
||||
if (u_pass.x < 1.5) {
|
||||
gl_Position = vec4(a_pos * 2.0 - 1.0, 0.0, 1.0);
|
||||
} else if (u_pass.x > 3.5) {
|
||||
// 40-03: a_pos is a static State texel index generated only when the
|
||||
// particle capacity changes. The position itself remains GPU-owned.
|
||||
vec2 stateUV = (a_pos + vec2(0.5)) / u_mask.xy;
|
||||
// 40-05: a_pos.x packs State texel X plus a static line endpoint.
|
||||
// State position, interpolated Field velocity, speed color and streak
|
||||
// orientation are all evaluated on GPU; the CPU never supplies live
|
||||
// particle position, direction, or color data.
|
||||
float endpoint = mod(a_pos.x, 2.0) * 2.0 - 1.0;
|
||||
vec2 stateIndex = vec2(floor(a_pos.x * 0.5), a_pos.y);
|
||||
vec2 stateUV = (stateIndex + vec2(0.5)) / u_mask.xy;
|
||||
vec2 position = texture(u_state_texture, stateUV).rg;
|
||||
vec4 field0 = texture(u_field_texture, position);
|
||||
vec4 field1 = texture(u_next_field_texture, position);
|
||||
vec2 velocity = mix(field0.rg * 2.0 - 1.0, field1.rg * 2.0 - 1.0, u_marker.x);
|
||||
bool valid = field0.a >= 0.5 && field1.a >= 0.5 && !any(isnan(velocity));
|
||||
vec2 mercatorVelocity = vec2(velocity.x, -velocity.y);
|
||||
float speed = length(mercatorVelocity);
|
||||
v_uv = position;
|
||||
v_world = position;
|
||||
gl_PointSize = 3.0;
|
||||
gl_Position = vec4(position * 2.0 - 1.0, 0.0, 1.0);
|
||||
v_particle_velocity = mercatorVelocity;
|
||||
v_particle_valid = valid && speed > 0.00001 ? 1.0 : 0.0;
|
||||
if (v_particle_valid < 0.5) {
|
||||
gl_Position = vec4(-2.0, -2.0, 0.0, 1.0);
|
||||
} else {
|
||||
vec2 direction = mercatorVelocity / speed;
|
||||
vec2 streakPosition = position + direction * endpoint * u_visual.y * 0.5;
|
||||
gl_Position = vec4(streakPosition * 2.0 - 1.0, 0.0, 1.0);
|
||||
}
|
||||
} else {
|
||||
// VALIDATION ONLY: a_pos is canonical z0 world geometry; u_matrix is
|
||||
// PaintParameters::matrixForTile() for the selected MapLibre world copy.
|
||||
@@ -47,6 +71,7 @@ void main() {
|
||||
highp vec4 u_pass;
|
||||
highp vec4 u_marker;
|
||||
highp vec4 u_mask;
|
||||
highp vec4 u_visual;
|
||||
highp mat4 u_matrix;
|
||||
};
|
||||
|
||||
@@ -57,6 +82,16 @@ uniform sampler2D u_next_field_texture;
|
||||
uniform sampler2D u_state_texture;
|
||||
in vec2 v_uv;
|
||||
in vec2 v_world;
|
||||
in vec2 v_particle_velocity;
|
||||
in float v_particle_valid;
|
||||
|
||||
vec3 particleSpeedColor(float speed) {
|
||||
float t = clamp(speed * 0.75, 0.0, 1.0);
|
||||
vec3 slow = vec3(0.10, 0.45, 1.00);
|
||||
vec3 medium = vec3(0.10, 0.95, 0.72);
|
||||
vec3 fast = vec3(1.00, 0.48, 0.12);
|
||||
return mix(mix(slow, medium, smoothstep(0.0, 0.55, t)), fast, smoothstep(0.45, 1.0, t));
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec4 previous = texture(u_previous, v_uv);
|
||||
@@ -88,7 +123,7 @@ void main() {
|
||||
// Formal Trail first fades the previous target. Its full-particle point
|
||||
// draw follows in the same target and consumes every State texel.
|
||||
if (u_mask.z > 0.5) {
|
||||
fragColor = vec4(previous.rgb * u_marker.w, 1.0);
|
||||
fragColor = vec4(previous.rgb * u_visual.w, 1.0);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -102,10 +137,12 @@ void main() {
|
||||
vec3 color = previous.rgb * u_marker.w + marker * vec3(0.08, 0.55, 1.0);
|
||||
fragColor = vec4(min(color, vec3(1.0)), 1.0);
|
||||
} else if (mode > 3.5) {
|
||||
// The vertex shader fetched this particle's State texel directly from
|
||||
// the current State write texture. Every point contributes one formal
|
||||
// particle draw; no CPU position data is involved per frame.
|
||||
fragColor = vec4(0.08, 0.55, 1.0, 1.0);
|
||||
if (v_particle_valid < 0.5) discard;
|
||||
float speed = length(v_particle_velocity);
|
||||
vec3 color = u_marker.y > 0.5 ? particleSpeedColor(speed) : vec3(0.08, 0.55, 1.0);
|
||||
// Trail targets are RGB accumulation buffers. Opacity changes source
|
||||
// energy only; it never alters State, Field textures, or time windows.
|
||||
fragColor = vec4(color * u_visual.x, 1.0);
|
||||
} else {
|
||||
// The formal RasterParticle layer displays the Trail texture written by
|
||||
// the current State -> Trail pass. Legacy ParticleValidation keeps its
|
||||
@@ -114,8 +151,9 @@ void main() {
|
||||
float ocean = texture(u_mask_texture, v_world).a;
|
||||
if (u_pass.z > 0.5 && ocean < 0.5) discard;
|
||||
if (u_mask.z > 0.5) {
|
||||
float alpha = clamp(intensity * 1.25, 0.0, 0.96);
|
||||
fragColor = vec4(previous.rgb * alpha, alpha);
|
||||
vec3 color = previous.rgb * u_visual.z;
|
||||
float alpha = clamp(max(color.r, max(color.g, color.b)), 0.0, 0.96);
|
||||
fragColor = vec4(color, alpha);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -10,9 +10,12 @@ struct alignas(16) ParticleGpuValidationUBO {
|
||||
std::array<float, 4> pass;
|
||||
std::array<float, 4> marker;
|
||||
std::array<float, 4> mask;
|
||||
// Formal RasterParticle visual controls: particle opacity, canonical
|
||||
// streak length, trail opacity, and Trail A/B fade multiplier.
|
||||
std::array<float, 4> visual;
|
||||
std::array<float, 16> matrix;
|
||||
};
|
||||
static_assert(sizeof(ParticleGpuValidationUBO) == 7 * 16);
|
||||
static_assert(sizeof(ParticleGpuValidationUBO) == 8 * 16);
|
||||
|
||||
} // namespace shaders
|
||||
} // namespace mbgl
|
||||
|
||||
@@ -18,6 +18,18 @@ public:
|
||||
void setTimeMix(float);
|
||||
uint32_t getParticleCount() const;
|
||||
void setParticleCount(uint32_t);
|
||||
float getLineWidth() const;
|
||||
void setLineWidth(float);
|
||||
float getParticleLength() const;
|
||||
void setParticleLength(float);
|
||||
float getParticleOpacity() const;
|
||||
void setParticleOpacity(float);
|
||||
float getTrailOpacity() const;
|
||||
void setTrailOpacity(float);
|
||||
float getTrailFade() const;
|
||||
void setTrailFade(float);
|
||||
bool isSpeedColorEnabled() const;
|
||||
void setSpeedColorEnabled(bool);
|
||||
class Impl;
|
||||
const Impl& impl() const;
|
||||
Mutable<Impl> mutableImpl() const;
|
||||
|
||||
Reference in New Issue
Block a user