Каково происхождение этого однострочного GLSL rand ()?
Я видел этот генератор псевдослучайных чисел для использования в шейдерах называют здесь и там по всему интернету:
float rand(vec2 co){
return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453);
}
это по-разному называется "каноническим"или" однострочным, который я нашел в интернете где-то".
каково происхождение этой функции? Являются ли постоянные значения столь же произвольными, как они кажутся, или есть какое-то искусство в их выборе? Обсуждаются ли достоинства этой функции?
EDIT: самая старая ссылка на это функция, с которой я столкнулся, это этот архив от февраля 08, исходная страница теперь исчезла из интернета. Но там это обсуждается не больше, чем где-либо еще.
4 ответов
очень интересный вопрос!
Я пытаюсь понять это, набирая ответ :) Первый простой способ играть с ним: http://www.wolframalpha.com/input/?i=plot%28+mod%28+sin%28x*12.9898+%2B+y*78.233%29+*+43758.5453%2C1%29x%3D0..2%2C+y%3D0..2%29
тогда давайте подумаем о том,что мы пытаемся сделать здесь: для двух входных координат x, y мы возвращаем "случайное число". Однако это не случайное число. Каждый раз, когда мы вводим тот же x,y. Это хэш-функция!
первое, что делает функция, это перейти от 2d к 1d. Это не интересно само по себе, но числа выбираются так, чтобы они не повторялись обычно. Также у нас есть дополнение с плавающей точкой. Будет еще несколько бит от y или x, но числа могут быть выбраны правильно, поэтому он делает микс.
затем мы пробуем функцию Sin () черного ящика. Это будет во многом зависеть от реализации!
наконец он усиливает ошибка в реализации sin () путем умножения и взятия дроби.
Я не думаю, что это хорошая хэш-функция в общем случае. Sin () - это черный ящик на GPU, численно. Должно быть возможно построить гораздо лучший, взяв почти любую хэш-функцию и преобразовав ее. Трудная часть состоит в том, чтобы превратить типичную целочисленную операцию, используемую в хешировании ЦП, в операции float (half или 32bit) или fixed point, но это должно быть возможно.
снова, реальная проблема с этим как хэш-функцией заключается в том, что sin() является черным ящиком.
происхождение, вероятно, бумага: "о генерации случайных чисел с помощью y= [(a+x) sin (bx)] mod 1", W. J. J. Rey, 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
}
постоянные значения произвольны, особенно то, что они очень большие, и на пару десятичных знаков от простых чисел.
модуль более 1 синуса Hi амплитуды, умноженный на 4000, является периодической функцией. это как оконная штора или гофрированный металл, сделанный очень маленьким, потому что он умножен на 4000 и повернут под углом точечным продуктом.
по мере того как функция 2-D, продукт точки имеет влияние поворачивать периодическую функцию на косом относительно оси X и Y. По соотношению 13/79 примерно. Это неэффективно, вы можете достичь того же самого, делая синус (13x + 79y), это также достигнет того же, что я думаю, с меньшим количеством математики..
Если вы найдете период функции как в X, так и в Y, вы можете попробовать его так, чтобы он снова выглядел как простая синусоидальная волна.
вот его изображение увеличено графика
Я не знаю происхождения, но он похож на многие другие, если вы использовали его в графике через регулярные промежутки времени, он будет иметь тенденцию производить муаровые узоры, и вы могли видеть, что он в конечном итоге снова идет.
возможно, это какое-то непериодическое хаотическое отображение, тогда оно может объяснить многие вещи, но также может быть просто произвольной манипуляцией с большими числами.
EDIT: в основном, функция fract(sin(x) * 43758.5453) является простой хэш-подобной функцией, sin(x) обеспечивает плавную интерполяцию греха между -1 до 1, поэтому sin (x) * 43758.5453 будет интерполяцией от -43758.5453 до 43758.5453. Это довольно огромный диапазон, поэтому даже небольшой шаг в x обеспечит большой шаг в результате и очень большая вариация дробной части. "Fract" необходим для получения значений в диапазоне -0.99... до 0.999... . Теперь, когда у нас есть что-то вроде хэш-функции, мы должны создать функцию для производства хэша из вектора. Самый простой способ-вызвать" хэш " отдельно для x любой y-компоненты входного вектора. Но тогда у нас будут симметричные значения. Итак, мы должны получить некоторое значение из вектора, подход-найти некоторый случайный вектор и найти" точечный " продукт к этому вектору, здесь мы идем: fract (sin (dot (co.xy, vec2 (12.9898,78.233)) * 43758.5453); Кроме того, согласно выбранному вектору, его длина должна быть достаточно длинной, чтобы иметь несколько пероидов функции "sin" после того, как будет вычислено "точечное" произведение.