Яке походження цього однокласника GLSL rand ()?


92

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

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

Це по-різному називають "канонічним", або "одноклассником, який я десь знайшов в Інтернеті".

Яке походження цієї функції? Чи постійні значення настільки довільні, як вони здаються, чи існує якесь мистецтво для їх вибору? Чи обговорюється суть цієї функції?

EDIT: Найдавніше посилання на цю функцію, з яким я зіткнувся, - це цей архів від 08 лютого 2008 р. , Оригінальна сторінка відсутня в Інтернеті. Але там обговорюється не більше, ніж де-небудь ще.


Це шумова функція, яка використовується для створення процедурно сформованого рельєфу. схожий на щось подібне en.wikipedia.org/wiki/Perlin_noise
foreyez

Відповіді:


42

Дуже цікаве питання!

Я намагаюся це зрозуміти, набираючи відповідь :) Спочатку простий спосіб пограти з нею: 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 () - це чорний ящик на графічному процесорі, чисельно. Має бути можливо створити набагато кращу, взявши майже будь-яку хеш-функцію та перетворивши її. Важкою частиною є перетворення типової цілочисельної операції, що використовується в хешуванні процесора, в операції з плаваючою (половинною або 32-бітною) або фіксованою точкою, але це повинно бути можливим.

Знову ж таки, справжня проблема з цим як хеш-функцією полягає в тому, що sin () - це чорний ящик.


1
Це не відповідає на питання про походження, але я не думаю, що це насправді відповідає. Я прийму цю відповідь через ілюстративний графік.
Grumdrig

19

Походженням є, мабуть, стаття: "Про генерування випадкових чисел за допомогою y = [(a + x) sin (bx)] mod 1", WJJ 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
}

3
Дуже цікаво! Я знайшов статтю, яка посилається на неї , а також на сам журнал у Google Books, але, схоже, сама доповідь чи стаття не були включені до журналу.
Grumdrig

1
Крім того, із заголовка видно, що функція, про яку я запитую, повинна повертатися, fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * (co.xy + vec2(43758.5453, SOMENUMBER))щоб відповідати функції, про яку йдеться в статті.
Grumdrig

І ще одне, якщо це справді походження використання функції, питання про походження магічних чисел (вибір aта b), що використовуються неодноразово, залишається, але, можливо, було використано в роботі, яку ви цитуєте.
Grumdrig

Я теж не можу знайти папір. (редагувати: той же документ, що і посилання вище)
М.Б. Рейнольдс,

Оновіть відповідь додатковою інформацією.
М.Б. Рейнольдс,

8

постійні значення довільні, особливо, якщо вони дуже великі, і на пару десяткових знаків від простих чисел.

модуль над 1 синуса високої амплітуди, помножений на 4000, є періодичною функцією. це як віконна штора або гофрований метал, зроблений дуже маленьким, оскільки його помножують на 4000 і повертають під кутом на точковий виріб.

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

Якщо ви знайдете період функції як у X, так і у Y, ви можете взяти його так, щоб він знову виглядав як проста синусоїда.

Ось його малюнок, збільшений у графіку

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


Але на графічних процесорах X і Y варіюються від 0..1, і це виглядає набагато більш випадковим, якщо ви зміните графік. Я знаю, що це звучить як твердження, але насправді це питання, бо моя математична освіта закінчилася у 18 років.
Струни

я знаю, я просто збільшив масштаб, щоб ви могли побачити, що випадкова функція має таку форму, за винятком того, що хребти дуже швидко змінюються, за винятком того, що вам потрібно масштабувати мало, щоб побачити зміни взагалі ... ви можете собі уявити, що брати очки на хребтах дасть досить випадкові числа від 0 до 1 висоти для значень 1 до 1 x та y.
аліментальний

О, я розумію, і це видається дуже логічним для будь-якого покоління випадкових чисел, яке в своїй основі використовує функцію sin
Рядки

2
це, по суті, лінійний зигзаг, і гріх повинен додати крихітну варіацію, це те саме, що якби хтось гортав пачку карток від однієї до 10 дуже швидко навколо вас, і ви повинні спробувати В кінці візьміть шаблон з чисел з карток, вони були б випадковими, оскільки це було б дуже швидко, коли він міг отримати шаблон, лише вибравши карти в точній синхронізації щодо того, наскільки швидко карти оберталися.
аліментальний

Просто до відома, це не було б швидше зробити (13x + 79y)так dot(XY, AB)буде робити саме те , що ви описали, як його скалярний твір, якеx,y dot 13, 79 = (13x + 79y)
WHN

1

Можливо, це якесь неповторне хаотичне відображення, тоді воно може пояснити багато речей, але також може бути лише деякою довільною маніпуляцією з великими числами.

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


але тоді також повинен працювати 4e5, я не розумію, чому магічне число 43758.5453. (також, я б компенсував x на деяке дробове число, щоб уникнути rand (0) = 0.
Fabrice NEYRET

1
Я думаю, що з 4e5 ви не отримаєте стільки змін дробових бітів, це завжди дасть вам однакове значення. Отже, мають бути дотримані дві умови, достатньо великі і мають достатньо хороші варіації дробових частин.
Роман

що ви маєте на увазі, "завжди дасть вам одне і те ж значення"? (якщо ви маєте на увазі, що це буде завжди однакові цифри, по-перше, вони все ще хаотичні, по-друге, float зберігаються як m * 2 ^ p, а не 10 ^ p, тому * 4e5 все ще скремблює біти).
Fabrice NEYRET

Я думав, ви написали експоненційне подання числа 4 * 10 ^ 5, тож sin (x) * 4e5 дасть вам не таке хаотичне число. Я згоден, що дробові біти від хвилі гріха також дадуть вам хорошу чатоїчність.
Роман

Але тоді це залежить від діапазону x, я маю на увазі, чи повинна функція бути надійною для малих (-0,001, 0,001) та великих значень (-1, 1). Ви можете спробувати побачити різницю з фрагментом (sin (x / 1000.0) * 43758.5453); і дріб (sin (x / 1000.0) * 4e5) ;, де x в діапазоні [-1., 1.]. У другому варіанті зображення буде більш монотонним (принаймні я бачу різницю в шейдері). Але, загалом, я згоден з тим, що ви все ще можете використовувати 4e5 і мати досить хороший результат.
Роман
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.