我的 Perlin Noise 函数有太多黑点和小白条

My Perlin Noise function having too many dark spots, and little strips of white

我大致按照 java C# 教程创建了一个 Perlin 噪声函数。 然后我使用 Unity 对其进行可视化(我认为问题不在于 unity,因为它们的内置功能完美运行)

问题是大黑点太多了。 Here is a photo

This is how I would like it to look

如您所见,这些点似乎没有很好地融合。

这是我的噪音代码:

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PerlinNoise
{
    public PerlinNoise()
    {
    }

    public double GetPixel(double x, double y)
    {
        int xint = (int)Math.Floor(x) & 255;
        int yint = (int)Math.Floor(y) & 255;    //gets what cube the input coords are


        int g1 = p[p[xint] + yint];
        int g2 = p[p[xint + 1] + yint];
        int g3 = p[p[xint] + yint + 1];
        int g4 = p[p[xint + 1] + yint + 1];     //gets the gradient vector's vaule on the permutation table

        double xdec = x - Math.Floor(x);
        double ydec = y - Math.Floor(y);    //gets the pixel inside the cube

        double d1 = grad(g1, xdec, ydec);
        double d2 = grad(g2, xdec - 1, ydec);
        double d3 = grad(g3, xdec, ydec - 1);
        double d4 = grad(g4, xdec - 1, ydec - 1);

        double u = fade(xdec);
        double v = fade(ydec);

        double x1Inter = lerp(u, d1, d2);
        double x2Inter = lerp(u, d3, d4);
        double yInter = lerp(v, x1Inter, x2Inter);

        return yInter;
    }

    private static double lerp(double amount, double left, double right)
    {
        return ((1 - amount) * left + amount * right);
    }

    private static double fade(double t)
    {
        return t * t * t * (t * (t * 6 - 15) + 10);
    }

    private static double grad(int hash, double x, double y)
    {
        switch (hash & 3)
        {
            case 0: return x + y;
            case 1: return -x + y;
            case 2: return x - y;
            case 3: return -x - y;
            default: return 0;
       }
   }

    private static readonly int[] p = { 151,160,137,91,90,15,
    131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23,    
    190, 6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33,
    88,237,149,56,87,174,20,125,136,171,168, 68,175,74,165,71,134,139,48,27,166,
    77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244,
    102,143,54, 65,25,63,161, 1,216,80,73,209,76,132,187,208, 89,18,169,200,196,
    135,130,116,188,159,86,164,100,109,198,173,186, 3,64,52,217,226,250,124,123,
    5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42,
    223,183,170,213,119,248,152, 2,44,154,163, 70,221,153,101,155,167, 43,172,9,
    129,22,39,253, 19,98,108,110,79,113,224,232,178,185, 112,104,218,246,97,228,
    251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,107,
    49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,150,254,
    138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180,151,160,137,91,90,15,
    131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23,
    190, 6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33,
    88,237,149,56,87,174,20,125,136,171,168, 68,175,74,165,71,134,139,48,27,166,
    77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244,
    102,143,54, 65,25,63,161, 1,216,80,73,209,76,132,187,208, 89,18,169,200,196,
    135,130,116,188,159,86,164,100,109,198,173,186, 3,64,52,217,226,250,124,123,
    5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42,
    223,183,170,213,119,248,152, 2,44,154,163, 70,221,153,101,155,167, 43,172,9,
    129,22,39,253, 19,98,108,110,79,113,224,232,178,185, 112,104,218,246,97,228,
    251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,107,
    49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,150,254,
    138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180
    };

}

这里是统一贴图代码:

using UnityEngine;
using System.Collections;
using System.Runtime.InteropServices.ComTypes;

// Create a texture and fill it with Perlin noise.
// Try varying the xOrg, yOrg and scale values in the inspector
// while in Play mode to see the effect they have on the noise.

public class TextureApply : MonoBehaviour
{
    // Width and height of the texture in pixels.
    public int pixWidth;
    public int pixHeight;

    // The origin of the sampled area in the plane.
    public float xOrg;
    public float yOrg;

    // The number of cycles of the basic noise pattern that are repeated
    // over the width and height of the texture.
    public float scale = 1.0F;

