Adding Shimmer FX (#4923)

Sends a shimmer across the strip at defined (or random) intervals
Optional brightness modulators: sine or perlin noise
Can be used as an overlay to other effects.
This commit is contained in:
Damian Schneider
2025-09-24 20:49:40 +02:00
committed by GitHub
parent 7fe831c5e3
commit daa833f33d
2 changed files with 74 additions and 0 deletions

View File

@@ -4877,6 +4877,78 @@ uint16_t mode_FlowStripe(void) {
} // mode_FlowStripe()
static const char _data_FX_MODE_FLOWSTRIPE[] PROGMEM = "Flow Stripe@Hue speed,Effect speed;;!;pal=11";
/*
Shimmer effect: moves a gradient with optional modulators across the strip at a given interval, up to 60 seconds
It can be used as an overlay to other effects or standalone
by DedeHai (Damian Schneider), based on idea from @Charming-Lime (#4905)
*/
uint16_t mode_shimmer() {
if(!SEGENV.allocateData(sizeof(uint32_t))) { return mode_static(); }
uint32_t* lastTime = reinterpret_cast<uint32_t*>(SEGENV.data);
uint32_t radius = (SEGMENT.custom1 * SEGLEN >> 7) + 1; // [1, 2*SEGLEN+1] pixels
uint32_t traversalDistance = (SEGLEN + 2 * radius) << 8; // total subpixels to cross, 1 pixel = 256 subpixels
uint32_t traversalTime = 200 + (255 - SEGMENT.speed) * 80; // [200, 20600] ms
uint32_t speed = ((traversalDistance << 5) / traversalTime); // subpixels/512ms
int32_t position = static_cast<int32_t>(SEGENV.step); // current position in subpixels
uint16_t inputstate = (uint16_t(SEGMENT.intensity) << 8) | uint16_t(SEGMENT.custom1); // current user input state
// init
if (SEGENV.call == 0 || inputstate != SEGENV.aux1) {
position = -(radius << 8);
SEGENV.aux0 = 0; // aux0 is pause timer
*lastTime = strip.now;
SEGENV.aux1 = inputstate; // save user input state
}
if(SEGMENT.speed) {
uint32_t deltaTime = (strip.now - *lastTime) & 0x7F; // clamp to 127ms to avoid overflows. note: speed*deltaTime can still overflow for segments > ~10k pixels
*lastTime = strip.now;
if (SEGENV.aux0 > 0) {
SEGENV.aux0 = (SEGENV.aux0 > deltaTime) ? SEGENV.aux0 - deltaTime : 0;
} else {
// calculate movement step and update position
int32_t step = 1 + ((speed * deltaTime) >> 5); // subpixels moved this frame. note >>5 as speed is in subpixels/512ms
position += step;
int endposition = (SEGLEN + radius) << 8;
if (position > endposition) {
SEGENV.aux0 = SEGMENT.intensity * 236; // [0, 60180] ms pause
if(SEGMENT.check3) SEGENV.aux0 = hw_random(SEGENV.aux0 + 1000); // randomise interval, +1 second to affect low intensity values
position = -(radius << 8); // reset to start position (out of frame)
}
SEGENV.step = (uint32_t)position; // save back
}
if (SEGMENT.check2)
position = (SEGLEN << 8) - position; // invert position (and direction)
} else {
position = (SEGLEN << 7); // at speed=0, make it static in the center (this enables to use modulators only)
}
for (int i = 0; i < SEGLEN; i++) {
uint32_t dist = abs(position - (i << 8));
if (dist < (radius << 8)) {
uint32_t color = SEGMENT.color_from_palette(i * 255 / SEGLEN, false, false, 0);
uint8_t blend = dist / radius; // linear gradient note: dist is in subpixels, radius in pixels, result is [0, 255] since dist < radius*256
if (SEGMENT.custom2) {
uint8_t modVal; // modulation value
if (SEGMENT.check1) {
modVal = (sin16_t((i * SEGMENT.custom2 << 6) + (strip.now * SEGMENT.custom3 << 5)) >> 8) + 128; // sine modulation: regular "Zebra" stripes
} else {
modVal = perlin16((i * SEGMENT.custom2 << 7), strip.now * SEGMENT.custom3 << 5) >> 8; // perlin noise modulation
}
color = color_fade(color, modVal, true); // dim by modulator value
}
SEGMENT.setPixelColor(i, color_blend(color, SEGCOLOR(1), blend)); // blend to background color
} else {
SEGMENT.setPixelColor(i, SEGCOLOR(1));
}
}
return FRAMETIME;
}
static const char _data_FX_MODE_SHIMMER[] PROGMEM = "Shimmer@Speed,Interval,Size,Granular,Flow,Zebra,Reverse,Sporadic;Fx,Bg,Cx;!;1;pal=15,sx=220,ix=10,c2=0,c3=0";
#ifndef WLED_DISABLE_2D
///////////////////////////////////////////////////////////////////////////////
@@ -10814,6 +10886,7 @@ void WS2812FX::setupEffectData() {
addEffect(FX_MODE_FLOWSTRIPE, &mode_FlowStripe, _data_FX_MODE_FLOWSTRIPE);
addEffect(FX_MODE_WAVESINS, &mode_wavesins, _data_FX_MODE_WAVESINS);
addEffect(FX_MODE_ROCKTAVES, &mode_rocktaves, _data_FX_MODE_ROCKTAVES);
addEffect(FX_MODE_SHIMMER, &mode_shimmer, _data_FX_MODE_SHIMMER);
// --- 2D effects ---
#ifndef WLED_DISABLE_2D

View File

@@ -320,6 +320,7 @@ extern byte realtimeMode; // used in getMappedPixelIndex()
#define FX_MODE_DJLIGHT 159
#define FX_MODE_2DFUNKYPLANK 160
//#define FX_MODE_2DCENTERBARS 161
#define FX_MODE_SHIMMER 161 // gap fill, non SR 1D effect
#define FX_MODE_2DPULSER 162
#define FX_MODE_BLURZ 163
#define FX_MODE_2DDRIFT 164