From 680d09c3a768a1df45fab55e9755b77a2ccc8ec3 Mon Sep 17 00:00:00 2001 From: Damian Schneider Date: Wed, 10 Sep 2025 06:11:48 +0200 Subject: [PATCH 1/2] improvements to Aurora FX - converted to integer math, increasing speed on all ESPs, also shrinks code size - caching values to avoid repeated calculations - CRGBW instead or CRGB, adds white channel support when not using palette - fix for new brightness/gamma handling --- wled00/FX.cpp | 149 +++++++++++++++++++++++++------------------------- 1 file changed, 74 insertions(+), 75 deletions(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index 3d580bb9a5..f5ed89c236 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -4666,7 +4666,8 @@ static const char _data_FX_MODE_TV_SIMULATOR[] PROGMEM = "TV Simulator@!,!;;!;01 /* - Aurora effect + Aurora effect by @Mazen + improved and converted to integer math by @dedehai */ //CONFIG @@ -4678,140 +4679,138 @@ static const char _data_FX_MODE_TV_SIMULATOR[] PROGMEM = "TV Simulator@!,!;;!;01 #define W_MAX_SPEED 6 //Higher number, higher speed #define W_WIDTH_FACTOR 6 //Higher number, smaller waves -//24 bytes +// fixed-point math scaling +#define AW_SHIFT 16 +#define AW_SCALE (1 << AW_SHIFT) // 65536 representing 1.0 + +// 32 bytes class AuroraWave { private: + int32_t center; // scaled by AW_SCALE + uint32_t ageFactor_cached; // cached age factor scaled by AW_SCALE uint16_t ttl; - CRGB basecolor; - float basealpha; uint16_t age; uint16_t width; - float center; + uint16_t basealpha; // scaled by AW_SCALE + uint16_t speed_factor; // scaled by AW_SCALE + int16_t wave_start; // wave start LED index + int16_t wave_end; // wave end LED index bool goingleft; - float speed_factor; bool alive = true; + CRGBW basecolor; public: - void init(uint32_t segment_length, CRGB color) { + void init(uint32_t segment_length, CRGBW color) { ttl = hw_random16(500, 1501); basecolor = color; - basealpha = hw_random8(60, 101) / (float)100; + basealpha = hw_random8(60, 100) * AW_SCALE / 100; // 0-99% note: if using 100% there is risk of integer overflow age = 0; - width = hw_random16(segment_length / 20, segment_length / W_WIDTH_FACTOR); //half of width to make math easier - if (!width) width = 1; - center = hw_random8(101) / (float)100 * segment_length; - goingleft = hw_random8(0, 2) == 0; - speed_factor = (hw_random8(10, 31) / (float)100 * W_MAX_SPEED / 255); + width = hw_random16(segment_length / 20, segment_length / W_WIDTH_FACTOR) + 1; + center = (((uint32_t)hw_random8(101) << AW_SHIFT) / 100) * segment_length; // 0-100% + goingleft = hw_random8() & 0x01; // 50/50 chance + speed_factor = (((uint32_t)hw_random8(10, 31) * W_MAX_SPEED) << AW_SHIFT) / (100 * 255); alive = true; } - CRGB getColorForLED(int ledIndex) { - if(ledIndex < center - width || ledIndex > center + width) return 0; //Position out of range of this wave - - CRGB rgb; - - //Offset of this led from center of wave - //The further away from the center, the dimmer the LED - float offset = ledIndex - center; - if (offset < 0) offset = -offset; - float offsetFactor = offset / width; - - //The age of the wave determines it brightness. - //At half its maximum age it will be the brightest. - float ageFactor = 0.1; - if((float)age / ttl < 0.5) { - ageFactor = (float)age / (ttl / 2); + void updateCachedValues() { + uint32_t half_ttl = ttl >> 1; + if (age < half_ttl) { + ageFactor_cached = ((uint32_t)age << AW_SHIFT) / half_ttl; } else { - ageFactor = (float)(ttl - age) / ((float)ttl * 0.5); + ageFactor_cached = ((uint32_t)(ttl - age) << AW_SHIFT) / half_ttl; } + if (ageFactor_cached > AW_SCALE) ageFactor_cached = AW_SCALE; + + uint32_t center_led = center >> AW_SHIFT; + wave_start = (int16_t)center_led - (int16_t)width; + wave_end = (int16_t)center_led + (int16_t)width; + } - //Calculate color based on above factors and basealpha value - float factor = (1 - offsetFactor) * ageFactor * basealpha; - rgb.r = basecolor.r * factor; - rgb.g = basecolor.g * factor; - rgb.b = basecolor.b * factor; + CRGBW getColorForLED(int ledIndex) { + // linear brightness falloff from center to edge of wave + if (ledIndex < wave_start || ledIndex > wave_end) return 0; + int32_t ledIndex_scaled = (int32_t)ledIndex << AW_SHIFT; + int32_t offset = ledIndex_scaled - center; + if (offset < 0) offset = -offset; + uint32_t offsetFactor = offset / width; // scaled by AW_SCALE + if (offsetFactor > AW_SCALE) return 0; // outside of wave + uint32_t brightness_factor = (AW_SCALE - offsetFactor); + brightness_factor = (brightness_factor * ageFactor_cached) >> AW_SHIFT; + brightness_factor = (brightness_factor * basealpha) >> AW_SHIFT; + + CRGBW rgb; + rgb.r = (basecolor.r * brightness_factor) >> AW_SHIFT; + rgb.g = (basecolor.g * brightness_factor) >> AW_SHIFT; + rgb.b = (basecolor.b * brightness_factor) >> AW_SHIFT; + rgb.w = (basecolor.w * brightness_factor) >> AW_SHIFT; return rgb; }; //Change position and age of wave - //Determine if its sill "alive" + //Determine if its still "alive" void update(uint32_t segment_length, uint32_t speed) { - if(goingleft) { - center -= speed_factor * speed; - } else { - center += speed_factor * speed; - } - + int32_t step = speed_factor * speed; + center += goingleft ? -step : step; age++; - if(age > ttl) { + if (age > ttl) { alive = false; } else { - if(goingleft) { - if(center + width < 0) { + uint32_t width_scaled = (uint32_t)width << AW_SHIFT; + uint32_t segment_length_scaled = segment_length << AW_SHIFT; + + if (goingleft) { + if (center + width_scaled < 0) { alive = false; } } else { - if(center - width > segment_length) { + if (center > segment_length_scaled + width_scaled) { alive = false; } } } }; - bool stillAlive() { - return alive; - }; + bool stillAlive() { return alive; } }; uint16_t mode_aurora(void) { AuroraWave* waves; SEGENV.aux1 = map(SEGMENT.intensity, 0, 255, 2, W_MAX_COUNT); // aux1 = Wavecount - if(!SEGENV.allocateData(sizeof(AuroraWave) * SEGENV.aux1)) { // 20 on ESP32, 9 on ESP8266 - return mode_static(); //allocation failed + if (!SEGENV.allocateData(sizeof(AuroraWave) * SEGENV.aux1)) { + return mode_static(); } waves = reinterpret_cast(SEGENV.data); - if(SEGENV.call == 0) { - for (int i = 0; i < SEGENV.aux1; i++) { - waves[i].init(SEGLEN, CRGB(SEGMENT.color_from_palette(hw_random8(), false, false, hw_random8(0, 3)))); - } - } - + // note: on first call, SEGENV.data is zero -> all waves are dead and will be initialized for (int i = 0; i < SEGENV.aux1; i++) { - //Update values of wave waves[i].update(SEGLEN, SEGMENT.speed); - - if(!(waves[i].stillAlive())) { - //If a wave dies, reinitialize it starts over. - waves[i].init(SEGLEN, CRGB(SEGMENT.color_from_palette(hw_random8(), false, false, hw_random8(0, 3)))); + if (!(waves[i].stillAlive())) { + waves[i].init(SEGLEN, SEGMENT.color_from_palette(hw_random8(), false, false, hw_random8(0, 3))); } + waves[i].updateCachedValues(); } - uint8_t backlight = 1; //dimmer backlight if less active colors + uint8_t backlight = 0; // note: original code used 1, with inverse gamma applied background would never be black if (SEGCOLOR(0)) backlight++; if (SEGCOLOR(1)) backlight++; if (SEGCOLOR(2)) backlight++; - //Loop through LEDs to determine color - for (unsigned i = 0; i < SEGLEN; i++) { - CRGB mixedRgb = CRGB(backlight, backlight, backlight); + backlight = gamma8inv(backlight); // preserve backlight when using gamma correction - //For each LED we must check each wave if it is "active" at this position. - //If there are multiple waves active on a LED we multiply their values. - for (int j = 0; j < SEGENV.aux1; j++) { - CRGB rgb = waves[j].getColorForLED(i); + for (unsigned i = 0; i < SEGLEN; i++) { + CRGBW mixedRgb = CRGBW(backlight, backlight, backlight); - if(rgb != CRGB(0)) { - mixedRgb += rgb; - } + for (int j = 0; j < SEGENV.aux1; j++) { + CRGBW rgb = waves[j].getColorForLED(i); + mixedRgb = color_add(mixedRgb, rgb); // sum all waves influencing this pixel } - SEGMENT.setPixelColor(i, mixedRgb[0], mixedRgb[1], mixedRgb[2]); + SEGMENT.setPixelColor(i, mixedRgb); } - return FRAMETIME; } + static const char _data_FX_MODE_AURORA[] PROGMEM = "Aurora@!,!;1,2,3;!;;sx=24,pal=50"; // WLED-SR effects From c2a359ec07633778ce22eed7c24b486f81383bc8 Mon Sep 17 00:00:00 2001 From: Damian Schneider Date: Sat, 13 Sep 2025 20:05:25 +0200 Subject: [PATCH 2/2] overflow & unsigned fix --- wled00/FX.cpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index f5ed89c236..8cdc9ba36f 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -4719,7 +4719,7 @@ class AuroraWave { } else { ageFactor_cached = ((uint32_t)(ttl - age) << AW_SHIFT) / half_ttl; } - if (ageFactor_cached > AW_SCALE) ageFactor_cached = AW_SCALE; + if (ageFactor_cached >= AW_SCALE) ageFactor_cached = AW_SCALE - 1; // prevent overflow uint32_t center_led = center >> AW_SHIFT; wave_start = (int16_t)center_led - (int16_t)width; @@ -4760,15 +4760,15 @@ class AuroraWave { uint32_t width_scaled = (uint32_t)width << AW_SHIFT; uint32_t segment_length_scaled = segment_length << AW_SHIFT; - if (goingleft) { - if (center + width_scaled < 0) { - alive = false; - } - } else { - if (center > segment_length_scaled + width_scaled) { - alive = false; - } - } + if (goingleft) { + if (center < - (int32_t)width_scaled) { + alive = false; + } + } else { + if (center > (int32_t)segment_length_scaled + (int32_t)width_scaled) { + alive = false; + } + } } };