initial version, basically working but repetitive patterns, work in progress

This commit is contained in:
Damian Schneider 2025-03-03 06:57:16 +01:00
parent fa2949af7d
commit 2012317bc9
5 changed files with 421 additions and 40 deletions

View File

@ -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

View File

@ -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; i<tempsamp; i++) {
uint8_t index = inoise8(i*segmentSampleAvg+strip.now, 5000+i*segmentSampleAvg);
uint8_t index = perlin8(i*segmentSampleAvg+strip.now, 5000+i*segmentSampleAvg);
SEGMENT.setPixelColor(i, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(index, false, PALETTE_SOLID_WRAP, 0), uint8_t(segmentSampleAvg*8)));
}
if (gravcen->topLED > 0) {
@ -6499,7 +6500,7 @@ uint16_t mode_gravcenter_base(unsigned mode) {
}
else { //Gravcenter
for (int i=0; i<tempsamp; i++) {
uint8_t index = inoise8(i*segmentSampleAvg+strip.now, 5000+i*segmentSampleAvg);
uint8_t index = perlin8(i*segmentSampleAvg+strip.now, 5000+i*segmentSampleAvg);
SEGMENT.setPixelColor(i+SEGLEN/2, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(index, false, PALETTE_SOLID_WRAP, 0), uint8_t(segmentSampleAvg*8)));
SEGMENT.setPixelColor(SEGLEN/2-i-1, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(index, false, PALETTE_SOLID_WRAP, 0), uint8_t(segmentSampleAvg*8)));
}
@ -6621,7 +6622,7 @@ uint16_t mode_midnoise(void) { // Midnoise. By Andrew Tuline.
if (maxLen >SEGLEN/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<maxLen; i++) { // The louder the sound, the wider the soundbar. By Andrew Tuline.
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));
}
@ -7096,7 +7097,7 @@ uint16_t mode_noisemove(void) { // Noisemove. By: Andrew Tuli
uint8_t numBins = map(SEGMENT.intensity,0,255,0,16); // Map slider to fftResult bins.
for (int i=0; i<numBins; i++) { // How many active bins are we using.
unsigned locn = inoise16(strip.now*SEGMENT.speed+i*50000, strip.now*SEGMENT.speed); // Get a new pixel location from moving noise.
unsigned locn = perlin16(strip.now*SEGMENT.speed+i*50000, strip.now*SEGMENT.speed); // Get a new pixel location from moving noise.
// if SEGLEN equals 1 locn will be always 0, hence we set the first pixel only
locn = map(locn, 7500, 58000, 0, SEGLEN-1); // Map that to the length of the strand, and ensure we don't go over.
SEGMENT.setPixelColor(locn, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(i*64, false, PALETTE_SOLID_WRAP, 0), uint8_t(fftResult[i % 16]*4)));
@ -7546,7 +7547,7 @@ uint16_t mode_2Dsoap() {
int32_t ioffset = scale32_x * (i - cols / 2);
for (int j = 0; j < rows; j++) {
int32_t joffset = scale32_y * (j - rows / 2);
uint8_t data = inoise16(noisecoord[0] + ioffset, noisecoord[1] + joffset, noisecoord[2]) >> 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) {

View File

@ -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)

View File

@ -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;
}

View File

@ -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