From 2012317bc9bc43184da9e1a81897410df701ec00 Mon Sep 17 00:00:00 2001 From: Damian Schneider Date: Mon, 3 Mar 2025 06:57:16 +0100 Subject: [PATCH 1/7] initial version, basically working but repetitive patterns, work in progress --- usermods/audioreactive/audio_reactive.h | 2 +- wled00/FX.cpp | 75 ++++----- wled00/fcn_declare.h | 9 ++ wled00/util.cpp | 195 +++++++++++++++++++++++- wled00/wled.cpp | 180 ++++++++++++++++++++++ 5 files changed, 421 insertions(+), 40 deletions(-) diff --git a/usermods/audioreactive/audio_reactive.h b/usermods/audioreactive/audio_reactive.h index e6b620098..d4b4b2d59 100644 --- a/usermods/audioreactive/audio_reactive.h +++ b/usermods/audioreactive/audio_reactive.h @@ -870,7 +870,7 @@ class AudioReactive : public Usermod { const int AGC_preset = (soundAgc > 0)? (soundAgc-1): 0; // make sure the _compiler_ knows this value will not change while we are inside the function #ifdef WLED_DISABLE_SOUND - micIn = inoise8(millis(), millis()); // Simulated analog read + micIn = perlin8(millis(), millis()); // Simulated analog read micDataReal = micIn; #else #ifdef ARDUINO_ARCH_ESP32 diff --git a/wled00/FX.cpp b/wled00/FX.cpp index e5132da57..a17d48603 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -2183,7 +2183,7 @@ static const char _data_FX_MODE_BPM[] PROGMEM = "Bpm@!;!;!;;sx=64"; uint16_t mode_fillnoise8() { if (SEGENV.call == 0) SEGENV.step = hw_random(); for (unsigned i = 0; i < SEGLEN; i++) { - unsigned index = inoise8(i * SEGLEN, SEGENV.step + i * SEGLEN); + unsigned index = perlin8(i * SEGLEN, SEGENV.step + i * SEGLEN); SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(index, false, PALETTE_SOLID_WRAP, 0)); } SEGENV.step += beatsin8_t(SEGMENT.speed, 1, 6); //10,1,4 @@ -2203,7 +2203,7 @@ uint16_t mode_noise16_1() { unsigned real_x = (i + shift_x) * scale; // the x position of the noise field swings @ 17 bpm unsigned real_y = (i + shift_y) * scale; // the y position becomes slowly incremented uint32_t real_z = SEGENV.step; // the z position becomes quickly incremented - unsigned noise = inoise16(real_x, real_y, real_z) >> 8; // get the noise data and scale it down + unsigned noise = perlin16(real_x, real_y, real_z) >> 8; // get the noise data and scale it down unsigned index = sin8_t(noise * 3); // map LED color based on noise data SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(index, false, PALETTE_SOLID_WRAP, 0)); @@ -2221,7 +2221,7 @@ uint16_t mode_noise16_2() { for (unsigned i = 0; i < SEGLEN; i++) { unsigned shift_x = SEGENV.step >> 6; // x as a function of time uint32_t real_x = (i + shift_x) * scale; // calculate the coordinates within the noise field - unsigned noise = inoise16(real_x, 0, 4223) >> 8; // get the noise data and scale it down + unsigned noise = perlin16(real_x, 0, 4223) >> 8; // get the noise data and scale it down unsigned index = sin8_t(noise * 3); // map led color based on noise data SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(index, false, PALETTE_SOLID_WRAP, 0, noise)); @@ -2242,7 +2242,7 @@ uint16_t mode_noise16_3() { uint32_t real_x = (i + shift_x) * scale; // calculate the coordinates within the noise field uint32_t real_y = (i + shift_y) * scale; // based on the precalculated positions uint32_t real_z = SEGENV.step*8; - unsigned noise = inoise16(real_x, real_y, real_z) >> 8; // get the noise data and scale it down + unsigned noise = perlin16(real_x, real_y, real_z) >> 8; // get the noise data and scale it down unsigned index = sin8_t(noise * 3); // map led color based on noise data SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(index, false, PALETTE_SOLID_WRAP, 0, noise)); @@ -2257,7 +2257,7 @@ static const char _data_FX_MODE_NOISE16_3[] PROGMEM = "Noise 3@!;!;!;;pal=35"; uint16_t mode_noise16_4() { uint32_t stp = (strip.now * SEGMENT.speed) >> 7; for (unsigned i = 0; i < SEGLEN; i++) { - int index = inoise16(uint32_t(i) << 12, stp); + int index = perlin16(uint32_t(i) << 12, stp); SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(index, false, PALETTE_SOLID_WRAP, 0)); } return FRAMETIME; @@ -4119,7 +4119,7 @@ static uint16_t phased_base(uint8_t moder) { // We're making si *phase += SEGMENT.speed/32.0; // You can change the speed of the wave. AKA SPEED (was .4) for (unsigned i = 0; i < SEGLEN; i++) { - if (moder == 1) modVal = (inoise8(i*10 + i*10) /16); // Let's randomize our mod length with some Perlin noise. + if (moder == 1) modVal = (perlin8(i*10 + i*10) /16); // Let's randomize our mod length with some Perlin noise. unsigned val = (i+1) * allfreq; // This sets the frequency of the waves. The +1 makes sure that led 0 is used. if (modVal == 0) modVal = 1; val += *phase * (i % modVal +1) /2; // This sets the varying phase change of the waves. By Andrew Tuline. @@ -4188,7 +4188,7 @@ uint16_t mode_noisepal(void) { // Slow noise if (SEGMENT.palette > 0) palettes[0] = SEGPALETTE; for (unsigned i = 0; i < SEGLEN; i++) { - unsigned index = inoise8(i*scale, SEGENV.aux0+i*scale); // Get a value from the noise function. I'm using both x and y axis. + unsigned index = perlin8(i*scale, SEGENV.aux0+i*scale); // Get a value from the noise function. I'm using both x and y axis. SEGMENT.setPixelColor(i, ColorFromPalette(palettes[0], index, 255, LINEARBLEND)); // Use my own palette. } @@ -4799,7 +4799,7 @@ uint16_t mode_perlinmove(void) { if (SEGLEN <= 1) return mode_static(); SEGMENT.fade_out(255-SEGMENT.custom1); for (int i = 0; i < SEGMENT.intensity/16 + 1; i++) { - unsigned locn = inoise16(strip.now*128/(260-SEGMENT.speed)+i*15000, strip.now*128/(260-SEGMENT.speed)); // Get a new pixel location from moving noise. + unsigned locn = perlin16(strip.now*128/(260-SEGMENT.speed)+i*15000, strip.now*128/(260-SEGMENT.speed)); // Get a new pixel location from moving noise. unsigned pixloc = map(locn, 50*256, 192*256, 0, SEGLEN-1); // Map that to the length of the strand, and ensure we don't go over. SEGMENT.setPixelColor(pixloc, SEGMENT.color_from_palette(pixloc%255, false, PALETTE_SOLID_WRAP, 0)); } @@ -5057,7 +5057,7 @@ uint16_t mode_2Dfirenoise(void) { // firenoise2d. By Andrew Tuline CRGBPalette16 pal = SEGMENT.check1 ? SEGPALETTE : SEGMENT.loadPalette(pal, 35); for (int j=0; j < cols; j++) { for (int i=0; i < rows; i++) { - indexx = inoise8(j*yscale*rows/255, i*xscale+strip.now/4); // We're moving along our Perlin map. + indexx = perlin8(j*yscale*rows/255, i*xscale+strip.now/4); // We're moving along our Perlin map. SEGMENT.setPixelColorXY(j, i, ColorFromPalette(pal, min(i*indexx/11, 225U), i*255/rows, LINEARBLEND)); // With that value, look up the 8 bit colour palette value and assign it to the current LED. } // for i } // for j @@ -5450,11 +5450,11 @@ uint16_t mode_2Dmetaballs(void) { // Metaballs by Stefan Petrick. Cannot have float speed = 0.25f * (1+(SEGMENT.speed>>6)); // get some 2 random moving points - int x2 = map(inoise8(strip.now * speed, 25355, 685), 0, 255, 0, cols-1); - int y2 = map(inoise8(strip.now * speed, 355, 11685), 0, 255, 0, rows-1); + int x2 = map(perlin8(strip.now * speed, 25355, 685), 0, 255, 0, cols-1); + int y2 = map(perlin8(strip.now * speed, 355, 11685), 0, 255, 0, rows-1); - int x3 = map(inoise8(strip.now * speed, 55355, 6685), 0, 255, 0, cols-1); - int y3 = map(inoise8(strip.now * speed, 25355, 22685), 0, 255, 0, rows-1); + int x3 = map(perlin8(strip.now * speed, 55355, 6685), 0, 255, 0, cols-1); + int y3 = map(perlin8(strip.now * speed, 25355, 22685), 0, 255, 0, rows-1); // and one Lissajou function int x1 = beatsin8_t(23 * speed, 0, cols-1); @@ -5510,7 +5510,7 @@ uint16_t mode_2Dnoise(void) { // By Andrew Tuline for (int y = 0; y < rows; y++) { for (int x = 0; x < cols; x++) { - uint8_t pixelHue8 = inoise8(x * scale, y * scale, strip.now / (16 - SEGMENT.speed/16)); + uint8_t pixelHue8 = perlin8(x * scale, y * scale, strip.now / (16 - SEGMENT.speed/16)); SEGMENT.setPixelColorXY(x, y, ColorFromPalette(SEGPALETTE, pixelHue8)); } } @@ -5532,10 +5532,10 @@ uint16_t mode_2DPlasmaball(void) { // By: Stepko https://edito SEGMENT.fadeToBlackBy(SEGMENT.custom1>>2); uint_fast32_t t = (strip.now * 8) / (256 - SEGMENT.speed); // optimized to avoid float for (int i = 0; i < cols; i++) { - unsigned thisVal = inoise8(i * 30, t, t); + unsigned thisVal = perlin8(i * 30, t, t); unsigned thisMax = map(thisVal, 0, 255, 0, cols-1); for (int j = 0; j < rows; j++) { - unsigned thisVal_ = inoise8(t, j * 30, t); + unsigned thisVal_ = perlin8(t, j * 30, t); unsigned thisMax_ = map(thisVal_, 0, 255, 0, rows-1); int x = (i + thisMax_ - cols / 2); int y = (j + thisMax - cols / 2); @@ -5580,7 +5580,7 @@ uint16_t mode_2DPolarLights(void) { // By: Kostyantyn Matviyevskyy https for (int x = 0; x < cols; x++) { for (int y = 0; y < rows; y++) { SEGENV.step++; - uint8_t palindex = qsub8(inoise8((SEGENV.step%2) + x * _scale, y * 16 + SEGENV.step % 16, SEGENV.step / _speed), fabsf((float)rows / 2.0f - (float)y) * adjustHeight); + uint8_t palindex = qsub8(perlin8((SEGENV.step%2) + x * _scale, y * 16 + SEGENV.step % 16, SEGENV.step / _speed), fabsf((float)rows / 2.0f - (float)y) * adjustHeight); uint8_t palbrightness = palindex; if(SEGMENT.check1) palindex = 255 - palindex; //flip palette SEGMENT.setPixelColorXY(x, y, SEGMENT.color_from_palette(palindex, false, false, 255, palbrightness)); @@ -5697,7 +5697,8 @@ uint16_t mode_2DSunradiation(void) { // By: ldirko https://edi uint8_t someVal = SEGMENT.speed/4; // Was 25. for (int j = 0; j < (rows + 2); j++) { for (int i = 0; i < (cols + 2); i++) { - byte col = (inoise8_raw(i * someVal, j * someVal, t)) / 2; + //byte col = (inoise8_raw(i * someVal, j * someVal, t)) / 2; + byte col = ((int16_t)perlin8(i * someVal, j * someVal, t) - 0x7F) / 3; bump[index++] = col; } } @@ -6232,7 +6233,7 @@ uint16_t mode_2Dplasmarotozoom() { int index = j*cols; for (int i = 0; i < cols; i++) { if (SEGMENT.check1) plasma[index+i] = (i * 4 ^ j * 4) + ms / 6; - else plasma[index+i] = inoise8(i * 40, j * 40, ms); + else plasma[index+i] = perlin8(i * 40, j * 40, ms); } } @@ -6395,10 +6396,10 @@ uint16_t mode_2DWaverly(void) { long t = strip.now / 2; for (int i = 0; i < cols; i++) { - unsigned thisVal = (1 + SEGMENT.intensity/64) * inoise8(i * 45 , t , t)/2; + unsigned thisVal = (1 + SEGMENT.intensity/64) * perlin8(i * 45 , t , t)/2; // use audio if available if (um_data) { - thisVal /= 32; // reduce intensity of inoise8() + thisVal /= 32; // reduce intensity of perlin8() thisVal *= volumeSmth; } int thisMax = map(thisVal, 0, 512, 0, rows); @@ -6477,7 +6478,7 @@ uint16_t mode_gravcenter_base(unsigned mode) { } else if(mode == 2) { //Gravimeter for (int i=0; itopLED > 0) { @@ -6499,7 +6500,7 @@ uint16_t mode_gravcenter_base(unsigned mode) { } else { //Gravcenter for (int i=0; iSEGLEN/2) maxLen = SEGLEN/2; for (unsigned i=(SEGLEN/2-maxLen); i<(SEGLEN/2+maxLen); i++) { - uint8_t index = inoise8(i*volumeSmth+SEGENV.aux0, SEGENV.aux1+i*volumeSmth); // Get a value from the noise function. I'm using both x and y axis. + uint8_t index = perlin8(i*volumeSmth+SEGENV.aux0, SEGENV.aux1+i*volumeSmth); // Get a value from the noise function. I'm using both x and y axis. SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(index, false, PALETTE_SOLID_WRAP, 0)); } @@ -6649,7 +6650,7 @@ uint16_t mode_noisefire(void) { // Noisefire. By Andrew Tuline. if (SEGENV.call == 0) SEGMENT.fill(BLACK); for (unsigned i = 0; i < SEGLEN; i++) { - unsigned index = inoise8(i*SEGMENT.speed/64,strip.now*SEGMENT.speed/64*SEGLEN/255); // X location is constant, but we move along the Y at the rate of millis(). By Andrew Tuline. + unsigned index = perlin8(i*SEGMENT.speed/64,strip.now*SEGMENT.speed/64*SEGLEN/255); // X location is constant, but we move along the Y at the rate of millis(). By Andrew Tuline. index = (255 - i*256/SEGLEN) * index/(256-SEGMENT.intensity); // Now we need to scale index so that it gets blacker as we get close to one of the ends. // This is a simple y=mx+b equation that's been scaled. index/128 is another scaling. @@ -6680,7 +6681,7 @@ uint16_t mode_noisemeter(void) { // Noisemeter. By Andrew Tuline. if (maxLen > SEGLEN) maxLen = SEGLEN; for (unsigned i=0; i> 8; + uint8_t data = perlin16(noisecoord[0] + ioffset, noisecoord[1] + joffset, noisecoord[2]) >> 8; noise3d[XY(i,j)] = scale8(noise3d[XY(i,j)], smoothness) + scale8(data, 255 - smoothness); } } @@ -8051,7 +8052,7 @@ uint16_t mode_particlefire(void) { if (SEGMENT.call % 10 == 0) SEGENV.aux1++; // move in noise y direction so noise does not repeat as often // add wind force to all particles - int8_t windspeed = ((int16_t)(inoise8(SEGENV.aux0, SEGENV.aux1) - 127) * SEGMENT.custom2) >> 7; + int8_t windspeed = ((int16_t)(perlin8(SEGENV.aux0, SEGENV.aux1) - 127) * SEGMENT.custom2) >> 7; PartSys->applyForce(windspeed, 0); } SEGENV.step++; @@ -8060,7 +8061,7 @@ uint16_t mode_particlefire(void) { if (SEGMENT.call % map(firespeed, 0, 255, 4, 15) == 0) { for (i = 0; i < PartSys->usedParticles; i++) { if (PartSys->particles[i].y < PartSys->maxY / 4) { // do not apply turbulance everywhere -> bottom quarter seems a good balance - int32_t curl = ((int32_t)inoise8(PartSys->particles[i].x, PartSys->particles[i].y, SEGENV.step << 4) - 127); + int32_t curl = ((int32_t)perlin8(PartSys->particles[i].x, PartSys->particles[i].y, SEGENV.step << 4) - 127); PartSys->particles[i].vx += (curl * (firespeed + 10)) >> 9; } } @@ -8277,8 +8278,8 @@ uint16_t mode_particlebox(void) { SEGENV.aux0 -= increment; if (SEGMENT.check1) { // random, use perlin noise - xgravity = ((int16_t)inoise8(SEGENV.aux0) - 127); - ygravity = ((int16_t)inoise8(SEGENV.aux0 + 10000) - 127); + xgravity = ((int16_t)perlin8(SEGENV.aux0) - 127); + ygravity = ((int16_t)perlin8(SEGENV.aux0 + 10000) - 127); // scale the gravity force xgravity = (xgravity * SEGMENT.custom1) / 128; ygravity = (ygravity * SEGMENT.custom1) / 128; @@ -8349,11 +8350,11 @@ uint16_t mode_particleperlin(void) { uint32_t scale = 16 - ((31 - SEGMENT.custom3) >> 1); uint16_t xnoise = PartSys->particles[i].x / scale; // position in perlin noise, scaled by slider uint16_t ynoise = PartSys->particles[i].y / scale; - int16_t baseheight = inoise8(xnoise, ynoise, SEGENV.aux0); // noise value at particle position + int16_t baseheight = perlin8(xnoise, ynoise, SEGENV.aux0); // noise value at particle position PartSys->particles[i].hue = baseheight; // color particles to perlin noise value if (SEGMENT.call % 8 == 0) { // do not apply the force every frame, is too chaotic - int8_t xslope = (baseheight + (int16_t)inoise8(xnoise - 10, ynoise, SEGENV.aux0)); - int8_t yslope = (baseheight + (int16_t)inoise8(xnoise, ynoise - 10, SEGENV.aux0)); + int8_t xslope = (baseheight + (int16_t)perlin8(xnoise - 10, ynoise, SEGENV.aux0)); + int8_t yslope = (baseheight + (int16_t)perlin8(xnoise, ynoise - 10, SEGENV.aux0)); PartSys->applyForce(i, xslope, yslope); } } @@ -9721,7 +9722,7 @@ uint16_t mode_particleBalance(void) { int32_t increment = (SEGMENT.speed >> 6) + 1; SEGENV.aux0 += increment; if (SEGMENT.check3) // random, use perlin noise - xgravity = ((int16_t)inoise8(SEGENV.aux0) - 128); + xgravity = ((int16_t)perlin8(SEGENV.aux0) - 128); else // sinusoidal xgravity = (int16_t)cos8(SEGENV.aux0) - 128;//((int32_t)(SEGMENT.custom3 << 2) * cos8(SEGENV.aux0) // scale the force @@ -10073,7 +10074,7 @@ uint16_t mode_particle1Dsonicstream(void) { else PartSys->particles[i].ttl = 0; } if (SEGMENT.check1) // modulate colors by mid frequencies - PartSys->particles[i].hue += (mids * inoise8(PartSys->particles[i].x << 2, SEGMENT.step << 2)) >> 9; // color by perlin noise from mid frequencies + PartSys->particles[i].hue += (mids * perlin8(PartSys->particles[i].x << 2, SEGMENT.step << 2)) >> 9; // color by perlin noise from mid frequencies } if (loudness > threshold) { diff --git a/wled00/fcn_declare.h b/wled00/fcn_declare.h index 6584d524e..4d5557fda 100644 --- a/wled00/fcn_declare.h +++ b/wled00/fcn_declare.h @@ -516,6 +516,15 @@ void enumerateLedmaps(); [[gnu::hot]] uint8_t get_random_wheel_index(uint8_t pos); [[gnu::hot, gnu::pure]] float mapf(float x, float in_min, float in_max, float out_min, float out_max); uint32_t hashInt(uint32_t s); +int32_t perlin1D_raw(uint32_t x); +int32_t perlin2D_raw(uint32_t x, uint32_t y); +int32_t perlin3D_raw(uint32_t x, uint32_t y, uint32_t z); +uint8_t perlin8(uint16_t x); +uint8_t perlin8(uint16_t x, uint16_t y); +uint8_t perlin8(uint16_t x, uint16_t y, uint16_t z); +uint16_t perlin16(uint32_t x); +uint16_t perlin16(uint32_t x, uint32_t y); +uint16_t perlin16(uint32_t x, uint32_t y, uint32_t z); // fast (true) random numbers using hardware RNG, all functions return values in the range lowerlimit to upperlimit-1 // note: for true random numbers with high entropy, do not call faster than every 200ns (5MHz) diff --git a/wled00/util.cpp b/wled00/util.cpp index 16af85e71..d5341a96e 100644 --- a/wled00/util.cpp +++ b/wled00/util.cpp @@ -506,12 +506,12 @@ um_data_t* simulateSound(uint8_t simulationId) break; case UMS_10_13: for (int i = 0; i<16; i++) - fftResult[i] = inoise8(beatsin8_t(90 / (i+1), 0, 200)*15 + (ms>>10), ms>>3); + fftResult[i] = perlin8(beatsin8_t(90 / (i+1), 0, 200)*15 + (ms>>10), ms>>3); volumeSmth = fftResult[8]; break; case UMS_14_3: for (int i = 0; i<16; i++) - fftResult[i] = inoise8(beatsin8_t(120 / (i+1), 10, 30)*10 + (ms>>14), ms>>3); + fftResult[i] = perlin8(beatsin8_t(120 / (i+1), 10, 30)*10 + (ms>>14), ms>>3); volumeSmth = fftResult[8]; break; } @@ -618,3 +618,194 @@ int32_t hw_random(int32_t lowerlimit, int32_t upperlimit) { uint32_t diff = upperlimit - lowerlimit; return hw_random(diff) + lowerlimit; } + +/* + * Fixed point integer based Perlin noise functions by @dedehai + * Note: optimized for speed and to mimic fastled inoise functions, not for accuracy or best randomness + */ + +// hash based gradient (speed is key) +static inline __attribute__((always_inline)) uint32_t perlinHash(uint32_t x) { + //x ^= x >> 15; //11? + //x *= 0x85ebca6b; + //x ^= x >> 7; + + //x *= 0xc2b2ae35; + //x ^= x >> 17; + //return x; + + //version from above, does not look too good + //x = ((x >> 16) ^ x) * 0x45d9f3b; + //x = ((x >> 16) ^ x) * 0x45d9f3b; + //return (x >> 16) ^ x; + +//found this online at https://github.com/skeeto/hash-prospector +// allegedly even better than the above murmur hash + x ^= x >> 16; + x *= 0x7feb352d; + x ^= x >> 15; + x *= 0x846ca68b; + x ^= x >> 16; + return x; + + +} + +//int8_t slopes[8] = {-4,-3,-2,-1,1,2,3,4}; +static inline __attribute__((always_inline)) int32_t cornergradient(uint32_t h) { + int grad = (h & 0x0F) - 8; // +7 to -8 + // int grad = slopes[h & 0x7]; // +1 or -1 (better mimics fastled but much slower, can be optimized by passing an array pointer or making the array global + //int grad = (h & 0x07) - 4; // +3 to -4 + //int grad = (h & 0x03) - 2; // +1 to -2 + //int grad = (h & 0x07) * (h & 0x10 ? -1 : 1); // symmetrical, much (!) slower + //return slopes[h & 0x7]; // lookup table is also very slow... + return grad; +} + +// Gradient functions for 1D, 2D and 3D Perlin noise note: forcing inline produces smaller code and makes it 3x faster! +static inline __attribute__((always_inline)) int32_t gradient1D(uint32_t x0, int32_t dx) { + int32_t grad = cornergradient(perlinHash(x0)); + return (grad * dx) >> 1; +} + +static inline __attribute__((always_inline)) int32_t gradient2D(uint32_t x0, int32_t dx, uint32_t y0, int32_t dy) { + uint32_t hashx = perlinHash(x0); + //uint32_t hashy = perlinHash(hashx ^ y0); + uint32_t hashy = perlinHash(y0 + 1013904223UL); + int32_t gradx = cornergradient(hashx); + int32_t grady = cornergradient(hashy); + return (gradx * dx + grady * dy) >> 2; +} + +static inline __attribute__((always_inline)) int32_t gradient3D(uint32_t x0, int32_t dx, uint32_t y0, int32_t dy, uint32_t z0, int32_t dz) { + //uint32_t hashx = perlinHash(x0); + //uint32_t hashy = perlinHash(hashx ^ y0); + //uint32_t hashz = perlinHash(hashy ^ z0); + + uint32_t hashx = perlinHash(x0); + uint32_t hashy = perlinHash(y0 + 1013904223UL); + uint32_t hashz = perlinHash(z0 + 1664525UL); + + int32_t gradx = cornergradient(hashx); + int32_t grady = cornergradient(hashy); + int32_t gradz = cornergradient(hashz); + return (gradx * dx + grady * dy + gradz * dz) >> 4; +} + +// fast cubic smoothstep: t*(3 - 2t²), optimized for fixed point +static uint32_t smoothstep(const uint32_t t) { + uint32_t t_squared = (t * t) >> 16; + uint32_t factor = (3 << 16) - ((t << 1)); + return (t_squared * factor) >> 17; // scale for best resolution without overflow +} + +// simple linear interpolation for fixed-point values, scaled for perlin noise use +static inline int32_t lerpPerlin(int32_t a, int32_t b, int32_t t) { + return a + (((b - a) * t) >> 15); +} + +// 1D Perlin noise function that returns a value in range of approximately -32768 to +32768 +int32_t perlin1D_raw(uint32_t x) { + // integer and fractional part coordinates + int32_t x0 = x >> 16; + int32_t x1 = x0 + 1; + int32_t dx0 = x & 0xFFFF; + int32_t dx1 = dx0 - 0xFFFF; + // gradient values for the two corners + int32_t g0 = gradient1D(x0, dx0); + int32_t g1 = gradient1D(x1, dx1); + // interpolate and smooth function + int32_t t = smoothstep(dx0); + int32_t noise = lerpPerlin(g0, g1, t); + return noise; +} + +// 2D Perlin noise function that returns a value in range of approximately -32768 to +32768 +int32_t perlin2D_raw(uint32_t x, uint32_t y) { + int32_t x0 = x >> 16; + int32_t y0 = y >> 16; + int32_t x1 = x0 + 1; + int32_t y1 = y0 + 1; + int32_t dx0 = x & 0xFFFF; + int32_t dy0 = y & 0xFFFF; + int32_t dx1 = dx0 - 0xFFFF; + int32_t dy1 = dy0 - 0xFFFF; + + int32_t g00 = gradient2D(x0, dx0, y0, dy0); + int32_t g10 = gradient2D(x1, dx1, y0, dy0); + int32_t g01 = gradient2D(x0, dx0, y1, dy1); + int32_t g11 = gradient2D(x1, dx1, y1, dy1); + + uint32_t tx = smoothstep(dx0); + uint32_t ty = smoothstep(dy0); + + int32_t nx0 = lerpPerlin(g00, g10, tx); + int32_t nx1 = lerpPerlin(g01, g11, tx); + + int32_t noise = lerpPerlin(nx0, nx1, ty); + return noise; +} + +// 2D Perlin noise function that returns a value in range of approximately -40000 to +40000 +int32_t perlin3D_raw(uint32_t x, uint32_t y, uint32_t z) { + int32_t x0 = x >> 16; + int32_t y0 = y >> 16; + int32_t z0 = z >> 16; + int32_t x1 = x0 + 1; + int32_t y1 = y0 + 1; + int32_t z1 = z0 + 1; + + int32_t dx0 = x & 0xFFFF; + int32_t dy0 = y & 0xFFFF; + int32_t dz0 = z & 0xFFFF; + int32_t dx1 = dx0 - 0xFFFF; + int32_t dy1 = dy0 - 0xFFFF; + int32_t dz1 = dz0 - 0xFFFF; + + int32_t g000 = gradient3D(x0, dx0, y0, dy0, z0, dz0); + int32_t g001 = gradient3D(x0, dx0, y0, dy0, z1, dz1); + int32_t g010 = gradient3D(x0, dx0, y1, dy1, z0, dz0); + int32_t g011 = gradient3D(x0, dx0, y1, dy1, z1, dz1); + int32_t g100 = gradient3D(x1, dx1, y0, dy0, z0, dz0); + int32_t g101 = gradient3D(x1, dx1, y0, dy0, z1, dz1); + int32_t g110 = gradient3D(x1, dx1, y1, dy1, z0, dz0); + int32_t g111 = gradient3D(x1, dx1, y1, dy1, z1, dz1); + + uint32_t tx = smoothstep(dx0); + uint32_t ty = smoothstep(dy0); + uint32_t tz = smoothstep(dz0); + + int32_t nx0 = lerpPerlin(g000, g100, tx); + int32_t nx1 = lerpPerlin(g010, g110, tx); + int32_t nx2 = lerpPerlin(g001, g101, tx); + int32_t nx3 = lerpPerlin(g011, g111, tx); + int32_t ny0 = lerpPerlin(nx0, nx1, ty); + int32_t ny1 = lerpPerlin(nx2, nx3, ty); + + int32_t noise = lerpPerlin(ny0, ny1, tz); + return noise; +} +// scaling functions for fastled replacement +uint8_t perlin8(uint16_t x) { + return (perlin1D_raw(uint32_t(x) << 8) >> 8) + 0x7F; +} + +uint8_t perlin8(uint16_t x, uint16_t y) { + return uint8_t((perlin2D_raw(uint32_t(x)<<8, uint32_t(y)<<8) >> 8) + 0x7F); +} + +uint8_t perlin8(uint16_t x, uint16_t y, uint16_t z) { + return ((perlin3D_raw(uint32_t(x)<<8, uint32_t(y)<<8, uint32_t(z)<<8) * 85) >> 15) + 0x7F; +} + +uint16_t perlin16(uint32_t x) { + return perlin1D_raw(x) + 0x7FFF; +} + +uint16_t perlin16(uint32_t x, uint32_t y) { + return perlin2D_raw(x, y) + 0x7FFF; +} + +uint16_t perlin16(uint32_t x, uint32_t y, uint32_t z) { + return ((perlin3D_raw(x, y, z) * 70) >> 6) + 0x7FFF; +} \ No newline at end of file diff --git a/wled00/wled.cpp b/wled00/wled.cpp index bafdf58a9..e242d68f5 100644 --- a/wled00/wled.cpp +++ b/wled00/wled.cpp @@ -340,6 +340,186 @@ void WLED::setup() DEBUG_PRINTLN(F("arduino-esp32 v1.0.x\n")); // we can't say in more detail. #endif + + uint32_t start; + uint32_t end; + uint32_t time; + uint8_t offset = hw_random(); + + for(int i = 0; i < 0xFFFFF; i+=800) { + Serial.print(inoise16(i, offset, (offset >> 3))); Serial.print(","); //x + Serial.print(inoise16(offset, i, (offset >> 3))); Serial.print(","); //y + Serial.print(inoise16(offset, (offset >> 3), i)); Serial.print(","); //z + Serial.print(perlin16(i, offset, (offset >> 3))); Serial.print(","); //x + Serial.print(perlin16(offset, i, (offset >> 3))); Serial.print(","); //y + Serial.print(perlin16(offset, (offset >> 3), i)); Serial.print(","); //z + Serial.print(inoise16(i, offset+i/4, i*2 + (offset >> 3))); Serial.print(","); //mixed mode + Serial.println(perlin16(i, offset+i/4, i*2 + (offset >> 3))); + } +/* + for(int i = 0; i < 0x2FFFF; i+=100) { + uint32_t pos = i + offset; + Serial.print(inoise8_raw((pos)>>3, (pos)>>3)); Serial.print(","); + Serial.print(inoise8((pos)>>3, (pos)>>3, (pos)>>3)); Serial.print(","); + Serial.print(inoise16(pos*20, pos*30)); Serial.print(","); + //Serial.print(inoise16_raw(pos*20, pos*30, pos*40)); Serial.print(","); + Serial.print(inoise16(pos*20, pos*20, pos*20)); Serial.print(","); + //Serial.print(((perlin1D_raw(pos*20)* 85)>>7) + 0x7FFF); Serial.print(","); + Serial.print(perlin1D_raw(pos*20)); Serial.print(","); + Serial.print(perlin2D_raw(pos*20, pos*20)); Serial.print(","); + //Serial.print(perlin2D_raw(pos*20, pos*30) + 0x7FFF); Serial.print(","); + Serial.println(perlin3D_raw(pos*20, pos*20, pos*20)); + //Serial.println(((perlin3D_raw(pos*20, pos*30, pos*40) * 85)>>7) + 0x7FFF); + }*/ + +/* + for(int i = 0; i < 0xF0000; i+=55) { + Serial.print(inoise8_raw(i,i+5648) / 2); Serial.print(","); // +/-32 ? + Serial.print(((int16_t)perlin8(i,i+5648) - 0x7F) >> 2); Serial.print(","); + Serial.print(inoise8(i,i/3,i/5)); Serial.print(","); + Serial.print(perlin8(i,i/3,i/5)); Serial.print(","); + Serial.print(inoise8(i,i/3)); Serial.print(","); + Serial.print(perlin8(i,i/3)); Serial.print(","); + Serial.print(inoise8(i)); Serial.print(","); + Serial.println(perlin8(i)); + } +*/ + int32_t minval=0xFFFFF; + int32_t maxval=0; + start = micros(); + for(int i = 0; i < 0xFFFFF; i+=100) { + uint16_t pos = i + offset; + int32_t noiseval = inoise8_raw(pos); + if(noiseval < minval) minval = noiseval; + if(noiseval > maxval) maxval = noiseval; + } + end = micros(); + time = end - start; + Serial.print("time: "); Serial.print(time); + Serial.print(" inoise8_raw min: "); Serial.print(minval); Serial.print(" max: "); Serial.println(maxval); + + minval=0xFFFFF; + maxval=0; + /* + start = micros(); + for(int i = 0; i < 0xFFFFFF; i+=100) { + uint32_t pos = i + offset; + //int32_t noiseval = inoise16(pos, pos+4684165, pos+985685); + int32_t noiseval = inoise16(hw_random(), hw_random(), hw_random()); + if(noiseval < minval) minval = noiseval; + if(noiseval > maxval) maxval = noiseval; + } + end = micros(); + time = end - start; + Serial.print("time: "); Serial.print(time); + Serial.print(" inoise16_3D min: "); Serial.print(minval); Serial.print(" max: "); Serial.println(maxval); +*/ + minval=0xFFFFF; + maxval=0; + start = micros(); + for(int i = 0; i < 0xFFFFFFF; i+=100) { + uint32_t pos = i + offset; + int32_t noiseval = perlin1D_raw( hw_random()); + if(noiseval < minval) minval = noiseval; + if(noiseval > maxval) maxval = noiseval; + } + end = micros(); + time = end - start; + Serial.print("time: "); Serial.print(time); +Serial.print(" perlin1D_raw min: "); Serial.print(minval); Serial.print(" max: "); Serial.println(maxval); + minval=0xFFFFF; + maxval=0; + start = micros(); + for(int i = 0; i < 0xFFFFFFF; i+=100) { + uint32_t pos = i + offset; + //int32_t noiseval = perlin2D_raw(pos, pos+6846354); + int32_t noiseval = perlin2D_raw( hw_random(), hw_random()); + if(noiseval < minval) minval = noiseval; + if(noiseval > maxval) maxval = noiseval; + } + end = micros(); + time = end - start; + Serial.print("time: "); Serial.print(time); + Serial.print(" perlin2D_raw min: "); Serial.print(minval); Serial.print(" max: "); Serial.println(maxval); + minval=0xFFFFF; + maxval=0; + for(int i = 0; i < 0xFFFFFFF; i+=100) { + uint32_t pos = i + offset; + //int32_t noiseval = perlin3D_raw(pos, pos+46845, pos+654684); + //int32_t noiseval = perlin3D_raw(hw_random(), hw_random(), hw_random()); + int32_t noiseval = perlin16(hw_random(), hw_random(), hw_random()); + if(noiseval < minval) minval = noiseval; + if(noiseval > maxval) maxval = noiseval; + } + end = micros(); + time = end - start; + Serial.print("time: "); Serial.print(time); + Serial.print(" perlin16 min: "); Serial.print(minval); Serial.print(" max: "); Serial.println(maxval); + + volatile uint32_t temp; + start = micros(); + for(int i = 0; i < 100000; i++){ + temp += inoise8(i); + } + end = micros(); + time = end - start; + Serial.print("inoise8: "); + Serial.print(temp); + Serial.print("time: "); + Serial.println(time); + start = micros(); + for(int i = 0; i < 100000; i++){ + temp += inoise16(i,i<<1,i<<2); + } + end = micros(); + time = end - start; + Serial.print("inoise16:"); + Serial.print(temp); + Serial.print("time: "); + Serial.println(time); + start = micros(); + for(int i = 0; i < 100000; i++){ + temp += perlin1D_raw(i); + } + end = micros(); + time = end - start; + Serial.print("perlin1D:"); + Serial.print(temp); + Serial.print("time: "); + Serial.println(time); + start = micros(); + for(int i = 0; i < 100000; i++){ + temp += perlin2D_raw(i,i*33); + } + end = micros(); + time = end - start; + Serial.print("perlin2D:"); + Serial.print(temp); + Serial.print("time: "); + Serial.println(time); + start = micros(); + for(int i = 0; i < 100000; i++){ + temp += perlin16(i,i*33,i*17); + } + end = micros(); + time = end - start; + Serial.print("perlin16:"); + Serial.print(temp); + Serial.print("time: "); + Serial.println(time); + + start = micros(); + for(int i = 0; i < 100000; i++){ + temp += perlin3D_raw(i,i*33,i*17); + } + end = micros(); + time = end - start; + Serial.print("perlin3D raw:"); + Serial.print(temp); + Serial.print("time: "); + Serial.println(time); + + DEBUG_PRINTF_P(PSTR("CPU: %s rev.%d, %d core(s), %d MHz.\n"), ESP.getChipModel(), (int)ESP.getChipRevision(), ESP.getChipCores(), ESP.getCpuFreqMHz()); DEBUG_PRINTF_P(PSTR("FLASH: %d MB, Mode %d "), (ESP.getFlashChipSize()/1024)/1024, (int)ESP.getFlashChipMode()); #ifdef WLED_DEBUG From 9553425374431d7adfff15c16a9bd22cdd52f2c6 Mon Sep 17 00:00:00 2001 From: Damian Schneider Date: Tue, 4 Mar 2025 07:55:41 +0100 Subject: [PATCH 2/7] some speed improvements using better hash, scaling is still off... needs a proper scaling analysis of all steps in all resolutions to minimize errors. --- wled00/util.cpp | 113 ++++++++++++++++++++---------------------------- wled00/wled.cpp | 56 ++++++++++++++++-------- 2 files changed, 85 insertions(+), 84 deletions(-) diff --git a/wled00/util.cpp b/wled00/util.cpp index d5341a96e..e7dbbca23 100644 --- a/wled00/util.cpp +++ b/wled00/util.cpp @@ -624,84 +624,63 @@ int32_t hw_random(int32_t lowerlimit, int32_t upperlimit) { * Note: optimized for speed and to mimic fastled inoise functions, not for accuracy or best randomness */ -// hash based gradient (speed is key) -static inline __attribute__((always_inline)) uint32_t perlinHash(uint32_t x) { - //x ^= x >> 15; //11? - //x *= 0x85ebca6b; - //x ^= x >> 7; - - //x *= 0xc2b2ae35; - //x ^= x >> 17; - //return x; - - //version from above, does not look too good - //x = ((x >> 16) ^ x) * 0x45d9f3b; - //x = ((x >> 16) ^ x) * 0x45d9f3b; - //return (x >> 16) ^ x; - -//found this online at https://github.com/skeeto/hash-prospector -// allegedly even better than the above murmur hash - x ^= x >> 16; - x *= 0x7feb352d; - x ^= x >> 15; - x *= 0x846ca68b; - x ^= x >> 16; - return x; - - -} - -//int8_t slopes[8] = {-4,-3,-2,-1,1,2,3,4}; -static inline __attribute__((always_inline)) int32_t cornergradient(uint32_t h) { - int grad = (h & 0x0F) - 8; // +7 to -8 - // int grad = slopes[h & 0x7]; // +1 or -1 (better mimics fastled but much slower, can be optimized by passing an array pointer or making the array global - //int grad = (h & 0x07) - 4; // +3 to -4 - //int grad = (h & 0x03) - 2; // +1 to -2 - //int grad = (h & 0x07) * (h & 0x10 ? -1 : 1); // symmetrical, much (!) slower - //return slopes[h & 0x7]; // lookup table is also very slow... - return grad; -} - // Gradient functions for 1D, 2D and 3D Perlin noise note: forcing inline produces smaller code and makes it 3x faster! static inline __attribute__((always_inline)) int32_t gradient1D(uint32_t x0, int32_t dx) { - int32_t grad = cornergradient(perlinHash(x0)); - return (grad * dx) >> 1; + //uint32_t hash ^= hash >> 16; + //hash *= 0x7feb352d; + //hash ^= hash >> 15; + //hash *= 0x846ca68b; + //hash ^= hash >> 16; + uint32_t hash = (x0 * 73856093); + hash ^= hash >> 15; + hash *= 0x92C3412B; + hash ^= hash >> 13; + int32_t gradx = (hash & 0xFF) - 128; // +127 to -128 + return (gradx * dx) >> 7; } static inline __attribute__((always_inline)) int32_t gradient2D(uint32_t x0, int32_t dx, uint32_t y0, int32_t dy) { - uint32_t hashx = perlinHash(x0); - //uint32_t hashy = perlinHash(hashx ^ y0); - uint32_t hashy = perlinHash(y0 + 1013904223UL); - int32_t gradx = cornergradient(hashx); - int32_t grady = cornergradient(hashy); - return (gradx * dx + grady * dy) >> 2; + //uint32_t hash = perlinHash(x0 ^ perlinHash(y0)); + // much faster and still decent entropy + uint32_t hash = (x0 * 73856093) ^ (y0 * 19349663); + hash ^= hash >> 15; + hash *= 0x92C3412B; + hash ^= hash >> 13; + // calculate gradients for each corner from hash value + int32_t gradx = (hash & 0xFF) - 128; // +127 to -128 + int32_t grady = ((hash>>7) & 0xFF) - 128; + return (gradx * dx + grady * dy) >> 9; } static inline __attribute__((always_inline)) int32_t gradient3D(uint32_t x0, int32_t dx, uint32_t y0, int32_t dy, uint32_t z0, int32_t dz) { - //uint32_t hashx = perlinHash(x0); - //uint32_t hashy = perlinHash(hashx ^ y0); - //uint32_t hashz = perlinHash(hashy ^ z0); + //uint32_t hash = perlinHash(x0 ^ perlinHash(y0 ^ perlinHash(z0))); - uint32_t hashx = perlinHash(x0); - uint32_t hashy = perlinHash(y0 + 1013904223UL); - uint32_t hashz = perlinHash(z0 + 1664525UL); + // fast and good entropy hash from corner coordinates + uint32_t hash = x0 * 0x68E31DA4 + y0 * 0xB5297A4D + z0 * 0x1B56C4E9; + hash ^= hash >> 8; + hash += hash << 3; + hash ^= hash >> 16; + // calculate gradients for each corner from hash value + //int32_t gradx = (hash & 0x07) - 4; // +3 to -4 + //int32_t grady = ((hash>>3) & 0x07) - 4; + //int32_t gradz = ((hash>>6) & 0x07) - 4; - int32_t gradx = cornergradient(hashx); - int32_t grady = cornergradient(hashy); - int32_t gradz = cornergradient(hashz); - return (gradx * dx + grady * dy + gradz * dz) >> 4; + int32_t gradx = (hash & 0xFF) - 128; // +127 to -128 + int32_t grady = ((hash>>7) & 0xFF) - 128; + int32_t gradz = ((hash>>14) & 0xFF) - 128; + return (gradx * dx + grady * dy + gradz * dz) >> 10; } // fast cubic smoothstep: t*(3 - 2t²), optimized for fixed point static uint32_t smoothstep(const uint32_t t) { uint32_t t_squared = (t * t) >> 16; uint32_t factor = (3 << 16) - ((t << 1)); - return (t_squared * factor) >> 17; // scale for best resolution without overflow + return (t_squared * factor) >> 16; } // simple linear interpolation for fixed-point values, scaled for perlin noise use static inline int32_t lerpPerlin(int32_t a, int32_t b, int32_t t) { - return a + (((b - a) * t) >> 15); + return a + (((b - a) * t) >> 16); } // 1D Perlin noise function that returns a value in range of approximately -32768 to +32768 @@ -710,13 +689,13 @@ int32_t perlin1D_raw(uint32_t x) { int32_t x0 = x >> 16; int32_t x1 = x0 + 1; int32_t dx0 = x & 0xFFFF; - int32_t dx1 = dx0 - 0xFFFF; + int32_t dx1 = dx0 - 0x10000; // gradient values for the two corners int32_t g0 = gradient1D(x0, dx0); int32_t g1 = gradient1D(x1, dx1); // interpolate and smooth function - int32_t t = smoothstep(dx0); - int32_t noise = lerpPerlin(g0, g1, t); + int32_t tx = smoothstep(dx0); + int32_t noise = lerpPerlin(g0, g1, tx); return noise; } @@ -728,8 +707,8 @@ int32_t perlin2D_raw(uint32_t x, uint32_t y) { int32_t y1 = y0 + 1; int32_t dx0 = x & 0xFFFF; int32_t dy0 = y & 0xFFFF; - int32_t dx1 = dx0 - 0xFFFF; - int32_t dy1 = dy0 - 0xFFFF; + int32_t dx1 = dx0 - 0x10000; + int32_t dy1 = dy0 - 0x10000; int32_t g00 = gradient2D(x0, dx0, y0, dy0); int32_t g10 = gradient2D(x1, dx1, y0, dy0); @@ -758,9 +737,9 @@ int32_t perlin3D_raw(uint32_t x, uint32_t y, uint32_t z) { int32_t dx0 = x & 0xFFFF; int32_t dy0 = y & 0xFFFF; int32_t dz0 = z & 0xFFFF; - int32_t dx1 = dx0 - 0xFFFF; - int32_t dy1 = dy0 - 0xFFFF; - int32_t dz1 = dz0 - 0xFFFF; + int32_t dx1 = dx0 - 0x10000; + int32_t dy1 = dy0 - 0x10000; + int32_t dz1 = dz0 - 0x10000; int32_t g000 = gradient3D(x0, dx0, y0, dy0, z0, dz0); int32_t g001 = gradient3D(x0, dx0, y0, dy0, z1, dz1); @@ -807,5 +786,5 @@ uint16_t perlin16(uint32_t x, uint32_t y) { } uint16_t perlin16(uint32_t x, uint32_t y, uint32_t z) { - return ((perlin3D_raw(x, y, z) * 70) >> 6) + 0x7FFF; + return (perlin3D_raw(x, y, z)>>1) + 0x7FFF; } \ No newline at end of file diff --git a/wled00/wled.cpp b/wled00/wled.cpp index e242d68f5..de36908ca 100644 --- a/wled00/wled.cpp +++ b/wled00/wled.cpp @@ -345,7 +345,7 @@ void WLED::setup() uint32_t end; uint32_t time; uint8_t offset = hw_random(); - +/* for(int i = 0; i < 0xFFFFF; i+=800) { Serial.print(inoise16(i, offset, (offset >> 3))); Serial.print(","); //x Serial.print(inoise16(offset, i, (offset >> 3))); Serial.print(","); //y @@ -354,8 +354,10 @@ void WLED::setup() Serial.print(perlin16(offset, i, (offset >> 3))); Serial.print(","); //y Serial.print(perlin16(offset, (offset >> 3), i)); Serial.print(","); //z Serial.print(inoise16(i, offset+i/4, i*2 + (offset >> 3))); Serial.print(","); //mixed mode - Serial.println(perlin16(i, offset+i/4, i*2 + (offset >> 3))); - } + Serial.print(perlin16(i, offset+i/4, i*2 + (offset >> 3))); Serial.print(","); + Serial.println(perlin3D_raw(i, offset+i/4, i*2 + (offset >> 3))); //raw + }*/ + /* for(int i = 0; i < 0x2FFFF; i+=100) { uint32_t pos = i + offset; @@ -387,7 +389,7 @@ void WLED::setup() int32_t minval=0xFFFFF; int32_t maxval=0; start = micros(); - for(int i = 0; i < 0xFFFFF; i+=100) { + for(int i = 0; i < 0xFFFFF; i+=50) { uint16_t pos = i + offset; int32_t noiseval = inoise8_raw(pos); if(noiseval < minval) minval = noiseval; @@ -400,9 +402,9 @@ void WLED::setup() minval=0xFFFFF; maxval=0; - /* + start = micros(); - for(int i = 0; i < 0xFFFFFF; i+=100) { + for(int i = 0; i < 0xFFFFFF; i+=50) { uint32_t pos = i + offset; //int32_t noiseval = inoise16(pos, pos+4684165, pos+985685); int32_t noiseval = inoise16(hw_random(), hw_random(), hw_random()); @@ -413,26 +415,27 @@ void WLED::setup() time = end - start; Serial.print("time: "); Serial.print(time); Serial.print(" inoise16_3D min: "); Serial.print(minval); Serial.print(" max: "); Serial.println(maxval); -*/ + minval=0xFFFFF; maxval=0; start = micros(); - for(int i = 0; i < 0xFFFFFFF; i+=100) { + for(int i = 0; i < 0xFFFFFFF; i+=5) { uint32_t pos = i + offset; - int32_t noiseval = perlin1D_raw( hw_random()); + //int32_t noiseval = perlin16(hw_random()); + int32_t noiseval = perlin1D_raw(hw_random()); if(noiseval < minval) minval = noiseval; if(noiseval > maxval) maxval = noiseval; } end = micros(); time = end - start; Serial.print("time: "); Serial.print(time); -Serial.print(" perlin1D_raw min: "); Serial.print(minval); Serial.print(" max: "); Serial.println(maxval); +Serial.print(" perlin1D raw min: "); Serial.print(minval); Serial.print(" max: "); Serial.println(maxval); minval=0xFFFFF; maxval=0; start = micros(); - for(int i = 0; i < 0xFFFFFFF; i+=100) { + for(int i = 0; i < 0xFFFFFFF; i+=5) { uint32_t pos = i + offset; - //int32_t noiseval = perlin2D_raw(pos, pos+6846354); + //int32_t noiseval = perlin16( hw_random(), hw_random()); int32_t noiseval = perlin2D_raw( hw_random(), hw_random()); if(noiseval < minval) minval = noiseval; if(noiseval > maxval) maxval = noiseval; @@ -440,10 +443,12 @@ Serial.print(" perlin1D_raw min: "); Serial.print(minval); Serial.print(" max: " end = micros(); time = end - start; Serial.print("time: "); Serial.print(time); - Serial.print(" perlin2D_raw min: "); Serial.print(minval); Serial.print(" max: "); Serial.println(maxval); + Serial.print(" perlin2D raw min: "); Serial.print(minval); Serial.print(" max: "); Serial.println(maxval); + + minval=0xFFFFF; maxval=0; - for(int i = 0; i < 0xFFFFFFF; i+=100) { + for(int i = 0; i < 0xFFFFFFF; i+=5) { uint32_t pos = i + offset; //int32_t noiseval = perlin3D_raw(pos, pos+46845, pos+654684); //int32_t noiseval = perlin3D_raw(hw_random(), hw_random(), hw_random()); @@ -456,6 +461,23 @@ Serial.print(" perlin1D_raw min: "); Serial.print(minval); Serial.print(" max: " Serial.print("time: "); Serial.print(time); Serial.print(" perlin16 min: "); Serial.print(minval); Serial.print(" max: "); Serial.println(maxval); + minval=0xFFFFF; + maxval=0; + for(int i = 0; i < 0xFFFFFFF; i+=5) { + uint32_t pos = i + offset; + //int32_t noiseval = perlin3D_raw(pos, pos+46845, pos+654684); + int32_t noiseval = perlin3D_raw(hw_random(), hw_random(), hw_random()); + //int32_t noiseval = perlin16(hw_random(), hw_random(), hw_random()); + if(noiseval < minval) minval = noiseval; + if(noiseval > maxval) maxval = noiseval; + } + end = micros(); + time = end - start; + Serial.print("time: "); Serial.print(time); + Serial.print(" perlin3D_raw min: "); Serial.print(minval); Serial.print(" max: "); Serial.println(maxval); + + + volatile uint32_t temp; start = micros(); for(int i = 0; i < 100000; i++){ @@ -483,7 +505,7 @@ Serial.print(" perlin1D_raw min: "); Serial.print(minval); Serial.print(" max: " } end = micros(); time = end - start; - Serial.print("perlin1D:"); + Serial.print("perlin1Draw:"); Serial.print(temp); Serial.print("time: "); Serial.println(time); @@ -493,7 +515,7 @@ Serial.print(" perlin1D_raw min: "); Serial.print(minval); Serial.print(" max: " } end = micros(); time = end - start; - Serial.print("perlin2D:"); + Serial.print("perlin2Draw:"); Serial.print(temp); Serial.print("time: "); Serial.println(time); @@ -503,7 +525,7 @@ Serial.print(" perlin1D_raw min: "); Serial.print(minval); Serial.print(" max: " } end = micros(); time = end - start; - Serial.print("perlin16:"); + Serial.print("perlin163D:"); Serial.print(temp); Serial.print("time: "); Serial.println(time); From 5e8073022bff7df19a54de4afc39dd5aa257ea26 Mon Sep 17 00:00:00 2001 From: Damian Schneider Date: Fri, 7 Mar 2025 06:39:49 +0100 Subject: [PATCH 3/7] 3D works but needs finetuning to match old looks --- wled00/util.cpp | 31 ++++++++++++------------------- wled00/wled.cpp | 18 +++++++++--------- 2 files changed, 21 insertions(+), 28 deletions(-) diff --git a/wled00/util.cpp b/wled00/util.cpp index e7dbbca23..c04915031 100644 --- a/wled00/util.cpp +++ b/wled00/util.cpp @@ -653,34 +653,27 @@ static inline __attribute__((always_inline)) int32_t gradient2D(uint32_t x0, int } static inline __attribute__((always_inline)) int32_t gradient3D(uint32_t x0, int32_t dx, uint32_t y0, int32_t dy, uint32_t z0, int32_t dz) { - //uint32_t hash = perlinHash(x0 ^ perlinHash(y0 ^ perlinHash(z0))); - - // fast and good entropy hash from corner coordinates - uint32_t hash = x0 * 0x68E31DA4 + y0 * 0xB5297A4D + z0 * 0x1B56C4E9; - hash ^= hash >> 8; - hash += hash << 3; - hash ^= hash >> 16; - // calculate gradients for each corner from hash value - //int32_t gradx = (hash & 0x07) - 4; // +3 to -4 - //int32_t grady = ((hash>>3) & 0x07) - 4; - //int32_t gradz = ((hash>>6) & 0x07) - 4; + // fast and good entropy hash from corner coordinates + uint32_t h = (x0 * 0x68E31DA4) ^ (y0 * 0xB5297A4D) ^ (z0 * 0x1B56C4E9); + h ^= h >> 15; + h = h * 0x92C3412B + (h >> 13); - int32_t gradx = (hash & 0xFF) - 128; // +127 to -128 - int32_t grady = ((hash>>7) & 0xFF) - 128; - int32_t gradz = ((hash>>14) & 0xFF) - 128; - return (gradx * dx + grady * dy + gradz * dz) >> 10; + int32_t gradx = (h & 0xFF) - 128; // +127 to -128 + int32_t grady = ((h>>7) & 0xFF) - 128; + int32_t gradz = ((h>>14) & 0xFF) - 128; + return (gradx * dx + grady * dy + gradz * dz) >> 8; // 25bit >> 8bit -> result is signed 17bit max } -// fast cubic smoothstep: t*(3 - 2t²), optimized for fixed point +// fast cubic smoothstep: t*(3 - 2t²), optimized for fixed point, scaled to avoid overflows static uint32_t smoothstep(const uint32_t t) { uint32_t t_squared = (t * t) >> 16; uint32_t factor = (3 << 16) - ((t << 1)); - return (t_squared * factor) >> 16; + return (t_squared * factor) >> 19; } // simple linear interpolation for fixed-point values, scaled for perlin noise use static inline int32_t lerpPerlin(int32_t a, int32_t b, int32_t t) { - return a + (((b - a) * t) >> 16); + return a + (((b - a) * t) >> 13); } // 1D Perlin noise function that returns a value in range of approximately -32768 to +32768 @@ -786,5 +779,5 @@ uint16_t perlin16(uint32_t x, uint32_t y) { } uint16_t perlin16(uint32_t x, uint32_t y, uint32_t z) { - return (perlin3D_raw(x, y, z)>>1) + 0x7FFF; + return perlin3D_raw(x, y, z) + 0x7FFF; // scale to signed 16bit range and offset } \ No newline at end of file diff --git a/wled00/wled.cpp b/wled00/wled.cpp index de36908ca..639b7fe0c 100644 --- a/wled00/wled.cpp +++ b/wled00/wled.cpp @@ -345,18 +345,18 @@ void WLED::setup() uint32_t end; uint32_t time; uint8_t offset = hw_random(); -/* - for(int i = 0; i < 0xFFFFF; i+=800) { + + for(int i = 0; i < 0xFFFFF; i+=500) { Serial.print(inoise16(i, offset, (offset >> 3))); Serial.print(","); //x Serial.print(inoise16(offset, i, (offset >> 3))); Serial.print(","); //y Serial.print(inoise16(offset, (offset >> 3), i)); Serial.print(","); //z Serial.print(perlin16(i, offset, (offset >> 3))); Serial.print(","); //x Serial.print(perlin16(offset, i, (offset >> 3))); Serial.print(","); //y Serial.print(perlin16(offset, (offset >> 3), i)); Serial.print(","); //z - Serial.print(inoise16(i, offset+i/4, i*2 + (offset >> 3))); Serial.print(","); //mixed mode - Serial.print(perlin16(i, offset+i/4, i*2 + (offset >> 3))); Serial.print(","); + Serial.print(inoise16(i, offset+i/2, i + (offset >> 3))); Serial.print(","); //mixed mode + Serial.print(perlin16(i, offset+i/2, i + (offset >> 3))); Serial.print(","); Serial.println(perlin3D_raw(i, offset+i/4, i*2 + (offset >> 3))); //raw - }*/ + } /* for(int i = 0; i < 0x2FFFF; i+=100) { @@ -419,7 +419,7 @@ void WLED::setup() minval=0xFFFFF; maxval=0; start = micros(); - for(int i = 0; i < 0xFFFFFFF; i+=5) { + for(int i = 0; i < 0xFFFFFFF; i+=50) { uint32_t pos = i + offset; //int32_t noiseval = perlin16(hw_random()); int32_t noiseval = perlin1D_raw(hw_random()); @@ -433,7 +433,7 @@ Serial.print(" perlin1D raw min: "); Serial.print(minval); Serial.print(" max: " minval=0xFFFFF; maxval=0; start = micros(); - for(int i = 0; i < 0xFFFFFFF; i+=5) { + for(int i = 0; i < 0xFFFFFFF; i+=50) { uint32_t pos = i + offset; //int32_t noiseval = perlin16( hw_random(), hw_random()); int32_t noiseval = perlin2D_raw( hw_random(), hw_random()); @@ -448,7 +448,7 @@ Serial.print(" perlin1D raw min: "); Serial.print(minval); Serial.print(" max: " minval=0xFFFFF; maxval=0; - for(int i = 0; i < 0xFFFFFFF; i+=5) { + for(int i = 0; i < 0xFFFFFFF; i+=50) { uint32_t pos = i + offset; //int32_t noiseval = perlin3D_raw(pos, pos+46845, pos+654684); //int32_t noiseval = perlin3D_raw(hw_random(), hw_random(), hw_random()); @@ -463,7 +463,7 @@ Serial.print(" perlin1D raw min: "); Serial.print(minval); Serial.print(" max: " minval=0xFFFFF; maxval=0; - for(int i = 0; i < 0xFFFFFFF; i+=5) { + for(int i = 0; i < 0xFFFFFFF; i+=50) { uint32_t pos = i + offset; //int32_t noiseval = perlin3D_raw(pos, pos+46845, pos+654684); int32_t noiseval = perlin3D_raw(hw_random(), hw_random(), hw_random()); From 4ecc531998463d6895fbe5e7e620690cc702b04c Mon Sep 17 00:00:00 2001 From: Damian Schneider Date: Sat, 8 Mar 2025 12:48:27 +0100 Subject: [PATCH 4/7] updated scaling, improved hashing, updated rotozoomer to not use a buffer --- wled00/FX.cpp | 23 ++++----- wled00/fcn_declare.h | 6 +-- wled00/util.cpp | 110 ++++++++++++++++++++++++------------------- wled00/wled.cpp | 58 ++++++++++++++++------- 4 files changed, 113 insertions(+), 84 deletions(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index a17d48603..1bba265dc 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -6221,22 +6221,12 @@ uint16_t mode_2Dplasmarotozoom() { const int cols = SEG_W; const int rows = SEG_H; - unsigned dataSize = SEGMENT.length() + sizeof(float); + unsigned dataSize = sizeof(float); if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed float *a = reinterpret_cast(SEGENV.data); - byte *plasma = reinterpret_cast(SEGENV.data+sizeof(float)); unsigned ms = strip.now/15; - // plasma - for (int j = 0; j < rows; j++) { - int index = j*cols; - for (int i = 0; i < cols; i++) { - if (SEGMENT.check1) plasma[index+i] = (i * 4 ^ j * 4) + ms / 6; - else plasma[index+i] = perlin8(i * 40, j * 40, ms); - } - } - // rotozoom float f = (sin_t(*a/2)+((128-SEGMENT.intensity)/128.0f)+1.1f)/1.5f; // scale factor float kosinus = cos_t(*a) * f; @@ -6245,9 +6235,14 @@ uint16_t mode_2Dplasmarotozoom() { float u1 = i * kosinus; float v1 = i * sinus; for (int j = 0; j < rows; j++) { - byte u = abs8(u1 - j * sinus) % cols; - byte v = abs8(v1 + j * kosinus) % rows; - SEGMENT.setPixelColorXY(i, j, SEGMENT.color_from_palette(plasma[v*cols+u], false, PALETTE_SOLID_WRAP, 255)); + unsigned u = abs8(u1 - j * sinus) % cols; + unsigned v = abs8(v1 + j * kosinus) % rows; + byte plasma; + if (SEGMENT.check1) plasma = (u * 4 ^ v * 4) + ms / 6; + else plasma = perlin8(u * 40, v * 40, ms); + //else plasma = inoise8(u * SEGMENT.intensity, v * SEGMENT.intensity, ms); + //SEGMENT.setPixelColorXY(i, j, SEGMENT.color_from_palette(plasma[v*cols+u], false, PALETTE_SOLID_WRAP, 255)); + SEGMENT.setPixelColorXY(i, j, SEGMENT.color_from_palette(plasma, false, PALETTE_SOLID_WRAP, 255)); } } *a -= 0.03f + float(SEGENV.speed-128)*0.0002f; // rotation speed diff --git a/wled00/fcn_declare.h b/wled00/fcn_declare.h index 4d5557fda..7ee87eaee 100644 --- a/wled00/fcn_declare.h +++ b/wled00/fcn_declare.h @@ -516,9 +516,9 @@ void enumerateLedmaps(); [[gnu::hot]] uint8_t get_random_wheel_index(uint8_t pos); [[gnu::hot, gnu::pure]] float mapf(float x, float in_min, float in_max, float out_min, float out_max); uint32_t hashInt(uint32_t s); -int32_t perlin1D_raw(uint32_t x); -int32_t perlin2D_raw(uint32_t x, uint32_t y); -int32_t perlin3D_raw(uint32_t x, uint32_t y, uint32_t z); +int32_t perlin1D_raw(uint32_t x, bool is16bit = false); +int32_t perlin2D_raw(uint32_t x, uint32_t y, bool is16bit = false); +int32_t perlin3D_raw(uint32_t x, uint32_t y, uint32_t z, bool is16bit = false); uint8_t perlin8(uint16_t x); uint8_t perlin8(uint16_t x, uint16_t y); uint8_t perlin8(uint16_t x, uint16_t y, uint16_t z); diff --git a/wled00/util.cpp b/wled00/util.cpp index c04915031..33973303b 100644 --- a/wled00/util.cpp +++ b/wled00/util.cpp @@ -623,64 +623,63 @@ int32_t hw_random(int32_t lowerlimit, int32_t upperlimit) { * Fixed point integer based Perlin noise functions by @dedehai * Note: optimized for speed and to mimic fastled inoise functions, not for accuracy or best randomness */ +#define PERLIN_SHIFT 1 + +// calculate gradient for corner from hash value +static inline __attribute__((always_inline)) int32_t hashToGradient(uint32_t h) { + // using more steps yields more "detailed" perlin noise but looks less like the original fastled version (adjust PERLIN_SHIFT to compensate) + //return (h & 0xFF) - 128; // use PERLIN_SHIFT 7 + //return (h & 0x0F) - 8; // use PERLIN_SHIFT 3 + //return (h & 0x07) - 4; // use PERLIN_SHIFT 2 + return (h & 0x03) - 2; // use PERLIN_SHIFT 1 +} // Gradient functions for 1D, 2D and 3D Perlin noise note: forcing inline produces smaller code and makes it 3x faster! static inline __attribute__((always_inline)) int32_t gradient1D(uint32_t x0, int32_t dx) { - //uint32_t hash ^= hash >> 16; - //hash *= 0x7feb352d; - //hash ^= hash >> 15; - //hash *= 0x846ca68b; - //hash ^= hash >> 16; - uint32_t hash = (x0 * 73856093); - hash ^= hash >> 15; - hash *= 0x92C3412B; - hash ^= hash >> 13; - int32_t gradx = (hash & 0xFF) - 128; // +127 to -128 - return (gradx * dx) >> 7; + uint32_t h = x0 * 0x27D4EB2D; + h ^= h >> 15; + h *= 0x92C3412B; + h ^= h >> 13; + h ^= h >> 7; + return (hashToGradient(h) * dx) >> PERLIN_SHIFT; } static inline __attribute__((always_inline)) int32_t gradient2D(uint32_t x0, int32_t dx, uint32_t y0, int32_t dy) { - //uint32_t hash = perlinHash(x0 ^ perlinHash(y0)); - // much faster and still decent entropy - uint32_t hash = (x0 * 73856093) ^ (y0 * 19349663); - hash ^= hash >> 15; - hash *= 0x92C3412B; - hash ^= hash >> 13; - // calculate gradients for each corner from hash value - int32_t gradx = (hash & 0xFF) - 128; // +127 to -128 - int32_t grady = ((hash>>7) & 0xFF) - 128; - return (gradx * dx + grady * dy) >> 9; + uint32_t h = (x0 * 0x27D4EB2D) ^ (y0 * 0xB5297A4D); + h ^= h >> 15; + h *= 0x92C3412B; + h ^= h >> 13; + return (hashToGradient(h) * dx + hashToGradient(h>>PERLIN_SHIFT) * dy) >> (1 + PERLIN_SHIFT); } static inline __attribute__((always_inline)) int32_t gradient3D(uint32_t x0, int32_t dx, uint32_t y0, int32_t dy, uint32_t z0, int32_t dz) { - // fast and good entropy hash from corner coordinates - uint32_t h = (x0 * 0x68E31DA4) ^ (y0 * 0xB5297A4D) ^ (z0 * 0x1B56C4E9); + // fast and good entropy hash from corner coordinates + uint32_t h = (x0 * 0x27D4EB2D) ^ (y0 * 0xB5297A4D) ^ (z0 * 0x1B56C4E9); h ^= h >> 15; - h = h * 0x92C3412B + (h >> 13); - - int32_t gradx = (h & 0xFF) - 128; // +127 to -128 - int32_t grady = ((h>>7) & 0xFF) - 128; - int32_t gradz = ((h>>14) & 0xFF) - 128; - return (gradx * dx + grady * dy + gradz * dz) >> 8; // 25bit >> 8bit -> result is signed 17bit max + h *= 0x92C3412B; + h ^= h >> 13; + return ((hashToGradient(h) * dx + hashToGradient(h>>(1+PERLIN_SHIFT)) * dy + hashToGradient(h>>(1 + 2*PERLIN_SHIFT)) * dz) * 85) >> (8 + PERLIN_SHIFT); // scale to 16bit, x*85 >> 8 = x/3 } // fast cubic smoothstep: t*(3 - 2t²), optimized for fixed point, scaled to avoid overflows static uint32_t smoothstep(const uint32_t t) { uint32_t t_squared = (t * t) >> 16; uint32_t factor = (3 << 16) - ((t << 1)); - return (t_squared * factor) >> 19; + return (t_squared * factor) >> 18; // scale to avoid overflows } // simple linear interpolation for fixed-point values, scaled for perlin noise use static inline int32_t lerpPerlin(int32_t a, int32_t b, int32_t t) { - return a + (((b - a) * t) >> 13); + return a + (((b - a) * t) >> 14); // match scaling with smoothstep to yield 16.16bit values } // 1D Perlin noise function that returns a value in range of approximately -32768 to +32768 -int32_t perlin1D_raw(uint32_t x) { +int32_t perlin1D_raw(uint32_t x, bool is16bit) { // integer and fractional part coordinates int32_t x0 = x >> 16; int32_t x1 = x0 + 1; + if(is16bit) x1 = x1 & 0xFF; // wrap back to zero at 0xFF instead of 0xFFFF + int32_t dx0 = x & 0xFFFF; int32_t dx1 = dx0 - 0x10000; // gradient values for the two corners @@ -693,11 +692,17 @@ int32_t perlin1D_raw(uint32_t x) { } // 2D Perlin noise function that returns a value in range of approximately -32768 to +32768 -int32_t perlin2D_raw(uint32_t x, uint32_t y) { +int32_t perlin2D_raw(uint32_t x, uint32_t y, bool is16bit) { int32_t x0 = x >> 16; int32_t y0 = y >> 16; int32_t x1 = x0 + 1; int32_t y1 = y0 + 1; + + if(is16bit) { + x1 = x1 & 0xFF; // wrap back to zero at 0xFF instead of 0xFFFF + y1 = y1 & 0xFF; + } + int32_t dx0 = x & 0xFFFF; int32_t dy0 = y & 0xFFFF; int32_t dx1 = dx0 - 0x10000; @@ -718,8 +723,7 @@ int32_t perlin2D_raw(uint32_t x, uint32_t y) { return noise; } -// 2D Perlin noise function that returns a value in range of approximately -40000 to +40000 -int32_t perlin3D_raw(uint32_t x, uint32_t y, uint32_t z) { +int32_t perlin3D_raw(uint32_t x, uint32_t y, uint32_t z, bool is16bit) { int32_t x0 = x >> 16; int32_t y0 = y >> 16; int32_t z0 = z >> 16; @@ -727,6 +731,12 @@ int32_t perlin3D_raw(uint32_t x, uint32_t y, uint32_t z) { int32_t y1 = y0 + 1; int32_t z1 = z0 + 1; + if(is16bit) { + x1 = x1 & 0xFF; // wrap back to zero at 0xFF instead of 0xFFFF + y1 = y1 & 0xFF; + z1 = z1 & 0xFF; + } + int32_t dx0 = x & 0xFFFF; int32_t dy0 = y & 0xFFFF; int32_t dz0 = z & 0xFFFF; @@ -758,26 +768,28 @@ int32_t perlin3D_raw(uint32_t x, uint32_t y, uint32_t z) { return noise; } // scaling functions for fastled replacement -uint8_t perlin8(uint16_t x) { - return (perlin1D_raw(uint32_t(x) << 8) >> 8) + 0x7F; -} - -uint8_t perlin8(uint16_t x, uint16_t y) { - return uint8_t((perlin2D_raw(uint32_t(x)<<8, uint32_t(y)<<8) >> 8) + 0x7F); -} - -uint8_t perlin8(uint16_t x, uint16_t y, uint16_t z) { - return ((perlin3D_raw(uint32_t(x)<<8, uint32_t(y)<<8, uint32_t(z)<<8) * 85) >> 15) + 0x7F; -} uint16_t perlin16(uint32_t x) { - return perlin1D_raw(x) + 0x7FFF; + //return ((perlin1D_raw(x) * 1168) >> 10) + 0x7FFF; //scale to 16bit and offset (full range) + return ((perlin1D_raw(x) * 895) >> 10) + 34616; //scale to 16bit and offset (fastled range) } uint16_t perlin16(uint32_t x, uint32_t y) { - return perlin2D_raw(x, y) + 0x7FFF; + return ((perlin2D_raw(x, y) * 1359) >> 10) + 31508; //scale to 16bit and offset (empirical values with some overflow safety margin) } uint16_t perlin16(uint32_t x, uint32_t y, uint32_t z) { - return perlin3D_raw(x, y, z) + 0x7FFF; // scale to signed 16bit range and offset + return ((perlin3D_raw(x, y, z) * 1923) >> 10) + 31290; //scale to 16bit and offset (empirical values with some overflow safety margin) +} + +uint8_t perlin8(uint16_t x) { + return (((perlin1D_raw((uint32_t)x << 8, true) * 1168) >> 10) + 0x7FFF) >> 8; +} + +uint8_t perlin8(uint16_t x, uint16_t y) { + return (((perlin2D_raw((uint32_t)x << 8, (uint32_t)y << 8, true) * 1359) >> 10) + 31508) >> 8; +} + +uint8_t perlin8(uint16_t x, uint16_t y, uint16_t z) { + return (((perlin3D_raw((uint32_t)x << 8, (uint32_t)y << 8, (uint32_t)z << 8, true) * 1923) >> 10) + 31290) >> 8; //scale to 8bit } \ No newline at end of file diff --git a/wled00/wled.cpp b/wled00/wled.cpp index 639b7fe0c..ea615f3cb 100644 --- a/wled00/wled.cpp +++ b/wled00/wled.cpp @@ -344,19 +344,41 @@ void WLED::setup() uint32_t start; uint32_t end; uint32_t time; - uint8_t offset = hw_random(); + uint8_t offset = hw_random()+hw_random(); + delay(2000); + /* +//online serial plotter: https://sekigon-gonnoc.github.io/web-serial-plotter/ format is "valueA:213423, ValueB:123123, \n" + for(int i = 0; i < 0xFFFFFFF; i+=10) { + //Serial.print(inoise16(i, offset, (offset >> 3))); Serial.print(" "); //x + //Serial.print(inoise16(offset, i, (offset >> 3))); Serial.print(" "); //y + //Serial.print(inoise16(offset, (offset >> 3), i)); Serial.print(" "); //z + //Serial.print(perlin16(i, offset, (offset >> 3))); Serial.print(" "); //x + //Serial.print(perlin16(offset, i, (offset >> 3))); Serial.print(" "); //y + //Serial.print(perlin16(offset, (offset >> 3), i)); Serial.print(" "); //z + + //Serial.print("Fastled:");Serial.print(inoise16(i, offset+i/2, i + (offset >> 3))); Serial.print(", "); //mixed mode + //Serial.print("New:");Serial.println(perlin16(i, offset+i/2, i + (offset >> 3)));// Serial.println(", "); + + //Serial.print("Fastled:");Serial.print(inoise16(i, offset+i/2)); Serial.print(", "); //mixed mode + //Serial.print("New:");Serial.println(perlin16(i, offset+i/2));// Serial.println(", "); - for(int i = 0; i < 0xFFFFF; i+=500) { - Serial.print(inoise16(i, offset, (offset >> 3))); Serial.print(","); //x - Serial.print(inoise16(offset, i, (offset >> 3))); Serial.print(","); //y - Serial.print(inoise16(offset, (offset >> 3), i)); Serial.print(","); //z - Serial.print(perlin16(i, offset, (offset >> 3))); Serial.print(","); //x - Serial.print(perlin16(offset, i, (offset >> 3))); Serial.print(","); //y - Serial.print(perlin16(offset, (offset >> 3), i)); Serial.print(","); //z - Serial.print(inoise16(i, offset+i/2, i + (offset >> 3))); Serial.print(","); //mixed mode - Serial.print(perlin16(i, offset+i/2, i + (offset >> 3))); Serial.print(","); - Serial.println(perlin3D_raw(i, offset+i/4, i*2 + (offset >> 3))); //raw - } + //Serial.print("Fastled:");Serial.print(inoise16(i)); Serial.print(", "); //mixed mode + //Serial.print("New:");Serial.println(perlin16(i));// Serial.println(", "); + + Serial.print("Fastled3D:");Serial.print(inoise8(i, offset+i/2, i + (offset >> 3))); Serial.print(", "); //mixed mode + Serial.print("New3D:");Serial.print(perlin8(i, offset+i/2, i + (offset >> 3)));// Serial.println(", "); + Serial.print(", "); + Serial.print("Fastled2D:");Serial.print(inoise8(i, offset+i/2)); Serial.print(", "); //mixed mode + Serial.print("New2D:");Serial.print(perlin8(i, offset+i/2));// Serial.println(", "); + Serial.print(", "); + Serial.print("Fastled1D:");Serial.print(inoise8(i)); Serial.print(", "); //mixed mode + Serial.print("New1D:");Serial.println(perlin8(i));// Serial.println(", "); + + //Serial.print(inoise16(i, offset+i/2, i + (offset >> 3))); Serial.print(","); //mixed mode + //Serial.println(perlin16(i, offset+i/2, i + (offset >> 3)));// Serial.println(", "); + //delay(10); + // Serial.println(perlin3D_raw(i, offset+i/4, i*2 + (offset >> 3))); //raw + }*/ /* for(int i = 0; i < 0x2FFFF; i+=100) { @@ -419,10 +441,10 @@ void WLED::setup() minval=0xFFFFF; maxval=0; start = micros(); - for(int i = 0; i < 0xFFFFFFF; i+=50) { + for(int i = 0; i < 0xFFFFFF; i+=50) { uint32_t pos = i + offset; //int32_t noiseval = perlin16(hw_random()); - int32_t noiseval = perlin1D_raw(hw_random()); + int32_t noiseval = perlin1D_raw(hw_random(),false); if(noiseval < minval) minval = noiseval; if(noiseval > maxval) maxval = noiseval; } @@ -433,7 +455,7 @@ Serial.print(" perlin1D raw min: "); Serial.print(minval); Serial.print(" max: " minval=0xFFFFF; maxval=0; start = micros(); - for(int i = 0; i < 0xFFFFFFF; i+=50) { + for(int i = 0; i < 0xFFFFFF; i+=50) { uint32_t pos = i + offset; //int32_t noiseval = perlin16( hw_random(), hw_random()); int32_t noiseval = perlin2D_raw( hw_random(), hw_random()); @@ -448,7 +470,7 @@ Serial.print(" perlin1D raw min: "); Serial.print(minval); Serial.print(" max: " minval=0xFFFFF; maxval=0; - for(int i = 0; i < 0xFFFFFFF; i+=50) { + for(int i = 0; i < 0xFFFFFF; i+=50) { uint32_t pos = i + offset; //int32_t noiseval = perlin3D_raw(pos, pos+46845, pos+654684); //int32_t noiseval = perlin3D_raw(hw_random(), hw_random(), hw_random()); @@ -463,10 +485,10 @@ Serial.print(" perlin1D raw min: "); Serial.print(minval); Serial.print(" max: " minval=0xFFFFF; maxval=0; - for(int i = 0; i < 0xFFFFFFF; i+=50) { + for(int i = 0; i < 0xFFFFFF; i+=50) { uint32_t pos = i + offset; //int32_t noiseval = perlin3D_raw(pos, pos+46845, pos+654684); - int32_t noiseval = perlin3D_raw(hw_random(), hw_random(), hw_random()); + int32_t noiseval = perlin3D_raw(hw_random(), hw_random(), hw_random(),false); //int32_t noiseval = perlin16(hw_random(), hw_random(), hw_random()); if(noiseval < minval) minval = noiseval; if(noiseval > maxval) maxval = noiseval; From 95dcb03f6dcc058e3e7c56460a39ffe09b56e1e2 Mon Sep 17 00:00:00 2001 From: Damian Schneider Date: Wed, 12 Mar 2025 06:56:33 +0100 Subject: [PATCH 5/7] updated scaling --- wled00/fcn_declare.h | 6 +- wled00/util.cpp | 28 ++++-- wled00/wled.cpp | 225 ------------------------------------------- 3 files changed, 23 insertions(+), 236 deletions(-) diff --git a/wled00/fcn_declare.h b/wled00/fcn_declare.h index 7ee87eaee..9bc323d7f 100644 --- a/wled00/fcn_declare.h +++ b/wled00/fcn_declare.h @@ -519,12 +519,12 @@ uint32_t hashInt(uint32_t s); int32_t perlin1D_raw(uint32_t x, bool is16bit = false); int32_t perlin2D_raw(uint32_t x, uint32_t y, bool is16bit = false); int32_t perlin3D_raw(uint32_t x, uint32_t y, uint32_t z, bool is16bit = false); -uint8_t perlin8(uint16_t x); -uint8_t perlin8(uint16_t x, uint16_t y); -uint8_t perlin8(uint16_t x, uint16_t y, uint16_t z); uint16_t perlin16(uint32_t x); uint16_t perlin16(uint32_t x, uint32_t y); uint16_t perlin16(uint32_t x, uint32_t y, uint32_t z); +uint8_t perlin8(uint16_t x); +uint8_t perlin8(uint16_t x, uint16_t y); +uint8_t perlin8(uint16_t x, uint16_t y, uint16_t z); // fast (true) random numbers using hardware RNG, all functions return values in the range lowerlimit to upperlimit-1 // note: for true random numbers with high entropy, do not call faster than every 200ns (5MHz) diff --git a/wled00/util.cpp b/wled00/util.cpp index 33973303b..07c0db121 100644 --- a/wled00/util.cpp +++ b/wled00/util.cpp @@ -631,7 +631,7 @@ static inline __attribute__((always_inline)) int32_t hashToGradient(uint32_t h) //return (h & 0xFF) - 128; // use PERLIN_SHIFT 7 //return (h & 0x0F) - 8; // use PERLIN_SHIFT 3 //return (h & 0x07) - 4; // use PERLIN_SHIFT 2 - return (h & 0x03) - 2; // use PERLIN_SHIFT 1 + return (h & 0x03) - 2; // use PERLIN_SHIFT 1 -> closest to original fastled version } // Gradient functions for 1D, 2D and 3D Perlin noise note: forcing inline produces smaller code and makes it 3x faster! @@ -658,6 +658,17 @@ static inline __attribute__((always_inline)) int32_t gradient3D(uint32_t x0, int h ^= h >> 15; h *= 0x92C3412B; h ^= h >> 13; + +/* + // fastled version: 25% slower but gives original "look" + h = h&15; + int32_t u = h<8?dx:dy; + int32_t v = h<4?dy:h==12||h==14?dx:dz; + if(h&1) { u = -u; } + if(h&2) { v = -v; } + return (u >> 1) + (v >> 1) + (u & 0x1); +*/ + // closer to actual perlin version return ((hashToGradient(h) * dx + hashToGradient(h>>(1+PERLIN_SHIFT)) * dy + hashToGradient(h>>(1 + 2*PERLIN_SHIFT)) * dz) * 85) >> (8 + PERLIN_SHIFT); // scale to 16bit, x*85 >> 8 = x/3 } @@ -665,7 +676,7 @@ static inline __attribute__((always_inline)) int32_t gradient3D(uint32_t x0, int static uint32_t smoothstep(const uint32_t t) { uint32_t t_squared = (t * t) >> 16; uint32_t factor = (3 << 16) - ((t << 1)); - return (t_squared * factor) >> 18; // scale to avoid overflows + return (t_squared * factor) >> 18; // scale to avoid overflows and give best resolution } // simple linear interpolation for fixed-point values, scaled for perlin noise use @@ -771,25 +782,26 @@ int32_t perlin3D_raw(uint32_t x, uint32_t y, uint32_t z, bool is16bit) { uint16_t perlin16(uint32_t x) { //return ((perlin1D_raw(x) * 1168) >> 10) + 0x7FFF; //scale to 16bit and offset (full range) - return ((perlin1D_raw(x) * 895) >> 10) + 34616; //scale to 16bit and offset (fastled range) + //return ((perlin1D_raw(x) * 895) >> 10) + 34616; //scale to 16bit and offset (fastled range) -> 8 steps + return ((perlin1D_raw(x) * 1159) >> 10) + 32803; //scale to 16bit and offset (fastled range) -> 8 steps } uint16_t perlin16(uint32_t x, uint32_t y) { - return ((perlin2D_raw(x, y) * 1359) >> 10) + 31508; //scale to 16bit and offset (empirical values with some overflow safety margin) + return ((perlin2D_raw(x, y) * 1537) >> 10) + 32725; //scale to 16bit and offset (empirical values with some overflow safety margin) } uint16_t perlin16(uint32_t x, uint32_t y, uint32_t z) { - return ((perlin3D_raw(x, y, z) * 1923) >> 10) + 31290; //scale to 16bit and offset (empirical values with some overflow safety margin) + return ((perlin3D_raw(x, y, z) * 1731) >> 10) + 33147; //scale to 16bit and offset (empirical values with some overflow safety margin) } uint8_t perlin8(uint16_t x) { - return (((perlin1D_raw((uint32_t)x << 8, true) * 1168) >> 10) + 0x7FFF) >> 8; + return (((perlin1D_raw((uint32_t)x << 8, true) * 1353) >> 10) + 32769) >> 8; } uint8_t perlin8(uint16_t x, uint16_t y) { - return (((perlin2D_raw((uint32_t)x << 8, (uint32_t)y << 8, true) * 1359) >> 10) + 31508) >> 8; + return (((perlin2D_raw((uint32_t)x << 8, (uint32_t)y << 8, true) * 1620) >> 10) + 32771) >> 8; } uint8_t perlin8(uint16_t x, uint16_t y, uint16_t z) { - return (((perlin3D_raw((uint32_t)x << 8, (uint32_t)y << 8, (uint32_t)z << 8, true) * 1923) >> 10) + 31290) >> 8; //scale to 8bit + return (((perlin3D_raw((uint32_t)x << 8, (uint32_t)y << 8, (uint32_t)z << 8, true) * 2015) >> 10) + 33168) >> 8; //scale to 8bit } \ No newline at end of file diff --git a/wled00/wled.cpp b/wled00/wled.cpp index ea615f3cb..1d42e8c11 100644 --- a/wled00/wled.cpp +++ b/wled00/wled.cpp @@ -339,231 +339,6 @@ void WLED::setup() #else DEBUG_PRINTLN(F("arduino-esp32 v1.0.x\n")); // we can't say in more detail. #endif - - - uint32_t start; - uint32_t end; - uint32_t time; - uint8_t offset = hw_random()+hw_random(); - delay(2000); - /* -//online serial plotter: https://sekigon-gonnoc.github.io/web-serial-plotter/ format is "valueA:213423, ValueB:123123, \n" - for(int i = 0; i < 0xFFFFFFF; i+=10) { - //Serial.print(inoise16(i, offset, (offset >> 3))); Serial.print(" "); //x - //Serial.print(inoise16(offset, i, (offset >> 3))); Serial.print(" "); //y - //Serial.print(inoise16(offset, (offset >> 3), i)); Serial.print(" "); //z - //Serial.print(perlin16(i, offset, (offset >> 3))); Serial.print(" "); //x - //Serial.print(perlin16(offset, i, (offset >> 3))); Serial.print(" "); //y - //Serial.print(perlin16(offset, (offset >> 3), i)); Serial.print(" "); //z - - //Serial.print("Fastled:");Serial.print(inoise16(i, offset+i/2, i + (offset >> 3))); Serial.print(", "); //mixed mode - //Serial.print("New:");Serial.println(perlin16(i, offset+i/2, i + (offset >> 3)));// Serial.println(", "); - - //Serial.print("Fastled:");Serial.print(inoise16(i, offset+i/2)); Serial.print(", "); //mixed mode - //Serial.print("New:");Serial.println(perlin16(i, offset+i/2));// Serial.println(", "); - - //Serial.print("Fastled:");Serial.print(inoise16(i)); Serial.print(", "); //mixed mode - //Serial.print("New:");Serial.println(perlin16(i));// Serial.println(", "); - - Serial.print("Fastled3D:");Serial.print(inoise8(i, offset+i/2, i + (offset >> 3))); Serial.print(", "); //mixed mode - Serial.print("New3D:");Serial.print(perlin8(i, offset+i/2, i + (offset >> 3)));// Serial.println(", "); - Serial.print(", "); - Serial.print("Fastled2D:");Serial.print(inoise8(i, offset+i/2)); Serial.print(", "); //mixed mode - Serial.print("New2D:");Serial.print(perlin8(i, offset+i/2));// Serial.println(", "); - Serial.print(", "); - Serial.print("Fastled1D:");Serial.print(inoise8(i)); Serial.print(", "); //mixed mode - Serial.print("New1D:");Serial.println(perlin8(i));// Serial.println(", "); - - //Serial.print(inoise16(i, offset+i/2, i + (offset >> 3))); Serial.print(","); //mixed mode - //Serial.println(perlin16(i, offset+i/2, i + (offset >> 3)));// Serial.println(", "); - //delay(10); - // Serial.println(perlin3D_raw(i, offset+i/4, i*2 + (offset >> 3))); //raw - }*/ - -/* - for(int i = 0; i < 0x2FFFF; i+=100) { - uint32_t pos = i + offset; - Serial.print(inoise8_raw((pos)>>3, (pos)>>3)); Serial.print(","); - Serial.print(inoise8((pos)>>3, (pos)>>3, (pos)>>3)); Serial.print(","); - Serial.print(inoise16(pos*20, pos*30)); Serial.print(","); - //Serial.print(inoise16_raw(pos*20, pos*30, pos*40)); Serial.print(","); - Serial.print(inoise16(pos*20, pos*20, pos*20)); Serial.print(","); - //Serial.print(((perlin1D_raw(pos*20)* 85)>>7) + 0x7FFF); Serial.print(","); - Serial.print(perlin1D_raw(pos*20)); Serial.print(","); - Serial.print(perlin2D_raw(pos*20, pos*20)); Serial.print(","); - //Serial.print(perlin2D_raw(pos*20, pos*30) + 0x7FFF); Serial.print(","); - Serial.println(perlin3D_raw(pos*20, pos*20, pos*20)); - //Serial.println(((perlin3D_raw(pos*20, pos*30, pos*40) * 85)>>7) + 0x7FFF); - }*/ - -/* - for(int i = 0; i < 0xF0000; i+=55) { - Serial.print(inoise8_raw(i,i+5648) / 2); Serial.print(","); // +/-32 ? - Serial.print(((int16_t)perlin8(i,i+5648) - 0x7F) >> 2); Serial.print(","); - Serial.print(inoise8(i,i/3,i/5)); Serial.print(","); - Serial.print(perlin8(i,i/3,i/5)); Serial.print(","); - Serial.print(inoise8(i,i/3)); Serial.print(","); - Serial.print(perlin8(i,i/3)); Serial.print(","); - Serial.print(inoise8(i)); Serial.print(","); - Serial.println(perlin8(i)); - } -*/ - int32_t minval=0xFFFFF; - int32_t maxval=0; - start = micros(); - for(int i = 0; i < 0xFFFFF; i+=50) { - uint16_t pos = i + offset; - int32_t noiseval = inoise8_raw(pos); - if(noiseval < minval) minval = noiseval; - if(noiseval > maxval) maxval = noiseval; - } - end = micros(); - time = end - start; - Serial.print("time: "); Serial.print(time); - Serial.print(" inoise8_raw min: "); Serial.print(minval); Serial.print(" max: "); Serial.println(maxval); - - minval=0xFFFFF; - maxval=0; - - start = micros(); - for(int i = 0; i < 0xFFFFFF; i+=50) { - uint32_t pos = i + offset; - //int32_t noiseval = inoise16(pos, pos+4684165, pos+985685); - int32_t noiseval = inoise16(hw_random(), hw_random(), hw_random()); - if(noiseval < minval) minval = noiseval; - if(noiseval > maxval) maxval = noiseval; - } - end = micros(); - time = end - start; - Serial.print("time: "); Serial.print(time); - Serial.print(" inoise16_3D min: "); Serial.print(minval); Serial.print(" max: "); Serial.println(maxval); - - minval=0xFFFFF; - maxval=0; - start = micros(); - for(int i = 0; i < 0xFFFFFF; i+=50) { - uint32_t pos = i + offset; - //int32_t noiseval = perlin16(hw_random()); - int32_t noiseval = perlin1D_raw(hw_random(),false); - if(noiseval < minval) minval = noiseval; - if(noiseval > maxval) maxval = noiseval; - } - end = micros(); - time = end - start; - Serial.print("time: "); Serial.print(time); -Serial.print(" perlin1D raw min: "); Serial.print(minval); Serial.print(" max: "); Serial.println(maxval); - minval=0xFFFFF; - maxval=0; - start = micros(); - for(int i = 0; i < 0xFFFFFF; i+=50) { - uint32_t pos = i + offset; - //int32_t noiseval = perlin16( hw_random(), hw_random()); - int32_t noiseval = perlin2D_raw( hw_random(), hw_random()); - if(noiseval < minval) minval = noiseval; - if(noiseval > maxval) maxval = noiseval; - } - end = micros(); - time = end - start; - Serial.print("time: "); Serial.print(time); - Serial.print(" perlin2D raw min: "); Serial.print(minval); Serial.print(" max: "); Serial.println(maxval); - - - minval=0xFFFFF; - maxval=0; - for(int i = 0; i < 0xFFFFFF; i+=50) { - uint32_t pos = i + offset; - //int32_t noiseval = perlin3D_raw(pos, pos+46845, pos+654684); - //int32_t noiseval = perlin3D_raw(hw_random(), hw_random(), hw_random()); - int32_t noiseval = perlin16(hw_random(), hw_random(), hw_random()); - if(noiseval < minval) minval = noiseval; - if(noiseval > maxval) maxval = noiseval; - } - end = micros(); - time = end - start; - Serial.print("time: "); Serial.print(time); - Serial.print(" perlin16 min: "); Serial.print(minval); Serial.print(" max: "); Serial.println(maxval); - - minval=0xFFFFF; - maxval=0; - for(int i = 0; i < 0xFFFFFF; i+=50) { - uint32_t pos = i + offset; - //int32_t noiseval = perlin3D_raw(pos, pos+46845, pos+654684); - int32_t noiseval = perlin3D_raw(hw_random(), hw_random(), hw_random(),false); - //int32_t noiseval = perlin16(hw_random(), hw_random(), hw_random()); - if(noiseval < minval) minval = noiseval; - if(noiseval > maxval) maxval = noiseval; - } - end = micros(); - time = end - start; - Serial.print("time: "); Serial.print(time); - Serial.print(" perlin3D_raw min: "); Serial.print(minval); Serial.print(" max: "); Serial.println(maxval); - - - - volatile uint32_t temp; - start = micros(); - for(int i = 0; i < 100000; i++){ - temp += inoise8(i); - } - end = micros(); - time = end - start; - Serial.print("inoise8: "); - Serial.print(temp); - Serial.print("time: "); - Serial.println(time); - start = micros(); - for(int i = 0; i < 100000; i++){ - temp += inoise16(i,i<<1,i<<2); - } - end = micros(); - time = end - start; - Serial.print("inoise16:"); - Serial.print(temp); - Serial.print("time: "); - Serial.println(time); - start = micros(); - for(int i = 0; i < 100000; i++){ - temp += perlin1D_raw(i); - } - end = micros(); - time = end - start; - Serial.print("perlin1Draw:"); - Serial.print(temp); - Serial.print("time: "); - Serial.println(time); - start = micros(); - for(int i = 0; i < 100000; i++){ - temp += perlin2D_raw(i,i*33); - } - end = micros(); - time = end - start; - Serial.print("perlin2Draw:"); - Serial.print(temp); - Serial.print("time: "); - Serial.println(time); - start = micros(); - for(int i = 0; i < 100000; i++){ - temp += perlin16(i,i*33,i*17); - } - end = micros(); - time = end - start; - Serial.print("perlin163D:"); - Serial.print(temp); - Serial.print("time: "); - Serial.println(time); - - start = micros(); - for(int i = 0; i < 100000; i++){ - temp += perlin3D_raw(i,i*33,i*17); - } - end = micros(); - time = end - start; - Serial.print("perlin3D raw:"); - Serial.print(temp); - Serial.print("time: "); - Serial.println(time); - - DEBUG_PRINTF_P(PSTR("CPU: %s rev.%d, %d core(s), %d MHz.\n"), ESP.getChipModel(), (int)ESP.getChipRevision(), ESP.getChipCores(), ESP.getCpuFreqMHz()); DEBUG_PRINTF_P(PSTR("FLASH: %d MB, Mode %d "), (ESP.getFlashChipSize()/1024)/1024, (int)ESP.getFlashChipMode()); #ifdef WLED_DEBUG From 229e7b940fb5df89391a20de598e48bb3a238ec7 Mon Sep 17 00:00:00 2001 From: Damian Schneider Date: Wed, 12 Mar 2025 19:58:32 +0100 Subject: [PATCH 6/7] added ranges, removed unused code --- wled00/util.cpp | 40 ++++++++++++++-------------------------- 1 file changed, 14 insertions(+), 26 deletions(-) diff --git a/wled00/util.cpp b/wled00/util.cpp index 07c0db121..ac8a16207 100644 --- a/wled00/util.cpp +++ b/wled00/util.cpp @@ -627,10 +627,10 @@ int32_t hw_random(int32_t lowerlimit, int32_t upperlimit) { // calculate gradient for corner from hash value static inline __attribute__((always_inline)) int32_t hashToGradient(uint32_t h) { - // using more steps yields more "detailed" perlin noise but looks less like the original fastled version (adjust PERLIN_SHIFT to compensate) - //return (h & 0xFF) - 128; // use PERLIN_SHIFT 7 - //return (h & 0x0F) - 8; // use PERLIN_SHIFT 3 - //return (h & 0x07) - 4; // use PERLIN_SHIFT 2 + // using more steps yields more "detailed" perlin noise but looks less like the original fastled version (adjust PERLIN_SHIFT to compensate, also changes range and needs proper adustment) + // return (h & 0xFF) - 128; // use PERLIN_SHIFT 7 + // return (h & 0x0F) - 8; // use PERLIN_SHIFT 3 + // return (h & 0x07) - 4; // use PERLIN_SHIFT 2 return (h & 0x03) - 2; // use PERLIN_SHIFT 1 -> closest to original fastled version } @@ -658,17 +658,6 @@ static inline __attribute__((always_inline)) int32_t gradient3D(uint32_t x0, int h ^= h >> 15; h *= 0x92C3412B; h ^= h >> 13; - -/* - // fastled version: 25% slower but gives original "look" - h = h&15; - int32_t u = h<8?dx:dy; - int32_t v = h<4?dy:h==12||h==14?dx:dz; - if(h&1) { u = -u; } - if(h&2) { v = -v; } - return (u >> 1) + (v >> 1) + (u & 0x1); -*/ - // closer to actual perlin version return ((hashToGradient(h) * dx + hashToGradient(h>>(1+PERLIN_SHIFT)) * dy + hashToGradient(h>>(1 + 2*PERLIN_SHIFT)) * dz) * 85) >> (8 + PERLIN_SHIFT); // scale to 16bit, x*85 >> 8 = x/3 } @@ -684,7 +673,7 @@ static inline int32_t lerpPerlin(int32_t a, int32_t b, int32_t t) { return a + (((b - a) * t) >> 14); // match scaling with smoothstep to yield 16.16bit values } -// 1D Perlin noise function that returns a value in range of approximately -32768 to +32768 +// 1D Perlin noise function that returns a value in range of -24691 to 24689 int32_t perlin1D_raw(uint32_t x, bool is16bit) { // integer and fractional part coordinates int32_t x0 = x >> 16; @@ -702,7 +691,7 @@ int32_t perlin1D_raw(uint32_t x, bool is16bit) { return noise; } -// 2D Perlin noise function that returns a value in range of approximately -32768 to +32768 +// 2D Perlin noise function that returns a value in range of -20633 to 20629 int32_t perlin2D_raw(uint32_t x, uint32_t y, bool is16bit) { int32_t x0 = x >> 16; int32_t y0 = y >> 16; @@ -734,6 +723,7 @@ int32_t perlin2D_raw(uint32_t x, uint32_t y, bool is16bit) { return noise; } +// 3D Perlin noise function that returns a value in range of -16788 to 16381 int32_t perlin3D_raw(uint32_t x, uint32_t y, uint32_t z, bool is16bit) { int32_t x0 = x >> 16; int32_t y0 = y >> 16; @@ -778,30 +768,28 @@ int32_t perlin3D_raw(uint32_t x, uint32_t y, uint32_t z, bool is16bit) { int32_t noise = lerpPerlin(ny0, ny1, tz); return noise; } -// scaling functions for fastled replacement +// scaling functions for fastled replacement uint16_t perlin16(uint32_t x) { - //return ((perlin1D_raw(x) * 1168) >> 10) + 0x7FFF; //scale to 16bit and offset (full range) - //return ((perlin1D_raw(x) * 895) >> 10) + 34616; //scale to 16bit and offset (fastled range) -> 8 steps - return ((perlin1D_raw(x) * 1159) >> 10) + 32803; //scale to 16bit and offset (fastled range) -> 8 steps + return ((perlin1D_raw(x) * 1159) >> 10) + 32803; //scale to 16bit and offset (fastled range: about 4838 to 60766) } uint16_t perlin16(uint32_t x, uint32_t y) { - return ((perlin2D_raw(x, y) * 1537) >> 10) + 32725; //scale to 16bit and offset (empirical values with some overflow safety margin) + return ((perlin2D_raw(x, y) * 1537) >> 10) + 32725; //scale to 16bit and offset (fastled range: about 1748 to 63697) } uint16_t perlin16(uint32_t x, uint32_t y, uint32_t z) { - return ((perlin3D_raw(x, y, z) * 1731) >> 10) + 33147; //scale to 16bit and offset (empirical values with some overflow safety margin) + return ((perlin3D_raw(x, y, z) * 1731) >> 10) + 33147; //scale to 16bit and offset (fastled range: about 4766 to 60840) } uint8_t perlin8(uint16_t x) { - return (((perlin1D_raw((uint32_t)x << 8, true) * 1353) >> 10) + 32769) >> 8; + return (((perlin1D_raw((uint32_t)x << 8, true) * 1353) >> 10) + 32769) >> 8; //scale to 16 bit, offset, then scale to 8bit } uint8_t perlin8(uint16_t x, uint16_t y) { - return (((perlin2D_raw((uint32_t)x << 8, (uint32_t)y << 8, true) * 1620) >> 10) + 32771) >> 8; + return (((perlin2D_raw((uint32_t)x << 8, (uint32_t)y << 8, true) * 1620) >> 10) + 32771) >> 8; //scale to 16 bit, offset, then scale to 8bit } uint8_t perlin8(uint16_t x, uint16_t y, uint16_t z) { - return (((perlin3D_raw((uint32_t)x << 8, (uint32_t)y << 8, (uint32_t)z << 8, true) * 2015) >> 10) + 33168) >> 8; //scale to 8bit + return (((perlin3D_raw((uint32_t)x << 8, (uint32_t)y << 8, (uint32_t)z << 8, true) * 2015) >> 10) + 33168) >> 8; //scale to 16 bit, offset, then scale to 8bit } \ No newline at end of file From d2b7e474d6147f33d3ae7e8e0ca04f33936f08dd Mon Sep 17 00:00:00 2001 From: Damian Schneider Date: Fri, 14 Mar 2025 06:48:18 +0100 Subject: [PATCH 7/7] add legacy defines for compatibility, reverted test changes in rotozoomer --- wled00/FX.cpp | 23 ++++++++++++++--------- wled00/fcn_declare.h | 2 ++ 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index 1bba265dc..756750f27 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -6221,12 +6221,22 @@ uint16_t mode_2Dplasmarotozoom() { const int cols = SEG_W; const int rows = SEG_H; - unsigned dataSize = sizeof(float); + unsigned dataSize = SEGMENT.length() + sizeof(float); if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed float *a = reinterpret_cast(SEGENV.data); + byte *plasma = reinterpret_cast(SEGENV.data+sizeof(float)); unsigned ms = strip.now/15; + // plasma + for (int j = 0; j < rows; j++) { + int index = j*cols; + for (int i = 0; i < cols; i++) { + if (SEGMENT.check1) plasma[index+i] = (i * 4 ^ j * 4) + ms / 6; + else plasma[index+i] = inoise8(i * 40, j * 40, ms); + } + } + // rotozoom float f = (sin_t(*a/2)+((128-SEGMENT.intensity)/128.0f)+1.1f)/1.5f; // scale factor float kosinus = cos_t(*a) * f; @@ -6235,14 +6245,9 @@ uint16_t mode_2Dplasmarotozoom() { float u1 = i * kosinus; float v1 = i * sinus; for (int j = 0; j < rows; j++) { - unsigned u = abs8(u1 - j * sinus) % cols; - unsigned v = abs8(v1 + j * kosinus) % rows; - byte plasma; - if (SEGMENT.check1) plasma = (u * 4 ^ v * 4) + ms / 6; - else plasma = perlin8(u * 40, v * 40, ms); - //else plasma = inoise8(u * SEGMENT.intensity, v * SEGMENT.intensity, ms); - //SEGMENT.setPixelColorXY(i, j, SEGMENT.color_from_palette(plasma[v*cols+u], false, PALETTE_SOLID_WRAP, 255)); - SEGMENT.setPixelColorXY(i, j, SEGMENT.color_from_palette(plasma, false, PALETTE_SOLID_WRAP, 255)); + byte u = abs8(u1 - j * sinus) % cols; + byte v = abs8(v1 + j * kosinus) % rows; + SEGMENT.setPixelColorXY(i, j, SEGMENT.color_from_palette(plasma[v*cols+u], false, PALETTE_SOLID_WRAP, 255)); } } *a -= 0.03f + float(SEGENV.speed-128)*0.0002f; // rotation speed diff --git a/wled00/fcn_declare.h b/wled00/fcn_declare.h index 4f7603f50..bb7114ed5 100644 --- a/wled00/fcn_declare.h +++ b/wled00/fcn_declare.h @@ -486,6 +486,8 @@ void userLoop(); #include "soc/wdev_reg.h" #define HW_RND_REGISTER REG_READ(WDEV_RND_REG) #endif +#define inoise8 perlin8 // fastled legacy alias +#define inoise16 perlin16 // fastled legacy alias #define hex2int(a) (((a)>='0' && (a)<='9') ? (a)-'0' : ((a)>='A' && (a)<='F') ? (a)-'A'+10 : ((a)>='a' && (a)<='f') ? (a)-'a'+10 : 0) [[gnu::pure]] int getNumVal(const String* req, uint16_t pos); void parseNumber(const char* str, byte* val, byte minv=0, byte maxv=255);