此GLSL rand()单行代码的来源是什么?

问题描述 投票:88回答:5

我已经看到用于here and there around the web的着色器中的伪随机数生成器:

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

它被不同地称为“规范”,或“我在网上某处找到的单线”。

此功能的起源是什么?常数值看起来像是任意的吗,还是选择某些艺术?是否有关于此功能优点的讨论?

编辑:我遇到的对该功能的最早引用是this archive from Feb '08,现在原始页面已从网络上删除。但是那里没有比其他地方更多的讨论。

glsl shader prng
5个回答
41
投票

非常有趣的问题!

我试图在输入答案时解决这个问题:)首先,一个简单的玩法: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哈希中使用的典型整数运算转换为float(半位或32bit)或定点运算,但应该可以。

再次,将其作为哈希函数的真正问题是sin()是一个黑匣子。


19
投票

[起源可能是论文:“借助y = [(a + x)sin(bx)] mod 1生成随机数,W.J.J。雷伊,第22届欧洲统计学家会议和第七届维尔纽斯概率论与数理统计会议,1998年8月]

编辑:由于我找不到本文的副本,并且“ 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。

注意,这是针对流的。转换为一维哈希'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
}

8
投票

常数是任意的,尤其是它们非常大,并且与质数相距小数点后两位。

高振幅窦的1上的模数乘以4000是一个周期函数。它就像一个百叶窗或一个很小的波纹金属,因为它乘以4000,然后由点积成角度旋转。

因为函数是二维的,所以点积具有使周期函数相对于X和Y轴倾斜的效果。以大约13/79的比例。这是低效的,您实际上可以通过做(13x + 79y)的正弦来实现相同的目标,这也将以较少的数学实现相同的事情。

如果在X和Y中都找到函数的周期,则可以对其进行采样,使其再次看起来像一个简单的正弦波。

这里是放大了graph的图片

我不知道起源,但它与许多其他起源相似,如果您以固定间隔在图形中使用它,则它往往会产生莫尔条纹,您可能会发现它最终会再次出现。


1
投票

也许是一些非周期性的混沌映射,它可以解释很多事情,但是也可以只是一些任意的大数操作。


0
投票

这是统一的归一化分布。特别是那个依赖于高精度的浮子。

© www.soinside.com 2019 - 2024. All rights reserved.