【问题标题】:Perlin Noise Generation [closed]柏林噪声生成[关闭]
【发布时间】:2015-04-18 00:57:00
【问题描述】:

我整天都在尝试让 Perlin 噪声生成器正常工作,但在实现 this tutorial 中的伪代码时遇到了麻烦。

this question的回复中显示了类似的代码。

问题是我不知道在 Hugo Elias 文章底部附近的 PerlinNoise_2D 函数中 x 和 y 的输入值应该是什么(或者 Total 函数中的 i 和 j 值如果您正在查看前面的 Stack Overflow 问题)。

我有一个 500 x 500 的数组,我将像素值存储到其中,所以起初我以为我应该为每个像素循环遍历 PerlinNoise_2D(或 Total)函数,但是这导致我立即超出数组的范围,因为第一个函数调用之一的代码使用索引x - 1,这当然意味着当我给它我的第一个 x 索引(显然是 0)时,它会中断.

PerlinNoise_2D(或Total)函数绝对看起来像是 Perlin 类的预期入口点,而且它看起来也像是可以返回我想要的值的函数,但我不能我的生活弄清楚我应该传入什么,因为它绝对不是我的 x 和 y 像素数组索引。

有人知道我应该在这里传递什么吗?

【问题讨论】:

  • 这个中断的函数调用是什么——“第一个函数调用”——它是如何导致你超出数组范围的?噪声函数不会对任何内容进行任何索引。
  • 为了避免延续一个常见的误解:Elias 的文章中描述的噪音根本不是 Perlin 噪音,而是价值噪音,分层为分形噪音(参见相关的维基百科文章)。

标签: c++ algorithm graphics


【解决方案1】:

您最初的假设是正确的,该函数希望您传入一个坐标对。在代码中看到x-1,它只是噪声计算的中间值。该值不用作数组的索引。

我已经在下面的 C++ 程序中实现了伪代码。

// Two-dimensional value noise based on Hugo Elias's description:
//   http://freespace.virgin.net/hugo.elias/models/m_perlin.htm

#include <cstdio>
#include <cmath>
#include <cstdlib>
using namespace std;

int numX = 512,
    numY = 512,
    numOctaves = 7;
double persistence = 0.5;

#define maxPrimeIndex 10
int primeIndex = 0;

int primes[maxPrimeIndex][3] = {
  { 995615039, 600173719, 701464987 },
  { 831731269, 162318869, 136250887 },
  { 174329291, 946737083, 245679977 },
  { 362489573, 795918041, 350777237 },
  { 457025711, 880830799, 909678923 },
  { 787070341, 177340217, 593320781 },
  { 405493717, 291031019, 391950901 },
  { 458904767, 676625681, 424452397 },
  { 531736441, 939683957, 810651871 },
  { 997169939, 842027887, 423882827 }
};

double Noise(int i, int x, int y) {
  int n = x + y * 57;
  n = (n << 13) ^ n;
  int a = primes[i][0], b = primes[i][1], c = primes[i][2];
  int t = (n * (n * n * a + b) + c) & 0x7fffffff;
  return 1.0 - (double)(t)/1073741824.0;
}

double SmoothedNoise(int i, int x, int y) {
  double corners = (Noise(i, x-1, y-1) + Noise(i, x+1, y-1) +
                    Noise(i, x-1, y+1) + Noise(i, x+1, y+1)) / 16,
         sides = (Noise(i, x-1, y) + Noise(i, x+1, y) + Noise(i, x, y-1) +
                  Noise(i, x, y+1)) / 8,
         center = Noise(i, x, y) / 4;
  return corners + sides + center;
}

double Interpolate(double a, double b, double x) {  // cosine interpolation
  double ft = x * 3.1415927,
         f = (1 - cos(ft)) * 0.5;
  return  a*(1-f) + b*f;
}

double InterpolatedNoise(int i, double x, double y) {
  int integer_X = x;
  double fractional_X = x - integer_X;
  int integer_Y = y;
  double fractional_Y = y - integer_Y;

  double v1 = SmoothedNoise(i, integer_X, integer_Y),
         v2 = SmoothedNoise(i, integer_X + 1, integer_Y),
         v3 = SmoothedNoise(i, integer_X, integer_Y + 1),
         v4 = SmoothedNoise(i, integer_X + 1, integer_Y + 1),
         i1 = Interpolate(v1, v2, fractional_X),
         i2 = Interpolate(v3, v4, fractional_X);
  return Interpolate(i1, i2, fractional_Y);
}

double ValueNoise_2D(double x, double y) {
  double total = 0,
         frequency = pow(2, numOctaves),
         amplitude = 1;
  for (int i = 0; i < numOctaves; ++i) {
    frequency /= 2;
    amplitude *= persistence;
    total += InterpolatedNoise((primeIndex + i) % maxPrimeIndex,
        x / frequency, y / frequency) * amplitude;
  }
  return total / frequency;
}

