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

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

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

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

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

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



답변

매우 흥미로운 질문입니다!

나는 대답을 입력하는 동안 이것을 알아 내려고 노력하고있다 🙂 먼저 그것을 가지고 노는 쉬운 방법 : 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 ()이 블랙 박스라는 것입니다.


답변

출처는 아마도 논문 일 것입니다 : “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
}


답변

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

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

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

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

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

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


답변

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

편집 : 기본적으로 함수 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”함수의 여러 페 로이드를 가질 수있을만큼 길이가 길어야합니다.


답변