Nguồn gốc của GLSL rand () one-liner này là gì?


92

Tôi đã thấy trình tạo số giả ngẫu nhiên này để sử dụng trong các trình tạo bóng được đề cập ở đây và ở đó trên web :

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

Nó có nhiều tên gọi khác nhau là "chuẩn" hoặc "một lớp lót mà tôi tìm thấy trên web ở đâu đó".

Nguồn gốc của chức năng này là gì? Các giá trị không đổi có tùy ý như chúng có vẻ không hay có một số nghệ thuật đối với lựa chọn của chúng? Có bất kỳ cuộc thảo luận nào về giá trị của chức năng này?

CHỈNH SỬA: Tài liệu tham khảo lâu đời nhất về hàm này mà tôi đã xem qua là kho lưu trữ này từ tháng 2 năm 08 , trang gốc hiện đã bị xóa khỏi web. Nhưng không có nhiều cuộc thảo luận về nó ở đó hơn bất cứ nơi nào khác.


Đó là một hàm nhiễu, được sử dụng để tạo địa hình được tạo theo thủ tục. tương tự như một cái gì đó như thế này en.wikipedia.org/wiki/Perlin_noise
foreyez

Câu trả lời:


42

Câu hỏi rất thú vị!

Tôi đang cố gắng tìm ra điều này trong khi nhập câu trả lời :) Đầu tiên, một cách dễ dàng để chơi với nó: 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

Sau đó, hãy nghĩ về những gì chúng tôi đang cố gắng làm ở đây: Đối với hai tọa độ đầu vào x, y, chúng tôi trả về một "số ngẫu nhiên". Bây giờ đây không phải là một con số ngẫu nhiên. Nó giống nhau mỗi khi chúng ta nhập cùng một x, y. Đó là một hàm băm!

Điều đầu tiên hàm làm là chuyển từ 2d đến 1d. Bản thân điều đó không thú vị, nhưng các con số được chọn để chúng không lặp lại một cách điển hình. Ngoài ra, chúng tôi có một bổ sung dấu chấm động ở đó. Sẽ có thêm một vài bit từ y hoặc x, nhưng các số có thể được chọn đúng để nó có sự kết hợp.

Sau đó, chúng tôi lấy mẫu một hàm sin () hộp đen. Điều này sẽ phụ thuộc rất nhiều vào việc thực hiện!

Cuối cùng, nó khuếch đại lỗi trong việc triển khai sin () bằng cách nhân và lấy phân số.

Tôi không nghĩ rằng đây là một hàm băm tốt trong trường hợp chung. Sin () là một hộp đen, trên GPU, về số. Có thể tạo một hàm tốt hơn nhiều bằng cách sử dụng hầu hết mọi hàm băm và chuyển đổi nó. Phần khó là biến phép toán số nguyên điển hình được sử dụng trong băm cpu thành phép toán float (một nửa hoặc 32bit) hoặc điểm cố định, nhưng nó sẽ hoàn toàn khả thi.

Một lần nữa, vấn đề thực sự với điều này như một hàm băm là sin () là một hộp đen.


1
Điều này không trả lời câu hỏi về nguồn gốc, nhưng tôi không nghĩ rằng nó thực sự có thể trả lời được. Tôi sẽ chấp nhận câu trả lời này vì đồ thị minh họa.
Grumdrig

19

Nguồn gốc có lẽ là bài báo: "Về việc tạo số ngẫu nhiên, với sự trợ giúp của y = [(a + x) sin (bx)] mod 1", WJJ Rey, Hội nghị thống kê châu Âu lần thứ 22 và Hội nghị Vilnius lần thứ 7 về Lý thuyết xác suất và Thống kê toán học, tháng 8 năm 1998

CHỈNH SỬA: Vì tôi không thể tìm thấy bản sao của bài báo này và tham chiếu "TestU01" có thể không rõ ràng, đây là sơ đồ như được mô tả trong TestU01 ở dạng pseudo-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;
} 

trong đó giá trị hằng số được đề xuất duy nhất là B1.