    private Texture2D noiseTex;
    private Color[] pix;
    private Renderer rend;

    void Start()
    {
        rend = GetComponent<Renderer>();

        // Set up the texture and a Color array to hold pixels during processing.
        noiseTex = new Texture2D(pixWidth, pixHeight);
        pix = new Color[noiseTex.width * noiseTex.height];
        rend.material.mainTexture = noiseTex;

        CalcNoise();
    }

    void CalcNoise()
    {
        // For each pixel in the texture...
        float y = 0.0F;

         while (y < noiseTex.height)
        {
            float x = 0.0F;
            while (x < noiseTex.width)
             {
                double xCoord = xOrg + x / noiseTex.width * scale;
                double yCoord = yOrg + y / noiseTex.height * scale;

                PerlinNoise perlin = new PerlinNoise();
                double sample = perlin.GetPixel(xCoord, yCoord);
            
                pix[(int)y * noiseTex.width + (int)x] = new Color((float)sample, (float)sample, (float)sample);
                x++;
            }
            y++;
        }

        // Copy the pixel data to the texture and load it into the GPU.
        noiseTex.SetPixels(pix);
        noiseTex.Apply();
    }

    void OnValidate()
    {
        CalcNoise();
    }
}

以上只是 docs.unity3d.com

的修改代码

我认为问题可能出在排列 table 上,我只是复制并粘贴了它,而不是采用一些复杂的方式将值的数量加倍。

编辑: [这是我将其更改为 Simplex Noise 时得到的图片][1] [1]: https://i.stack.imgur.com/jfxNr.png

这是正在使用的渐变集。方向 (1, 1) 直接指向对角线邻居。如果那些对角线邻居有 (-1, -1),那么它们相长干涉。其他角对也是如此,当它们彼此指向负而不是正时。编辑:您可以尝试 +x + ROOT2*y, +ROOT2*x + y 及其所有符号排列,以产生 8 个渐变。

此外,即使你改进了梯度,你可能听说过 Perlin 是一种较老的噪声方法,它会产生很多 45 度和 90 度的偏差。我推荐它的情况并不多。如果您想将噪声作为一种编程练习来实现,那就太好了。但是如果你想在项目中使用噪声,我会编码或导入一个好的 2D 单纯形实现。

编辑:查看评论。我看错图了。您需要的解决方案只是 sample = sample * 0.5 + 0.5 重新缩放输出,这样图像中的负值就不会全部被截断。

此外,如果您想将噪声转换为基本的单纯形,您可以这样做:

  1. 定义常量 double F2 = 0.366025403784439double G2 = -0.211324865405187。这些将在下面使用,以处理三角形网格。
  2. 在GetPixel(double x, double y)的开头加上double s = (x + y) * F2;x += s; y += s;。这会将在正方形网格上产生整数的坐标转换为在代表等边三角形的 diagonally-compressed 正方形网格上产生整数的坐标。
  3. 定义 xdecydec 后,添加 double t = (xdec + ydec) * G2; xdec += t; ydec += t;。这将使这些坐标相对于压缩正方形的底部不倾斜,因此它们现在可以再次用于距离和梯度。
  4. 定义双打 xdec2 = xdec - 1 - G2; ydec2 = ydec - G2;。定义 xdec3 = xdec - G2; ydec3 = ydec - 1 - G2;。定义 xdec4 = xdec - 1 - 2*G2; ydec4 = ydec - 1 - 2*G2;。你需要这个,因为简单地减去 1 不再适用于获得其他四个角的相对坐标,因为角不再与正方形对齐。现在,任何时候你从坐标的 either 中减去 1,你还需要从 both 中减去 G2。因此,对于 #2 和 #3,你从每个减去 G2 一次,对于 #4,你减去它两次 (2*G2)。您可以自己 re-derive 通过查看如果您已经从一个或两个坐标中减去 1 会发生什么,然后再计算您定义 double t.
  5. 的上一步
  6. 更改 d2、d3、d4 以使用这些值,例如 d2 = grad(g2, xdec2, ydec2);
  7. 删除所有以 double u 开头并以 return yInter; 结尾的内容。添加double value = 0;。您将向该值添加内容。
  8. 定义 double a1 = 0.5 - xdec*xdec - ydec*ydec; double a2 = 0.5 - xdec2*xdec2 - ydec2*ydec2; 以及 a3 和 a4 的相同模式。这些函数表示围绕每个三角形顶点的圆圈,圆圈停在三角形边缘。
  9. 添加 if (a1 > 0) value += (a1 * a1) * (a1 * a1) * d1; 和 a2、a3、a4 相同。这些根据梯度和距离为每个圆圈内的噪声添加一个值。
  10. 如果您使用我为 Perlin 建议的改进渐变,请添加 return value * 38.283687591552734375;。如果您使用原始值,我认为乘以的正确值是 75.3929901123046875 但我不确定。不过,这将接近于此。真的,你可能想要更多的单纯形渐变,但 8 应该至少让它看起来比 Perlin 更好。

