Skip to content

Commit daa833f

Browse files
authored
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.
1 parent 7fe831c commit daa833f

File tree

2 files changed

+74
-0
lines changed

2 files changed

+74
-0
lines changed

wled00/FX.cpp

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4877,6 +4877,78 @@ uint16_t mode_FlowStripe(void) {
48774877
} // mode_FlowStripe()
48784878
static const char _data_FX_MODE_FLOWSTRIPE[] PROGMEM = "Flow Stripe@Hue speed,Effect speed;;!;pal=11";
48794879

4880+
/*
4881+
Shimmer effect: moves a gradient with optional modulators across the strip at a given interval, up to 60 seconds
4882+
It can be used as an overlay to other effects or standalone
4883+
by DedeHai (Damian Schneider), based on idea from @Charming-Lime (#4905)
4884+
*/
4885+
uint16_t mode_shimmer() {
4886+
if(!SEGENV.allocateData(sizeof(uint32_t))) { return mode_static(); }
4887+
uint32_t* lastTime = reinterpret_cast<uint32_t*>(SEGENV.data);
4888+
4889+
uint32_t radius = (SEGMENT.custom1 * SEGLEN >> 7) + 1; // [1, 2*SEGLEN+1] pixels
4890+
uint32_t traversalDistance = (SEGLEN + 2 * radius) << 8; // total subpixels to cross, 1 pixel = 256 subpixels
4891+
uint32_t traversalTime = 200 + (255 - SEGMENT.speed) * 80; // [200, 20600] ms
4892+
uint32_t speed = ((traversalDistance << 5) / traversalTime); // subpixels/512ms
4893+
int32_t position = static_cast<int32_t>(SEGENV.step); // current position in subpixels
4894+
uint16_t inputstate = (uint16_t(SEGMENT.intensity) << 8) | uint16_t(SEGMENT.custom1); // current user input state
4895+
4896+
// init
4897+
if (SEGENV.call == 0 || inputstate != SEGENV.aux1) {
4898+
position = -(radius << 8);
4899+
SEGENV.aux0 = 0; // aux0 is pause timer
4900+
*lastTime = strip.now;
4901+
SEGENV.aux1 = inputstate; // save user input state
4902+
}
4903+
4904+
if(SEGMENT.speed) {
4905+
uint32_t deltaTime = (strip.now - *lastTime) & 0x7F; // clamp to 127ms to avoid overflows. note: speed*deltaTime can still overflow for segments > ~10k pixels
4906+
*lastTime = strip.now;
4907+
4908+
if (SEGENV.aux0 > 0) {
4909+
SEGENV.aux0 = (SEGENV.aux0 > deltaTime) ? SEGENV.aux0 - deltaTime : 0;
4910+
} else {
4911+
// calculate movement step and update position
4912+
int32_t step = 1 + ((speed * deltaTime) >> 5); // subpixels moved this frame. note >>5 as speed is in subpixels/512ms
4913+
position += step;
4914+
int endposition = (SEGLEN + radius) << 8;
4915+
if (position > endposition) {
4916+
SEGENV.aux0 = SEGMENT.intensity * 236; // [0, 60180] ms pause
4917+
if(SEGMENT.check3) SEGENV.aux0 = hw_random(SEGENV.aux0 + 1000); // randomise interval, +1 second to affect low intensity values
4918+
position = -(radius << 8); // reset to start position (out of frame)
4919+
}
4920+
SEGENV.step = (uint32_t)position; // save back
4921+
}
4922+
4923+
if (SEGMENT.check2)
4924+
position = (SEGLEN << 8) - position; // invert position (and direction)
4925+
} else {
4926+
position = (SEGLEN << 7); // at speed=0, make it static in the center (this enables to use modulators only)
4927+
}
4928+
4929+
for (int i = 0; i < SEGLEN; i++) {
4930+
uint32_t dist = abs(position - (i << 8));
4931+
if (dist < (radius << 8)) {
4932+
uint32_t color = SEGMENT.color_from_palette(i * 255 / SEGLEN, false, false, 0);
4933+
uint8_t blend = dist / radius; // linear gradient note: dist is in subpixels, radius in pixels, result is [0, 255] since dist < radius*256
4934+
if (SEGMENT.custom2) {
4935+
uint8_t modVal; // modulation value
4936+
if (SEGMENT.check1) {
4937+
modVal = (sin16_t((i * SEGMENT.custom2 << 6) + (strip.now * SEGMENT.custom3 << 5)) >> 8) + 128; // sine modulation: regular "Zebra" stripes
4938+
} else {
4939+
modVal = perlin16((i * SEGMENT.custom2 << 7), strip.now * SEGMENT.custom3 << 5) >> 8; // perlin noise modulation
4940+
}
4941+
color = color_fade(color, modVal, true); // dim by modulator value
4942+
}
4943+
SEGMENT.setPixelColor(i, color_blend(color, SEGCOLOR(1), blend)); // blend to background color
4944+
} else {
4945+
SEGMENT.setPixelColor(i, SEGCOLOR(1));
4946+
}
4947+
}
4948+
4949+
return FRAMETIME;
4950+
}
4951+
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";
48804952

48814953
#ifndef WLED_DISABLE_2D
48824954
///////////////////////////////////////////////////////////////////////////////
@@ -10814,6 +10886,7 @@ void WS2812FX::setupEffectData() {
1081410886
addEffect(FX_MODE_FLOWSTRIPE, &mode_FlowStripe, _data_FX_MODE_FLOWSTRIPE);
1081510887
addEffect(FX_MODE_WAVESINS, &mode_wavesins, _data_FX_MODE_WAVESINS);
1081610888
addEffect(FX_MODE_ROCKTAVES, &mode_rocktaves, _data_FX_MODE_ROCKTAVES);
10889+
addEffect(FX_MODE_SHIMMER, &mode_shimmer, _data_FX_MODE_SHIMMER);
1081710890

1081810891
// --- 2D effects ---
1081910892
#ifndef WLED_DISABLE_2D

wled00/FX.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,7 @@ extern byte realtimeMode; // used in getMappedPixelIndex()
320320
#define FX_MODE_DJLIGHT 159
321321
#define FX_MODE_2DFUNKYPLANK 160
322322
//#define FX_MODE_2DCENTERBARS 161
323+
#define FX_MODE_SHIMMER 161 // gap fill, non SR 1D effect
323324
#define FX_MODE_2DPULSER 162
324325
#define FX_MODE_BLURZ 163
325326
#define FX_MODE_2DDRIFT 164

0 commit comments

Comments
 (0)