Lưu ý rằng đây là cho một luồng. Chuyển đổi thành băm 1D 'n' trở thành lưới số nguyên. Vì vậy, suy đoán của tôi là ai đó đã nhìn thấy điều này và chuyển đổi 't' thành một hàm đơn giản f (x, y). Sử dụng các hằng số ban đầu ở trên sẽ mang lại:

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
Quả thực rất thú vị! Tôi đã tìm thấy một bài báo tham khảo nó cũng như tạp chí trên Google Sách nhưng có vẻ như bài nói hoặc bài báo đó không có trong tạp chí.
Grumdrig

1
Ngoài ra, nó sẽ xuất hiện từ tiêu đề mà chức năng tôi đang hỏi sẽ trở lại fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * (co.xy + vec2(43758.5453, SOMENUMBER))để phù hợp với chức năng của bài báo.
Grumdrig

Và một điều nữa, nếu đây thực sự là nguồn gốc của việc sử dụng hàm, câu hỏi về nguồn gốc của những con số ma thuật (lựa chọn ab) được sử dụng lặp đi lặp lại, nhưng có thể đã được sử dụng trong bài báo mà bạn trích dẫn.
Grumdrig

Tôi cũng không tìm thấy tờ giấy nữa. (chỉnh sửa: cùng một bài báo như được liên kết ở trên)
MB Reynolds

Cập nhật câu trả lời với nhiều thông tin hơn.
MB Reynolds

8

các giá trị hằng số là tùy ý, đặc biệt là chúng rất lớn và cách số nguyên tố một vài số thập phân.

môđun trên 1 của xoang biên độ hi nhân với 4000 là một hàm tuần hoàn. nó giống như một tấm rèm che cửa sổ hoặc một tấm kim loại gợn sóng được làm rất nhỏ bởi vì nó nhân với 4000 và biến một góc bởi tích số chấm.

vì hàm là 2-D, tích dấu chấm có tác dụng biến hàm tuần hoàn theo phương xiên so với trục X và Y. Theo tỷ lệ khoảng 13/79. Nó không hiệu quả, bạn thực sự có thể đạt được điều tương tự bằng cách thực hiện sin của (13x + 79y), điều này cũng sẽ đạt được điều tương tự như tôi nghĩ với ít toán hơn ..

Nếu bạn tìm thấy chu kỳ của hàm trong cả X và Y, bạn có thể lấy mẫu để nó giống như một sóng sin đơn giản một lần nữa.

Đây là hình ảnh của nó được phóng to trong biểu đồ

Tôi không biết nguồn gốc nhưng nó tương tự như nhiều người khác, nếu bạn sử dụng nó trong đồ họa thường xuyên, nó sẽ có xu hướng tạo ra các mẫu moire và bạn có thể thấy nó cuối cùng sẽ lặp lại.


Nhưng trên GPU X và Y có phạm vi từ 0..1 và điều đó trông ngẫu nhiên hơn nhiều nếu bạn thay đổi biểu đồ của mình. Tôi biết điều này nghe có vẻ giống như một tuyên bố nhưng nó thực sự là một câu hỏi, bởi vì việc học toán của tôi đã kết thúc năm 18 tuổi.
Chuỗi

tôi biết, tôi chỉ phóng to để bạn có thể thấy rằng hàm ngẫu nhiên có dạng đó ngoại trừ việc các đường gờ thay đổi rất nhanh, ngoại trừ việc bạn phải phóng to nhỏ để xem các thay đổi ... bạn có thể tưởng tượng rằng việc lấy điểm trên các đường gờ sẽ cho các số khá ngẫu nhiên từ 0 đến 1 chiều cao cho các giá trị x và y từ 1 đến 1.

Ồ, tôi hiểu, và điều đó có vẻ rất hợp lý đối với bất kỳ thế hệ số ngẫu nhiên nào mà cốt lõi của nó sử dụng hàm sin
Strings

2
về cơ bản nó là một đường ziczac tuyến tính, và tội lỗi là phải thêm một chút biến thể nhỏ, nó giống như thể ai đó đang lật một gói thẻ từ một đến 10 vòng rất nhanh trước mặt bạn và bạn phải thử kết thúc chọn một mẫu số từ các thẻ, chúng sẽ là các số ngẫu nhiên vì nó sẽ nhấp nháy rất nhanh đến mức anh ta chỉ có thể lấy một mẫu bằng cách chọn các thẻ trong đồng bộ hóa chính xác liên quan đến tốc độ quay của thẻ.

