Какое происхождение этого GLSL rand() однострочный?

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

float rand(vec2 co){
  return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453);
}

Он по-разному называется "каноническим", или "одним слоем, который я нашел где-то в Интернете".

Какое происхождение этой функции? Являются ли постоянные значения такими же произвольными, какими они кажутся или есть какое-то искусство для их выбора? Существует ли обсуждение достоинств этой функции?

EDIT: самая старая ссылка на эту функцию, с которой я столкнулся, - этот архив с февраля 2008 года, исходная страница теперь исчезла из Интернета. Но там больше не обсуждается, чем где-либо еще.

Ответ 1

Очень интересный вопрос!

Я пытаюсь понять это, набрав ответ:) Сначала простой способ поиграть с ним: http://www.wolframalpha.com/input/?i=plot%28+mod%28+sin%28x*12.9898+%2B + у * 78,233% 29 + * + 43758,5453% 2C1% 29x% 3D0..2% 2C + у% 3D0..2% 29

Тогда подумайте о том, что мы пытаемся сделать здесь: для двух входных координат x, y мы возвращаем "случайное число". Однако это не случайное число. Это то же самое каждый раз, когда мы вводим одни и те же x, y. Это хеш-функция!

Первое, что делает функция, - перейти от 2d к 1d. Это не интересно само по себе, но числа выбраны так, чтобы они не повторялись обычно. Также есть добавление с плавающей запятой. Будут еще несколько бит от y или x, но цифры могут быть выбраны правильно, так что это соединение.

Затем мы пробуем функцию black box sin(). Это будет зависеть от реализации!

Наконец, он усиливает ошибку в реализации sin(), умножая и принимая долю.

Я не думаю, что это хорошая хэш-функция в общем случае. Sin() - это черный ящик, на графическом процессоре, численно. Должна быть возможность построить намного лучшую, взяв почти любую хэш-функцию и преобразовать ее. Жесткая часть состоит в том, чтобы превратить типичную операцию целого числа, используемую в хешировании процессора, в операции float (половина или 32 бит) или операции с фиксированной точкой, но это должно быть возможно.

Опять же, реальная проблема с этим как хэш-функцией состоит в том, что sin() является черным ящиком.

Ответ 2

Источником, вероятно, является бумага: "При генерации случайных чисел с помощью y = [(a + x) sin (bx)] mod 1", W.J.J. Рей, 22-е европейское совещание статистиков и 7-я Вильнюсская конференция по теории вероятностей и математической статистике, август 1998 г.

EDIT: Поскольку я не могу найти копию этой статьи, и ссылка "TestU01" может быть неясной, здесь схема, описанная в TestU01 в псевдо-C:

#define A1 ???
#define A2 ???
#define B1 pi*(sqrt(5.0)-1)/2
#define B2 ???

uint32_t n;   // position in the stream

double next() {
  double t = fract(A1     * sin(B1*n));
  double u = fract((A2+t) * sin(B2*t));
  n++;
  return u;
} 

где единственным рекомендуемым постоянным значением является B1.

Обратите внимание, что это для потока. Преобразование в 1D хэш 'n' становится сеткой целых чисел. Поэтому я предполагаю, что кто-то увидел это и превратил 't' в простую функцию f (x, y). Используя исходные константы выше, которые дадут:

float hash(vec2 co){
  float t = 12.9898*co.x + 78.233*co.y; 
  return fract((A2+t) * sin(t));  // any B2 is folded into 't' computation
}

Ответ 3

постоянные значения являются произвольными, особенно, что они очень большие, и пара десятичных знаков от простых чисел.

модуль над 1 амплитудного синуса hi, умноженный на 4000, является периодической функцией. это как ослепитель или гофрированный металл, сделанный очень маленьким, потому что он умножился на 4000 и повернулся под углом точечным продуктом.

поскольку функция является 2-D, точечный продукт имеет функцию поворота периодической функции на наклонной относительно X и оси Y. Отношение к 13/79 приблизительно. Это неэффективно, вы действительно можете достичь того же путем синуса (13x + 79y), это также приведет к тому же, что я думаю с меньшей математикой.

Если вы найдете период функции как в X, так и в Y, вы можете опробовать его так, чтобы он снова выглядел как простая синусоидальная волна.

Вот изображение его увеличено в graph

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

Ответ 4

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

EDIT: в принципе, функция fract (sin (x) * 43758.5453) является простой хэш-подобной функцией, sin (x) обеспечивает гладкую интерполяцию sin между -1 и 1, поэтому sin (x) * 43758.5453 будет интерполяция от -43758,5453 до 43758,5453. Это довольно большой диапазон, поэтому даже небольшой шаг в x обеспечит большой шаг в результате и действительно большие изменения в дробной части. "Фракт" необходим для получения значений в диапазоне от -0,99... до 0,999.... Теперь, когда у нас есть что-то вроде хеш-функции, мы должны создать функцию для хеша производства из вектора. Самый простой способ - разобрать "хеш" отдельно для x любой y-составляющей входного вектора. Но тогда мы будем иметь некоторые симметричные значения. Итак, мы должны получить некоторое значение от вектора, подход - найти некоторый случайный вектор и найти "точечный" продукт этому вектору, здесь мы идем: fract (sin (dot (co.xy, vec2 (12.9898,78.233))) * 43758,5453); Кроме того, согласно выбранному вектору, его длина должна быть длинной, чтобы иметь несколько переходов функции "sin" после вычисления "точечного" продукта.