이 GLSL rand () 원 라이너의 기원은 무엇입니까?


92

에서 여기 저기 언급되는 셰이더에서 사용하기위한이 의사 난수 생성기를 보았습니다 .

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

"표준"또는 "웹 어딘가에서 찾은 한 줄"이라고 다양하게 불립니다.

이 기능의 기원은 무엇입니까? 상수 값은 보이는 것처럼 임의적입니까 아니면 선택에 예술이 있습니까? 이 기능의 장점에 대한 논의가 있습니까?

편집 : 내가 본이 기능에 대한 가장 오래된 참조는 20082 월의이 아카이브 이며, 원래 페이지는 이제 웹에서 사라졌습니다. 그러나 다른 곳보다 더 이상 토론이 없습니다.


절차 적으로 생성 된 지형을 만드는 데 사용되는 노이즈 함수입니다. 이 같은 유사 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 ()은 GPU에서 수치 적으로 블랙 박스입니다. 거의 모든 해시 함수를 가져 와서 변환함으로써 훨씬 더 나은 것을 구성 할 수 있어야합니다. 어려운 부분은 CPU 해싱에 사용되는 일반적인 정수 연산을 부동 소수점 연산 (반 또는 32 비트) 또는 고정 소수점 연산으로 바꾸는 것이지만 가능해야합니다.

다시 말하지만, 해시 함수로서의 진짜 문제는 sin ()이 블랙 박스라는 것입니다.


1
이것은 기원에 대한 질문에 대한 답은 아니지만 정말 대답 할 수 없다고 생각합니다. 설명 그래프 때문에이 대답을 받아 들일 것입니다.
Grumdrig 2014 년

19

출처는 아마도 논문 일 것입니다 : "On generation of random numbers, with the help of y = [(a + x) sin (bx)] mod 1", WJJ Rey, 22nd European Meeting of Statisticians and the 7th Vilnius Conference on Probability Theory and 수학적 통계, 1998 년 8 월

편집 :이 문서의 사본을 찾을 수없고 "TestU01"참조가 명확하지 않을 수 있으므로 다음은 의사 C의 TestU01에 설명 된 체계입니다.

#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 도서에서 저널 자체 뿐만 아니라 이를 참조하는 논문을 찾았 지만 해당 논문이나 논문 자체가 저널에 포함되지 않은 것 같습니다.
Grumdrig 2015

1
또한 제목에서 내가 요청한 fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * (co.xy + vec2(43758.5453, SOMENUMBER))기능이 논문이 다루는 기능에 맞게 반환되어야한다는 것이 나타납니다 .
Grumdrig 2015

그리고 한 가지 더, 이것이 실제로 함수 사용의 기원이라면, 계속해서 사용되는 마법 번호 ( ab) 의 기원에 대한 질문이 남아 있지만 인용 한 논문에서 사용되었을 수 있습니다.
Grumdrig 2015

더 이상 신문을 찾을 수 없습니다. (편집 : 위에 링크 된 것과 동일한 문서)
MB Reynolds

더 많은 정보로 답변을 업데이트하십시오.
MB Reynolds

8

상수 값은 임의적입니다. 특히 값이 매우 크고 소수에서 소수가 몇 개 떨어져 있습니다.

고 진폭 부비동의 1 이상의 계수에 4000을 곱한 값은주기 함수입니다. 그것은 창 블라인드 또는 매우 작은 골판지 금속과 같습니다. 4000을 곱하고 내적에 의해 비스듬히 회전하기 때문입니다.

함수가 2 차원이므로 내적은주기 함수를 X 및 Y 축에 대해 비스듬한 방향으로 돌리는 효과가 있습니다. 대략 13/79 비율로. 비효율적입니다. (13x + 79y)의 sinus를 수행하여 실제로 동일한 결과를 얻을 수 있습니다. 이것은 또한 더 적은 수학으로 생각하는 동일한 결과를 얻을 수 있습니다.

X와 Y 모두에서 함수의주기를 찾으면 다시 단순한 사인파처럼 보이도록 샘플링 할 수 있습니다.

그래프로 확대 한 사진입니다

나는 그 기원을 모르지만 다른 많은 것들과 비슷하다. 만약 당신이 일정한 간격으로 그래픽에서 그것을 사용한다면 그것은 무아레 패턴을 생성하는 경향이 있고 결국 다시 돌아 다니는 것을 볼 수있을 것이다.


그러나 GPU X와 Y의 범위는 0..1이며 그래프를 변경하면 훨씬 더 무작위로 보입니다. 저는 이것이 진술처럼 들리지만 실제로 질문입니다. 제 수학 교육이 18 세에 끝났기 때문입니다.
문자열

