这种GLSL rand()单衬板的起源是什么?


92

我已经看到了此伪随机数生成器,可用于在网上和此处引用的着色器:

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

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

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

编辑:我遇到的对此功能的最早引用是082月的存档,现在原始页面已从网络上消失。但是,那里没有比其他地方更多的讨论了。


这是一个噪波函数,用于创建程序生成的地形。类似于这样的东西en.wikipedia.org/wiki/Perlin_noise
foreyez

Answers:


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哈希中使用的典型整数运算转换为float(half或32bit)或定点运算,但是应该可行。

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


1
这不能回答有关原产地的问题,但我认为这不是真正可以回答的问题。由于示例性图表,我将接受此答案。
Grumdrig 2014年

19

起源可能是论文: “借助y = [(a + x)sin(bx)] mod 1生成随机数”,WJJ Rey,第22届欧洲统计学家会议以及第七届维尔纽斯概率论会议和数理统计,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。

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

1
而且,从标题中可以看出,我要询问的功能应该返回fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * (co.xy + vec2(43758.5453, SOMENUMBER))以符合论文所要使用的功能。
Grumdrig

还有一件事,如果这确实是该函数使用的起源,那么一遍又一遍使用的幻数(选择ab)的起源的问题仍然存在,但是您引用的论文中可能已经使用过。
Grumdrig 2015年

我也找不到纸了。(编辑:与上述链接相同的文章)
MB雷诺兹

使用更多信息更新答案。
MB雷诺兹

8

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

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

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

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

这是它的图片放大

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


但是在GPU上,X和Y的范围是0..1,如果更改图形,则看起来会更加随机。我知道这听起来像是一个陈述,但这实际上是一个问题,因为我的数学教育在18岁时结束。
2013年

我知道,我只是放大了一下,所以您可以看到随机函数具有这种形式,除了脊线变化非常快外,除了您必须缩小以查看所有变化外……您可以想象得出一点在山脊上,将为1和1的x和y值给出从0到1的高度的随机数。
异形2013年

哦,我明白了,对于任何以sin函数为核心的随机数生成来说,这似乎都是合乎逻辑的

2
它本质上是一个线性的锯齿形,而且罪恶应该增加一点点变化,就像有人在您面前快速地将一包纸牌从一圈快速滑动到十圈一样,您应该尝试一下最后从纸牌中选取一个数字模式,它们将是随机数,因为它甩得很快,他只能通过相对于纸牌旋转的速度完全同步地选择纸牌来获得模式。
2013年

刚一说明,它不会是快做(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分量x调用“哈希”。但是,那么我们将有一些对称值。因此,我们应该从向量中获取一些值,方法是找到一些随机向量,并找到该向量的“点”乘积,然后开始:fract(sin(dot(co.xy,vec2(12.9898,78.233)))* 43758.5453); 同样,根据选择的向量,在计算“点”积之后,其长度应足够长以具有“正弦”函数的多个垂直点。


但是4e5应该也能正常工作,我不明白为什么魔术数字43758.5453。(另,我会通过一些分数来避免兰特(0)= 0偏移x
和Fabrice NEYRET

1
我认为使用4e5时,分数位数不会有太大变化,它将始终为您提供相同的值。因此,必须满足两个条件,两个条件要足够大,并且分数部分要有足够好的变化。
罗马,

您是什么意思,“将始终为您提供相同的价值”?(如果要表示它将始终使用相同的数字,则第一,它们仍然是混乱的,第二,浮点数将存储为m * 2 ^ p而不是10 ^ p,因此* 4e5仍会加扰位)。
Fabrice NEYRET

我以为您写了数字的指数表示形式4 * 10 ^ 5,所以sin(x)* 4e5不会给您那么混乱的数字。我同意罪浪中的小数位也会给您带来好消息。
罗马,

但是,这取决于x的范围,我的意思是函数对于小值(-0.001,0.001)和大值(-1,1)是否应具有鲁棒性。您可以尝试使用fract(sin(x /1000.0)* 43758.5453)来查看差异。和fract(sin(x /1000.0)* 4e5);,其中x在[-1。,1.]范围内。在第二个变体中,图像将更加单调(至少我看到了着色器中的差异)。但是,总的来说,我同意您仍然可以使用4e5并获得足够好的结果。
罗马,
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.