【问题标题】:Randomly Generated 2D Noise随机生成的 2D 噪声
【发布时间】:2014-09-01 01:27:19
【问题描述】:

我正在尝试生成一个随机 2D 网格,该网格是从 [x, y] 坐标按程序生成的(而不是预先生成一个 2D 数字数组)。这样做的原因是地图可能很大,而且很可能实际上不需要大部分网格。

这是我目前所拥有的:

double value_at(Grid *grid, int seed, int x, int y) {
  srand(seed + x + y * grid->width);
  return (double)rand() / (RAND_MAX + 1.0);
}

更新:要求value_at 具有引用透明度(尽可能)。所以以下必须成立:

int i = value_at(grid, seed, 100, 100);
int j = value_at(grid, seed, 100, 100);

assert(i == j);

换句话说,如果我用相同的参数调用value_at,它应该总是给出相同的结果,无论我在程序执行期间调用它多少次。 这样做的原因是因为我正在尝试生成随机地形,这将涉及不同网格点之间的插值;用户也有可能会重新访问网格中的某个点。

它可以正确生成网格,但是我在输出中看到了很多规律:

你可以看到有一种“颗粒”,它使线在输出中对角线移动。

我正在对此进行一些研究,但在寻找方法时遇到了一些麻烦。有人知道如何让网格更加“随机”吗?

更新:感谢 ldgabbay 提供的链接,我添加了一个简单的旋转哈希函数,如下所示:

int simple_hash(int * is, int count) {
  // this contains a bunch of arbitrarily chosen prime numbers
  int i;
  unsigned int hash = 80238287;

  for (i = 0; i < count; i++){
    hash = (hash << 4) ^ (hash >> 28) ^ (is[i] * 5449 % 130651);
  }

  return hash % 75327403;
}

那么value_at函数看起来像这样:

double value_at(Grid *grid, int seed, int x, int y) {
  int is[] = {x, y, seed};
  srand(simple_hash(is, 3));
  return (double)rand() / (RAND_MAX + 1.0);
}

以下是它生成的一些示例网格:

【问题讨论】:

  • 停止播种 get 函数。像srand((unsigned int)time(NULL)); 这样的rand-seed 属于main() 的顶部; once 用于该过程,除非您有意尝试重新生成相同的序列(并且在此处出现的情况并非如此)。从那开始。 (无关:如果我慢慢滚动页面 =P,这些图像可以很好地测试我的像素刷新烧毁)。
  • 我在问题中添加了附加要求。如果我在流程开始时只播种一次,那么要求不成立。
  • 所以你想要一些随机的,但只有一次,然后重复?在我看来,知道初始化状态并将图像存储在存储中以进行检索(即使作为double 的矩阵)将是实现这一目标的机制。或者我仍然不理解 真正的 问题(最近不太可能)。
  • 如果在给定相同参数的情况下对value_at() 的两次调用必须生成相同的结果,最好不要使用系统srand()/rand(),因为这样这些函数对于其余代码将变得无用。最好使用自己的robbrit_rand()。许多互联网源代码可用于良好的 random() 函数。

标签: c random procedural-generation


【解决方案1】:

我认为您使用srand 来使value_at 具有确定性存在一个微妙的缺陷。通常,随机数生成器会播种一次,并且从中产生的数字是伪随机的。但是,在这种方法中,您正在查看从随机数生成器中为不同种子生成的第一个随机数。这可能不像您希望的那样随机。我希望这高度依赖于您系统上随机数生成器的实现。

既然你有不能保存随机序列的要求,我有几个想法。

1)

尝试更改用于更改随机种子的公式。现在,随着您在网格中移动,您的种子正在发生非常可预测的变化。 (例如seed、seed+1、seed+2、...、seed+width、seed+width+1等)你想要的是:

srand(seed + hash(x + y * grid->width));

其中hash 很简单,可以将 [0, width*height) 中的一个数字映射到该范围内的另一个数字,但不太可预测。

一些简单的哈希函数可以在here找到。

2)

在获取随机值之前,从随机数生成器中弹出一个确定的随机数。因此,在您的 return 语句之前,添加如下一行:

for (i=0; i!=skip; ++i) rand();

您可以将skip 设置为固定数字,甚至可以将其设为x、y 甚至种子的确定性函数。

【讨论】:

  • 比起srand(seed + hash(x + y * grid-&gt;width));hash(seed + x + y * grid-&gt;width)); 不是更有意义吗?
  • 是的,也可以这样做。关键是要阻止srand 的论点不可预测。
  • 完美,效果很好!我最终对您列表中的旋转散列函数进行了改编,并添加了一些额外的大素数,直到我得到一些看起来很随机的东西。我将在问题中发布代码。
猜你喜欢
  • 2011-12-06
  • 2011-03-20
  • 2012-01-08
  • 2022-01-02
  • 2018-10-07
  • 1970-01-01
  • 2017-02-01
  • 1970-01-01
  • 2010-11-18
相关资源
最近更新 更多