int main(int argc, char** args) {
  if (argc >= 3) {
    numX = atoi(args[1]);
    numY = atoi(args[2]);
  }
  if (argc >= 4) {
    numOctaves = atoi(args[3]);
  }
  if (argc >= 5) {
    persistence = atof(args[4]);
  }
  if (argc >= 6) {
    primeIndex = atoi(args[5]) % maxPrimeIndex;
  }
  fprintf(stderr, "numX: %d, numY: %d, numOctaves: %d, persistence: %.5f, ",
      numX, numY, numOctaves, persistence);
  fprintf(stderr, "primeIndex: %d\n", primeIndex);
  printf("var rawNoise = [\n");
  for (int y = 0; y < numY; ++y) {
    for (int x = 0; x < numX; ++x) {
      double noise = ValueNoise_2D(x, y);
      if (x == 0) {
        printf("  [");
      }
      printf("%.5f", noise);
      if (x == numX-1) {
        printf("]");
        if (y == numY-1) {
          printf("\n];\n");
        } else {
          printf(",\n");
        }
      } else {
        printf(", ");
      }
    }
  }
  return 0;
}

该程序在命令行上最多接受五个参数。前四个参数分别对应参数numXnumYnumOctavespersistence

第五个参数是primeIndex,一个从0到9的整数,它决定了十个随机数生成器中的哪一个首先被调用。因此,在固定其他四个参数的值后,您可以获得十种不同的结果。

程序的输出是一个 JavaScript 数组。如果将此输出存储在名为 rawNoise.js 的文件中,则可以加载以下网页以查看噪声图像。

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
  <title> Demonstration of two-dimensional value noise </title>
<script src="rawNoise.js"></script>
<script>
  var ValueNoise = {
    noise: { raw: rawNoise }
  };
  ValueNoise.load = function () {
    var g = ValueNoise,
        raw = g.noise.raw,
        numR = g.numR = raw.length,
        numC = g.numC = raw[0].length,
        minValue = raw[0][0],
        maxValue = minValue;
    for (var r = 0; r < numR; ++r) {
      for (var c = 0; c < numC; ++c) {
        maxValue = Math.max(maxValue, raw[r][c]);
        minValue = Math.min(minValue, raw[r][c]);
      }
    }
    var valueSpread = maxValue - minValue;
    console.log(minValue, maxValue, valueSpread);
    var container = document.getElementById('display'),
        canvas = document.createElement('canvas'),
        context = canvas.getContext('2d'),
        imageData = context.createImageData(numC, numR),
        data = imageData.data;
    for (var r = 0; r < numR; ++r) {
      for (var c = 0; c < numC; ++c) {
        var value = raw[r][c],
            scaled = Math.round(255 * (value - minValue) / valueSpread),
            pos = r*4*numC + 4*c;
        data[pos] = data[pos+1] = data[pos+2] = scaled;
        data[pos+3] = 255;
      }
    }
    console.log(imageData);
    canvas.width = numC;
    canvas.height = numR;
    container.appendChild(canvas);
    context.putImageData(imageData, 0, 0);
  };
  window.onload = ValueNoise.load;
</script>
</head>
<body>
<div id="wrapper">

<div id="display"></div>

</div><!--end wrapper -->
</body>
</html>

在 Unix 风格的命令行上,您可以像这样编译和运行 C++ 程序:

g++ -O2 noise.cpp -o noise
./noise 800 800 9 0.65 3 > rawNoise.js

然后如果你打开上面的网页,你会看到这个图片:

【讨论】:

  • 非常感谢。我以为我应该存储一个仅包含原始噪声数据的数组,然后遍历该数组并使用它来创建一个平滑的版本,然后让我认为 x - 1 应该是索引到它。感谢您解决这个问题。
  • 我仍然遇到问题,所以我逐行复制并粘贴了您的代码,但我仍然得到这个:i.imgur.com/IxoBSE6.png 我这里有什么遗漏吗?我更改了 numX 和 numY 以制作 500x500 的地图。在我返回的所有 250000 个值中,最大值是 3.12,最低是 0.0015。所以我取范围 0 到 4 并将其映射到 0 到 255,并使用它显示颜色(0 为黑色,255 为白色),这就是我得到的。我还有什么事情要做吗?
  • 重复似乎是算法参数造成的。你试过玩这些吗?
  • 我的意思是,即使我在制作我的代码时使用了您的代码作为参考,但我遇到了问题。我决定复制并粘贴你的整个代码,看看你的代码是否返回了我没有返回的东西。它或多或少地返回了相同的东西。是的,我检查了我的阵列。我接受了双重噪音,并立即将其存储到我的数组中。之后,我将这些值映射到可用于颜色的范围。
  • 是的,我把参数弄乱了一点,但它似乎做得不太好。不过,我并不十分关心重复。我认为此时应该是阴天的样子,像这样:cochoy-jeremy.developpez.com/tutoriels/2d/…
猜你喜欢
  • 2021-04-29
  • 2012-01-29
  • 1970-01-01
  • 2017-11-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多