알아요, 방금 확대해서 무작위 함수가 그 형태라는 것을 알 수 있습니다. 능선이 매우 빠르게 변하고 있다는 점을 제외하면 변경 사항을 전혀보기 위해 작게 확대해야한다는 점을 제외하면 ... 능선에서 1 ~ 1 x 및 y 값에 대해 0 ~ 1 높이의 꽤 난수를 제공합니다.
aliential

아, 이해합니다. 핵심에서 sin 함수를 사용하는 임의의 숫자 생성에 대해 매우 논리적으로 보입니다
Strings

2
그것은 본질적으로 선형 지그재그이고 죄는 약간의 변형을 추가해야합니다. 누군가가 당신 앞에서 매우 빠르게 1에서 10까지의 카드 팩을 튕기고있는 것과 같습니다. 카드에서 숫자의 패턴을 선택하면 카드가 회전하는 속도와 관련하여 정확한 동기화로 카드를 선택해야만 패턴을 얻을 수 있기 때문에 매우 빠르게 튕기기 때문에 임의의 숫자가됩니다.
aliential

참고로, 여러분이 설명하는 내적을 정확히 수행 (13x + 79y)하므로 더 빠르지 않을 것 dot(XY, AB)입니다.x,y dot 13, 79 = (13x + 79y)
whn

1

반복되지 않는 혼란스러운 매핑 일 수도 있습니다. 그러면 많은 것을 설명 할 수있을뿐 아니라 많은 수를 사용한 임의의 조작 일 수도 있습니다.

편집 : 기본적으로 함수 fract (sin (x) * 43758.5453)는 간단한 해시와 같은 함수이며 sin (x)는 -1에서 1 사이의 부드러운 sin 보간을 제공하므로 sin (x) * 43758.5453은- 43758.5453 ~ 43758.5453. 이것은 매우 큰 범위이므로 x의 작은 단계조차도 결과에서 큰 단계를 제공하고 분수 부분에서 실제로 큰 변동을 제공합니다. "fract"는 -0.99 ... ~ 0.999 ... 범위의 값을 가져 오는 데 필요합니다. 이제 해시 함수와 같은 것이 있으면 벡터에서 생산 해시를위한 함수를 만들어야합니다. 가장 간단한 방법은 입력 벡터의 모든 y 구성 요소에 대해 "해시"를 별도로 호출하는 것입니다. 그러나 우리는 대칭적인 가치를 갖게 될 것입니다. 그래서, 우리는 벡터로부터 어떤 값을 얻어야합니다. 접근법은 어떤 임의의 벡터를 찾고 그 벡터에 대한 "점"곱을 찾는 것입니다. 여기에 우리가갑니다 : fract (sin (dot (co.xy, vec2 (12.9898,78.233))) * 43758.5453); 또한 선택한 벡터에 따라 "dot"곱이 계산 된 후 "sin"함수의 여러 페 로이드를 가질 수있을만큼 길이가 길어야합니다.


하지만 4e5도 작동해야합니다. 매직 넘버가 43758.5453 인 이유를 이해할 수 없습니다. (또한, I는 않도록 랜드 (0) = 0에 의해 일부 분수 X을 상쇄한다.
파브리스 NEYRET

1
나는 4e5를 사용하면 분수 비트의 많은 변화를 얻지 못할 것이며 항상 동일한 값을 제공 할 것이라고 생각합니다. 따라서 두 가지 조건이 충족되어야합니다. 충분히 크고 분수 부분의 변형이 충분해야합니다.
Roman

"항상 똑같은 가치를 줄 것"이란 무슨 뜻입니까? (항상 동일한 숫자를 사용한다는 것을 의미한다면, 첫째, 여전히 혼란스럽고, 둘째, float는 10 ^ p가 아닌 m * 2 ^ p로 저장되므로 * 4e5는 여전히 비트를 스크램블합니다).
Fabrice NEYRET

나는 당신이 숫자 4 * 10 ^ 5의 지수 표현을 썼다고 생각했기 때문에 sin (x) * 4e5는 그렇게 혼란스러운 숫자를주지 않을 것입니다. 나는 sin wave의 분수 비트가 당신에게 좋은 Chatoic을 줄 것이라는 데 동의합니다.
Roman

그러나 x의 범위에 따라 달라집니다. 즉, 함수가 작은 값 (-0.001, 0.001)과 큰 값 (-1, 1)에 대해 강건해야하는지 의미합니다. fract (sin (x /1000.0) * 43758.5453); 및 fract (sin (x /1000.0) * 4e5) ;, 여기서 x는 [-1., 1.] 범위에 있습니다. 두 번째 변형 이미지는 더 단조롭습니다 (적어도 셰이더에서 차이를 볼 수 있습니다). 그러나 일반적으로 나는 여전히 4e5를 사용할 수 있고 충분한 결과를 얻을 수 있다는 데 동의합니다.
Roman
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.