Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Soap FX optimization #4543

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 50 additions & 38 deletions wled00/FX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7469,40 +7469,54 @@ static const char _data_FX_MODE_2DDISTORTIONWAVES[] PROGMEM = "Distortion Waves@
//@Stepko
//Idea from https://www.youtube.com/watch?v=DiHBgITrZck&ab_channel=StefanPetrick
// adapted for WLED by @blazoncek, optimization by @dedehai
void soapProcessPixels(bool isRow, uint8_t* noise3d) {
const int cols = SEG_W;
const int rows = SEG_H;
const auto XY = [&](int x, int y) { return (x%cols) + (y%rows) * cols; };
CRGB ledsbuff[MAX(cols,rows)];
const int rowcol = isRow ? rows : cols;
const int colrow = isRow ? cols : rows;
int amplitude = isRow ? (cols-8) >> 3 : (rows-8) >> 3;
amplitude = 2 * max(1, amplitude * (1 + SEGMENT.custom1) >> 6);

for (int i = 0; i < rowcol; i++) {
int amount = ((int)noise3d[isRow ? i * cols : i] - 128) * amplitude;
int delta = abs(amount) >> 8;
static void soapPixels(bool isRow, uint8_t *noise3d, CRGB *pixels) {
const int cols = SEG_W;
const int rows = SEG_H;
const auto XY = [&](int x, int y) { return x + y * cols; };
const auto abs = [](int x) { return x<0 ? -x : x; };
const int tRC = isRow ? rows : cols; // transpose if isRow
const int tCR = isRow ? cols : rows; // transpose if isRow
const int amplitude = 2 * ((tCR >= 16) ? (tCR-8) : 8) / (1 + ((255 - SEGMENT.custom1) >> 5));
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had >> 5 in the beginning but found it matches less with the original look on default setting. if you don't mind that, this is better.

edit: I tested this and it does change the look as well as the original logic, on high custom1 values it looks very weird. maybe you can keep my logic using max(1, xxxxx) as it will match better with the initial
amplitude = (cols >= 16) ? (cols-8)/8 : 1;

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Go ahead, I did not have much time to compare now-and-then but from the top of my head this was "original" if custom1==0.
I would avoid max() or min() nd just try to find a sweet spot.

Copy link
Collaborator

@blazoncek blazoncek Feb 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I find this the best compromise:

const int  amplitude = 1 + tCR / ((33 - SEGMENT.custom3) >> 1));

And is close to original source from @St3p40 while using slider to boost amplitude.
It may require default value of 4-8 to be equal to original.

NOTE: changed custom1 to custom3 as there is no need for 255 values.

const int shift = 0; //(128 - SEGMENT.custom2)*2;
blazoncek marked this conversation as resolved.
Show resolved Hide resolved

CRGB ledsbuff[tCR];

for (int i = 0; i < tRC; i++) {
int amount = ((int)noise3d[isRow ? i*cols : i] - 128) * amplitude + shift; // use first row/column: XY(0,i)/XY(i,0)
int delta = abs(amount) >> 8;
int fraction = abs(amount) & 255;
for (int j = 0; j < colrow; j++) {
for (int j = 0; j < tCR; j++) {
int zD, zF;
if (amount < 0) {
zD = j - delta;
zF = zD - 1;
zD = j - delta;
zF = zD - 1;
} else {
zD = j + delta;
zF = zD + 1;
zD = j + delta;
zF = zD + 1;
}
CRGB PixelA = CRGB::Black;
if ((zD >= 0) && (zD < colrow)) PixelA = isRow ? SEGMENT.getPixelColorXY(zD, i) : SEGMENT.getPixelColorXY(i, zD);
else PixelA = ColorFromPalette(SEGPALETTE, ~noise3d[isRow ? XY(abs(zD), i) : XY(i, abs(zD))] * 3);
CRGB PixelB = CRGB::Black;
if ((zF >= 0) && (zF < colrow)) PixelB = isRow ? SEGMENT.getPixelColorXY(zF, i) : SEGMENT.getPixelColorXY(i, zF);
else PixelB = ColorFromPalette(SEGPALETTE, ~noise3d[isRow ? XY(abs(zF), i) : XY(i, abs(zF))] * 3);
int yA = abs(zD);
int yB = abs(zF);
int xA = i;
int xB = i;
if (isRow) {
std::swap(xA,yA);
blazoncek marked this conversation as resolved.
Show resolved Hide resolved
std::swap(xB,yB);
}
const int indxA = XY(xA,yA);
const int indxB = XY(xB,yB);
CRGB PixelA;
CRGB PixelB;
if ((zD >= 0) && (zD < tCR)) PixelA = pixels[indxA];
else PixelA = ColorFromPalette(SEGPALETTE, ~noise3d[indxA]*3);
if ((zF >= 0) && (zF < tCR)) PixelB = pixels[indxB];
else PixelB = ColorFromPalette(SEGPALETTE, ~noise3d[indxB]*3);
ledsbuff[j] = (PixelA.nscale8(ease8InOutApprox(255 - fraction))) + (PixelB.nscale8(ease8InOutApprox(fraction)));
}
for (int j = 0; j < colrow; j++) {
if (isRow) SEGMENT.setPixelColorXY(j, i, ledsbuff[j]);
else SEGMENT.setPixelColorXY(i, j, ledsbuff[j]);
for (int j = 0; j < tCR; j++) {
CRGB c = ledsbuff[j];
if (isRow) std::swap(j,i);
SEGMENT.setPixelColorXY(i, j, pixels[XY(i,j)] = c);
if (isRow) std::swap(j,i);
}
}
}
Expand All @@ -7512,24 +7526,22 @@ uint16_t mode_2Dsoap() {

const int cols = SEG_W;
const int rows = SEG_H;
const auto XY = [&](int x, int y) { return (x%cols) + (y%rows) * cols; };
const auto XY = [&](int x, int y) { return x + y * cols; };

const size_t dataSize = SEGMENT.width() * SEGMENT.height() * sizeof(uint8_t); // prevent reallocation if mirrored or grouped
const size_t segSize = SEGMENT.width() * SEGMENT.height(); // prevent reallocation if mirrored or grouped
const size_t dataSize = segSize * (sizeof(uint8_t) + sizeof(CRGB)); // pixels and noise
if (!SEGENV.allocateData(dataSize + sizeof(uint32_t)*3)) return mode_static(); //allocation failed

uint8_t *noise3d = reinterpret_cast<uint8_t*>(SEGENV.data);
CRGB *pixels = reinterpret_cast<CRGB*>(SEGENV.data + segSize * sizeof(uint8_t));
blazoncek marked this conversation as resolved.
Show resolved Hide resolved
uint32_t *noisecoord = reinterpret_cast<uint32_t*>(SEGENV.data + dataSize); // x, y, z coordinates
const uint32_t scale32_x = 160000U/cols;
const uint32_t scale32_y = 160000U/rows;
const uint32_t mov = MIN(cols,rows)*(SEGMENT.speed+2)/2;
const uint8_t smoothness = MIN(250,SEGMENT.intensity); // limit as >250 produces very little changes

for (int i = 0; i < 3; i++) {
if (SEGENV.call == 0)
noisecoord[i] = hw_random(); // init
else
noisecoord[i] += mov;
}
if (SEGENV.call == 0) for (int i = 0; i < 3; i++) noisecoord[i] = hw_random(); // init
else for (int i = 0; i < 3; i++) noisecoord[i] += mov;

for (int i = 0; i < cols; i++) {
int32_t ioffset = scale32_x * (i - cols / 2);
Expand All @@ -7550,12 +7562,12 @@ uint16_t mode_2Dsoap() {
}
}

soapProcessPixels(true, noise3d); // rows
soapProcessPixels(false, noise3d); // cols
soapPixels(true, noise3d, pixels); // rows
soapPixels(false, noise3d, pixels); // cols

return FRAMETIME;
}
static const char _data_FX_MODE_2DSOAP[] PROGMEM = "Soap@!,Smoothness,Density;;!;2;pal=11";
static const char _data_FX_MODE_2DSOAP[] PROGMEM = "Soap@!,Smoothness,Density;;!;2;pal=11,c1=0";


//Idea from https://www.youtube.com/watch?v=HsA-6KIbgto&ab_channel=GreatScott%21
Expand Down