Почему эта шумовая функция не обрабатывает отрицательные аргументы?

У меня есть код, адаптированный из «Улучшенный» шум Перлина

double improved_noise (double x, double y, double z)
{
// Calculate the "unit cube" that the point asked will be located in
// The left bound is ( |_x_|,|_y_|,|_z_| ) and the right bound is that
// plus 1.  Next we calculate the location (from 0.0 to 1.0) in that
// cube. We also fade the location to smooth the result.

int xi = (int)x & 255;
int yi = (int)y & 255;
int zi = (int)z & 255;

double xf = x - (int) x;
double yf = y - (int) y;
double zf = z - (int) z;

double u = fade (xf);
double v = fade (yf);
double w = fade (zf);

int aaa, aba, aab, abb, baa, bba, bab, bbb;
auto & p = permutation;

aaa = p[p[p[    xi ] +     yi ] +     zi ];
aba = p[p[p[    xi ] + inc(yi)] +     zi ];
aab = p[p[p[    xi ] +     yi ] + inc(zi)];
abb = p[p[p[    xi ] + inc(yi)] + inc(zi)];
baa = p[p[p[inc(xi)] +     yi ] +     zi ];
bba = p[p[p[inc(xi)] + inc(yi)] +     zi ];
bab = p[p[p[inc(xi)] +     yi ] + inc(zi)];
bbb = p[p[p[inc(xi)] + inc(yi)] + inc(zi)];

double x1, x2, y1, y2;

// The gradient function calculates the dot product between a
// pseudorandom gradient vector and the vector from the input
// coordinate to the 8 surrounding points in its unit cube.

// This is all then lerped together as a sort of weighted average
// based on the faded (u,v,w) values we made earlier.

x1 = lerp (
grad (aaa, xf  , yf  , zf),
grad (baa, xf-1, yf  , zf),
u);

x2 = lerp (
grad (aba, xf  , yf-1, zf),
grad (bba, xf-1, yf-1, zf),
u);

y1 = lerp (x1, x2, v);

x1 = lerp (
grad (aab, xf  , yf  , zf-1),
grad (bab, xf-1, yf  , zf-1),
u);

x2 = lerp (
grad (abb, xf  , yf-1, zf-1),
grad (bbb, xf-1, yf-1, zf-1),
u);

y2 = lerp (x1, x2, v);

return (lerp (y1, y2, w) + 1) / 2;
}

Я хотел периодический шум в одном направлении, поэтому я обернул это направление в круг в дополнительном измерении, назвав его так

 improved_noise (sin(x*2*M_PI), cos(x*2*M_PI), y))

Я получил странные результаты (большие и / или отрицательные). Некоторые эксперименты показали, что это происходит, когда аргументы improved_noise отрицательны.

Почему эта функция плохо обрабатывает отрицательные значения, и ее легко адаптировать, чтобы строка с полным числом была допустимым аргументом?

0

Решение

improved_noise не был разработан для обработки отрицательных входных данных.

Комментарий в нем говорит:

Граница слева (| _x_ |, | _y_ |, | _z_ |)…

|…| нотация предполагает абсолютное значение предназначено. Тем не менее, код вычисляет:

int xi = (int)x & 255;

В распространенных реализациях C (где используется дополнение до двух) это эффективно вычисляет остаток от целочисленной части x по модулю 256. Например, если x равен -3.25, его целочисленная часть равна -3, и это установит xi до 253 (что составляет -3 + 256).

Есть две вещи не так с этим. Во-первых, 253 не является абсолютным значением -3, поэтому этот код не соответствует комментарию. Во-вторых, он берет «правую» границу единичного куба, содержащего точку (границу с большим значением), тогда как комментарии и поведение для положительных значений предполагают, что цель состоит в том, чтобы установить xi, yi, а также zi на «левую» границу (ту, которая имеет меньшее значение).

Продолжая оттуда, наборы кодов double xf = x - (int) x;, Для неотрицательных значений это дает дробную часть x, Например, если x были 3,25, xf будет 0,25. Однако с отрицательными значениями и предшествующим & 255 операция, это сбивается с пути. За x = -3,25, он вычисляет -3,25 — 253 = -256,25. Но код, скорее всего, предназначен просто для интерполяции в единичном кубе для дробных частей от 0 до 1. Какая бы функция ни использовалась для выполнения интерполяции, скорее всего, не поддерживается -256.25.

По сути, этот код никогда не был спроектирован для поддержки отрицательных значений, и для его исправления необходимо изменить его, исходя из первых принципов его работы.

оригинальный код, на который вы указываете лучше:

int X = (int)Math.floor(x) & 255
…
x -= Math.floor(x);

Бывший правильно использует floor найти «левую» границу, независимо от того, x отрицательно или нет. Тогда это относится & 255 к этому. Предполагая, что два дополняют друг друга, это обеспечит правильную координату в периодической мозаике. (Предполагая, что два дополнения не являются чисто переносимыми, их следует документировать или избегать.)

Затем он правильно находит дробь, вычитая floor из x а не вычитать результат & 255, Например, для x = −3.25, это даст целочисленную координату −4 и дробь 0,75.

Изменение improved_noise работать аналогично может помочь. Вы можете попробовать:

int xi = (int) floor(x) & 255;
int yi = (int) floor(y) & 255;
int zi = (int) floor(z) & 255;

double xf = x - floor(x);
double yf = y - floor(y);
double zf = z - floor(z);

Вы пометили этот вопрос как C ++, так и C. В C ++ предпочтительнее использовать std::floor вместо floor, и могут быть другие проблемы с различиями между C ++ и C.

3

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]