В последнее время я дурачусь с шумовыми функциями, а именно с перлиновым шумом и симплексным шумом. У меня нет никаких вопросов по поводу алгоритмов шума, так как я их запустил и запустил, но у меня есть некоторые вопросы по поводу метода хеширования, который использует ken perlin:
Таким образом, в этих шумовых функциях вам нужно (псевдо) случайное значение для каждой координаты. В реализации Кена Перлинса он использует таблицу поиска, чтобы получить эти значения:
static const U8 perlin_hash_values[] = {
151,160,137, 91, 90, 15,131, 13,201, 95, 96, 53,194,233, 7,225,
140, 36,103, 30, 69,142, 8, 99, 37,240, 21, 10, 23,190, 6,148,
247,120,234, 75, 0, 26,197, 62, 94,252,219,203,117, 35, 11, 32,
57,177, 33, 88,237,149, 56, 87,174, 20,125,136,171,168, 68,175,
74,165, 71,134,139, 48, 27,166, 77,146,158,231, 83,111,229,122,
60,211,133,230,220,105, 92, 41, 55, 46,245, 40,244,102,143, 54,
65, 25, 63,161, 1,216, 80, 73,209, 76,132,187,208, 89, 18,169,
200,196,135,130,116,188,159, 86,164,100,109,198,173,186, 3, 64,
52,217,226,250,124,123, 5,202, 38,147,118,126,255, 82, 85,212,
207,206, 59,227, 47, 16, 58, 17,182,189, 28, 42,223,183,170,213,
119,248,152, 2, 44,154,163, 70,221,153,101,155,167, 43,172, 9,
129, 22, 39,253, 19, 98,108,110, 79,113,224,232,178,185,112,104,
218,246, 97,228,251, 34,242,193,238,210,144, 12,191,179,162,241,
81, 51,145,235,249, 14,239,107, 49,192,214, 31,181,199,106,157,
184, 84,204,176,115,121, 50, 45,127, 4,150,254,138,236,205, 93,
222,114, 67, 29, 24, 72,243,141,128,195, 78, 66,215, 61,156,180,
151,160,137, 91, 90, 15,131, 13,201, 95, 96, 53,194,233, 7,225,
140, 36,103, 30, 69,142, 8, 99, 37,240, 21, 10, 23,190, 6,148,
247,120,234, 75, 0, 26,197, 62, 94,252,219,203,117, 35, 11, 32,
57,177, 33, 88,237,149, 56, 87,174, 20,125,136,171,168, 68,175,
74,165, 71,134,139, 48, 27,166, 77,146,158,231, 83,111,229,122,
60,211,133,230,220,105, 92, 41, 55, 46,245, 40,244,102,143, 54,
65, 25, 63,161, 1,216, 80, 73,209, 76,132,187,208, 89, 18,169,
200,196,135,130,116,188,159, 86,164,100,109,198,173,186, 3, 64,
52,217,226,250,124,123, 5,202, 38,147,118,126,255, 82, 85,212,
207,206, 59,227, 47, 16, 58, 17,182,189, 28, 42,223,183,170,213,
119,248,152, 2, 44,154,163, 70,221,153,101,155,167, 43,172, 9,
129, 22, 39,253, 19, 98,108,110, 79,113,224,232,178,185,112,104,
218,246, 97,228,251, 34,242,193,238,210,144, 12,191,179,162,241,
81, 51,145,235,249, 14,239,107, 49,192,214, 31,181,199,106,157,
184, 84,204,176,115,121, 50, 45,127, 4,150,254,138,236,205, 93,
222,114, 67, 29, 24, 72,243,141,128,195, 78, 66,215, 61,156,180};
Координаты сопоставляются со значениями следующим образом:
return perlin_hash_values[perlin_hash_values[y & 255] + x & 255];
У меня есть некоторые проблемы с этим: он съедает память, и повторяется очень быстро, и не является затравочным (по крайней мере, эталонной реализацией. Я думаю, вы могли бы просто использовать шум 3D Перлина со значением z, использованным в качестве начального числа), и я хотел знаю, можно ли идти быстрее. (вероятно, нет, так как массив, вероятно, находится в кеше, и его трудно превзойти 2 чтения памяти и 3 инструкции). Я делаю это в основном для развлечения и для изучения чего-то.
Что было бы хорошим подходом для замены этого метода алгоритмом? Желательные свойства, вероятно, заключаются в том, что он быстрый, не вызывает визуальных артефактов и не повторяется так же быстро, как этот массив.
Сначала я подумал об использовании стандартной хеш-функции, но обнаружил 2 свойства, которые на самом деле не нужны из них: хеш-функции обычно принимают произвольные длинные входные данные, что здесь не требуется. Они также пытаются избежать коллизий (один и тот же выход для разных входов), что также не требуется здесь. По крайней мере, если я прав, полагая, что между визуальными артефактами и столкновениями нет сильной корреляции. Эти два фактора делают стандартные хеш-функции кажущимися излишне сложными. Все, что нужно, это отображение из N измерений в 1 измерение, которое выглядит случайным.
Принятие генератора псевдослучайных чисел xorshift *: я установил внутреннее 64-битное состояние равным значениям сцепленных х и у.
F32 xor_shift_star_adaption(S32 x, S32 y, U32 seed) {
union concat_S32
{
S32 s[2];
U64 u;
};
concat_S32 temp;
temp.s[0] = x;
temp.s[1] = y;
xorshift_star_64 xor;
xor.state = temp.u;
return xor.get_random_number();
}
Это производит много артефактов У меня недостаточно репутации для публикации изображений: /. Я подозреваю, что генераторы псевдослучайных чисел не очень хорошо подходят для этой задачи, так как я слышал, что им обычно требуется некоторое время, чтобы «прогреться». Внутреннее состояние улучшается, и чем дольше оно успевает это делать, тем выше качество случайных чисел. При таком подходе я предотвращаю прогрев prng. С другой стороны: состояние можно рассматривать как линейную последовательность чисел. Если я инициализирую prng с номером, который является длинным путем в этой линейной последовательности, я не должен немедленно получить «высококачественные» числа?
Используя этот супер простой один лайнер, кто-то предложил (теперь уже не один лайнер):
F32 internet_oneliner(S32 x, S32 y, U32 seed){
F32 xf = x;
F32 yf = y;
F32 dot = xf * 12.9898 + yf * 78.233;
F32 sin = sinf(dot) * 43758.5453;
F32 fract = sin - floorf(sin);
return fract;
}
Это выполняет довольно хорошо, но имеет некоторые дорогостоящие операции в нем.
Используя хэш-функцию Murmur 3, которая кажется (?) Лучшей «нормальной» хэш-функцией, которую я мог найти. (Я не буду размещать код здесь, его большой). Он также работает очень хорошо, но на выполнение работы уходит очень много времени.
Попытка сделать некоторые xors и битшифтинг с простыми числами, потому что я думаю, что так работают эти prngs: D. Как вы можете себе представить, это выглядит ужасно (ближе к абстрактному искусству, чем к случайному шуму). Создание prng кажется довольно невозможным без каких-либо знаний о них.
Итак, есть ли у вас какие-либо предложения о том, что попробовать дальше? Я думаю, что в целом я больше ищу хэш-функцию, чем prng, потому что она больше соответствует целям, которые я пытаюсь достичь здесь.
(Прошу прощения за отсутствие ссылок и изображений, система репутации действительно мешает мне здесь)
Два возможных решения:
а) Используйте 2D случайную таблицу. Если он помещается в кэш L1, он будет быстрым.
б) Используйте целочисленный хеш: http://burtleburtle.net/bob/hash/integer.html
Например, это:
uint32_t hash( uint32_t a)
a = (a ^ 61) ^ (a >> 16);
a = a + (a << 3);
a = a ^ (a >> 4);
a = a * 0x27d4eb2d;
a = a ^ (a >> 15);
return a;
}
Использовать (хеш ((у<<8) + х)&0xff).
И даже, поскольку это полный 32-битный хеш, и вам нужен только 8-битный вывод, вы можете найти более быстрый вариант. Может быть, просто удалив некоторые операции из этой хэш-функции 🙂 (и вы можете попробовать не только младшие 8 бит, но и старшие 8 бит, возможно, у него есть лучшие свойства)
Других решений пока нет …