Chỉ cần một lưu ý, sẽ không nhanh hơn để làm (13x + 79y)dot(XY, AB)nó sẽ làm chính xác những gì bạn mô tả, như sản phẩm chấm của nó,x,y dot 13, 79 = (13x + 79y)
whn

1

Có thể đó là một số ánh xạ hỗn loạn không lặp lại, sau đó nó có thể giải thích nhiều điều, nhưng cũng có thể chỉ là một số thao tác tùy ý với số lượng lớn.

CHỈNH SỬA: Về cơ bản, hàm fract (sin (x) * 43758.5453) là một hàm dạng băm đơn giản, sin (x) cung cấp phép nội suy sin mượt mà giữa -1 đến 1, vì vậy sin (x) * 43758.5453 sẽ được nội suy từ - 43758.5453 đến 43758.5453. Đây là một phạm vi khá lớn, vì vậy ngay cả bước nhỏ trong x cũng sẽ mang lại kết quả là bước lớn và sự thay đổi thực sự lớn trong phần phân số. "Fract" là cần thiết để nhận các giá trị trong phạm vi -0,99 ... đến 0,999 .... Bây giờ, khi chúng ta có một cái gì đó như hàm băm, chúng ta nên tạo hàm cho hàm băm sản xuất từ ​​vector. Cách đơn giản nhất là gọi "băm" riêng lẻ cho x bất kỳ thành phần y nào của vectơ đầu vào. Nhưng sau đó, chúng ta sẽ có một số giá trị đối xứng. Vì vậy, chúng ta nên nhận một số giá trị từ vectơ, cách tiếp cận là tìm một số vectơ ngẫu nhiên và tìm tích "chấm" cho vectơ đó, ở đây chúng ta bắt đầu: fract (sin (dot (co.xy, vec2 (12.9898,78.233))) * 43758.5453); Ngoài ra, theo vectơ đã chọn, độ dài của nó phải dài để có một số peroit của hàm "sin" sau tích "chấm" sẽ được tính.


nhưng sau đó 4e5 cũng nên hoạt động, tôi không hiểu tại sao số 43758.5453 kỳ diệu. (ngoài ra, tôi sẽ bù x bằng một số phân số nào đó để tránh rand (0) = 0.
Fabrice NEYRET 29-04-16

1
Tôi nghĩ với 4e5, bạn sẽ không nhận được quá nhiều biến thể của các bit phân số, nó sẽ luôn cung cấp cho bạn cùng một giá trị. Vì vậy, hai điều kiện phải được đáp ứng, đủ lớn và có đủ sự biến đổi tốt của các phần của phân số.
La Mã

ý bạn là gì, "sẽ luôn mang lại cho bạn giá trị tương tự"? (nếu bạn có nghĩa là nó sẽ luôn lấy các chữ số giống nhau, thứ nhất, chúng vẫn hỗn loạn, thứ hai, float được lưu trữ dưới dạng m * 2 ^ p, không phải 10 ^ p, vì vậy * 4e5 vẫn xáo trộn các bit).
Fabrice NEYRET

Tôi nghĩ rằng bạn đã viết một biểu diễn hàm mũ của số, 4 * 10 ^ 5, vì vậy sin (x) * 4e5 sẽ cung cấp cho bạn một số không quá hỗn loạn. Tôi đồng ý rằng các bit phân đoạn từ sóng sin cũng sẽ cung cấp cho bạn chatoic tốt.
Roman

Nhưng, sau đó nó phụ thuộc vào phạm vi của x, ý tôi là nếu hàm phải mạnh mẽ đối với các giá trị nhỏ (-0,001, 0,001) và lớn (-1, 1). Bạn có thể thử xem sự khác biệt với fract (sin (x /1000.0) * 43758.5453); và fract (sin (x /1000.0) * 4e5) ;, trong đó x trong khoảng [-1., 1]. Trong hình ảnh biến thể thứ hai sẽ đơn điệu hơn (ít nhất tôi thấy sự khác biệt trong bộ đổ bóng). Tuy nhiên, nhìn chung, tôi đồng ý rằng bạn vẫn có thể sử dụng 4e5 và có kết quả đủ tốt.
Roman
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.