如果没有改进的梯度,噪点看起来不会很好。您仍然会看到很多 45 度偏差。如果你真的想改善噪点,这是我的建议,使用 24 个渐变。你可以找到这些梯度 here。使用 99.83685446303647 作为最后乘以的值。

    private static double grad(int hash, double x, double y)
    {
        switch (hash % 24)
        {
            case 0:  return 0.130526192220052 * x +   0.99144486137381 * y;
            case 1:  return 0.38268343236509 * x +    0.923879532511287 * y;
            case 2:  return 0.608761429008721 * x +   0.793353340291235 * y;
            case 3:  return 0.793353340291235 * x +   0.608761429008721 * y;
            case 4:  return 0.923879532511287 * x +   0.38268343236509 * y;
            case 5:  return 0.99144486137381 * x +    0.130526192220051 * y;
            case 6:  return 0.99144486137381 * x +   -0.130526192220051 * y;
            case 7:  return 0.923879532511287 * x +  -0.38268343236509 * y;
            case 8:  return 0.793353340291235 * x +  -0.60876142900872 * y;
            case 9:  return 0.608761429008721 * x +  -0.793353340291235 * y;
            case 10: return 0.38268343236509 * x +   -0.923879532511287 * y;
            case 11: return 0.130526192220052 * x +  -0.99144486137381 * y;
            case 12: return -0.130526192220052 * x + -0.99144486137381 * y;
            case 13: return -0.38268343236509 * x +  -0.923879532511287 * y;
            case 14: return -0.608761429008721 * x + -0.793353340291235 * y;
            case 15: return -0.793353340291235 * x + -0.608761429008721 * y;
            case 16: return -0.923879532511287 * x + -0.38268343236509 * y;
            case 17: return -0.99144486137381 * x +  -0.130526192220052 * y;
            case 18: return -0.99144486137381 * x +   0.130526192220051 * y;
            case 19: return -0.923879532511287 * x +  0.38268343236509 * y;
            case 20: return -0.793353340291235 * x +  0.608761429008721 * y;
            case 21: return -0.608761429008721 * x +  0.793353340291235 * y;
            case 22: return -0.38268343236509 * x +   0.923879532511287 * y;
            case 23: return -0.130526192220052 * x +  0.99144486137381 * y;
            default: return 0;
       }
   }

可选优化:您一次只需要a2/g2/xdec2/ydec2、a3/g3/xdec3/ydec3之一。如果 ydec > xdec 那么你可以做 a3/g3/xdec3/ydec3,否则 a2/g2/xdec2/ydec2。几乎所有的教程和实现都会有这个,但你不需要它来让噪音起作用。

可选优化 #2:在第 8 步中,您可以拉入 d1 的定义,并在其中拉入 g1 的定义。这样,只有在该条件中需要定义时才会计算定义(如果编译器尚未捕获)。 if (a1 > 0) value += (a1 * a1) * (a1 * a1) * grad(p[p[xint] + yint], xdec, ydec); 然后您可以将它们从最初计算的位置移除。

注意:一些教程或实现会使用正的G2,并翻转它的additions/subtractions。我发现保持负值最有意义,因为这样 unskew 变换的工作方式与 skew 变换完全一样。但值得牢记。