2D 柏林噪声

发布于 2024-12-23 08:15:38 字数 1891 浏览 0 评论 0原文

我已经完全掌握了 3D 柏林噪声的艺术,现在我正在尝试将相同的实现用于 2D 算法。 问题似乎在于选择我的渐变方向。在 3D 中,我在均匀分布的方向上使用 16 个渐变,效果非常好。 在 2D 中我想我会使用 8 个渐变。上、下、左、右和四个对角线方向。

这是我得到的:

在此处输入图像描述

噪声的总体外观始终是正确的,但方块的边缘不太相符。 我也尝试过使用其他渐变或更少的渐变,但得到了类似的结果。 在另一个示例中,您可以看到边缘有时确实匹配,并且该区域的结果很好 -

在此处输入图像描述

当我不使用渐变而只是在四个角中随机选取的值之间进行插值时,我会得到正确的结果,这让我认为是渐变部分搞乱了它。

这是我的代码:

//8 different gradient directions
private Point[] grads = new Point[] { 
    new Point(0, 1), new Point(1, 1), new Point(1, 0), new Point(1, -1), 
    new Point(0, -1), new Point(-1, -1), new Point(-1, 0), new Point(-1, 1),};

//takes the dot product of a gradient and (x, y)
private float dot2D(int i, float x, float y)
{
    return
        grads[i].X * x + grads[i].Y * y;
}

public float Noise2D(float x, float y)
{
    int
        ix = (int)(x),
        iy = (int)(y);

        x  = x - ix;
        y  = y - iy;

    float
        fx  = fade(x),
        fy  = fade(y);

        ix &= 255;
        iy &= 255;

    // here is where i get the index to look up in the list of 
    // different gradients.
    // hashTable is my array of 0-255 in random order
    int
        g00 = hashTable[ix +     hashTable[iy    ]],
        g10 = hashTable[ix + 1 + hashTable[iy    ]],
        g01 = hashTable[ix +     hashTable[iy + 1]],
        g11 = hashTable[ix + 1 + hashTable[iy + 1]];

    // this takes the dot product to find the values to interpolate between
    float
        n00 = dot2D(g00 & 7, x, y),
        n10 = dot2D(g10 & 7, x, y),
        n01 = dot2D(g01 & 7, x, y),
        n11 = dot2D(g11 & 7, x, y);

    // lerp() is just normal linear interpolation
    float
        y1 = lerp(fx, n00, n10),
        y2 = lerp(fx, n01, n11);
    return
        lerp(fy, y1, y2);
}

I have fully mastered the art of Perlin Noise in 3D, and now I'm trying to use my same implementation for a 2D algorithm.
The problem seems to be in picking my gradient directions. In 3D I use 16 gradients in evenly distributed directions and this works great.
In 2D I figured I'd use 8 gradients. up, down, left, right, and the four diagonal directions.

Here is what I get:

enter image description here

The general look of the noise is always correct, but the edges of the squares don't quite match up.
I have also tried using other gradients or fewer gradients but get similar results.
Here in another example you can see that the edges do match up sometimes and the results are fine in that area -

enter image description here

When I don't use gradients and instead just interpolate between a value picked randomly at each of the 4 corners I get the right results, which is what makes me think it is the gradient part that is messing it up.

Here is my code:

//8 different gradient directions
private Point[] grads = new Point[] { 
    new Point(0, 1), new Point(1, 1), new Point(1, 0), new Point(1, -1), 
    new Point(0, -1), new Point(-1, -1), new Point(-1, 0), new Point(-1, 1),};

//takes the dot product of a gradient and (x, y)
private float dot2D(int i, float x, float y)
{
    return
        grads[i].X * x + grads[i].Y * y;
}

public float Noise2D(float x, float y)
{
    int
        ix = (int)(x),
        iy = (int)(y);

        x  = x - ix;
        y  = y - iy;

    float
        fx  = fade(x),
        fy  = fade(y);

        ix &= 255;
        iy &= 255;

    // here is where i get the index to look up in the list of 
    // different gradients.
    // hashTable is my array of 0-255 in random order
    int
        g00 = hashTable[ix +     hashTable[iy    ]],
        g10 = hashTable[ix + 1 + hashTable[iy    ]],
        g01 = hashTable[ix +     hashTable[iy + 1]],
        g11 = hashTable[ix + 1 + hashTable[iy + 1]];

    // this takes the dot product to find the values to interpolate between
    float
        n00 = dot2D(g00 & 7, x, y),
        n10 = dot2D(g10 & 7, x, y),
        n01 = dot2D(g01 & 7, x, y),
        n11 = dot2D(g11 & 7, x, y);

    // lerp() is just normal linear interpolation
    float
        y1 = lerp(fx, n00, n10),
        y2 = lerp(fx, n01, n11);
    return
        lerp(fy, y1, y2);
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

贪了杯 2024-12-30 08:15:38

我有点着急,但这可能会有所帮助。我将 Perlin 的参考实现改编为 C#。对于 2D,只需使用具有固定 z 参数的 3D Noise() 函数。 (public static float Noise(float x, float y, float z) 在课程结束时。)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using System.Diagnostics;

namespace GoEngine.Content.Entities
{
    public class NoiseMaker
    {
        /// adapted from http://cs.nyu.edu/~perlin/noise/
        // JAVA REFERENCE IMPLEMENTATION OF IMPROVED NOISE - COPYRIGHT 2002 KEN PERLIN.

        private static int[] p = new int[512];
        private static int[] permutation = { 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
               };

        static NoiseMaker()
        {
            CalculateP();
        }

        private static int _octaves;
        private static int _halfLength = 256;

        public static void SetOctaves(int octaves)
        {
            _octaves = octaves;

            var len = (int)Math.Pow(2, octaves);

            permutation = new int[len];

            Reseed();
        }

        private static void CalculateP()
        {
            p = new int[permutation.Length * 2];
            _halfLength = permutation.Length;

            for (int i = 0; i < permutation.Length; i++)
                p[permutation.Length + i] = p[i] = permutation[i];
        }

        public static void Reseed()
        {
            var random = new Random();
            var perm = Enumerable.Range(0, permutation.Length).ToArray();

            for (var i = 0; i < perm.Length; i++)
            {
                var swapIndex = random.Next(perm.Length);

                var t = perm[i];

                perm[i] = perm[swapIndex];

                perm[swapIndex] = t;
            }

            permutation = perm;

            CalculateP();

        }

        public static float Noise(Vector3 position, int octaves, ref float min, ref float max)
        {
            return Noise(position.X, position.Y, position.Z, octaves, ref min, ref max);
        }

        public static float Noise(float x, float y, float z, int octaves, ref float min, ref float max)
        {

            var perlin = 0f;
            var octave = 1;

            for (var i = 0; i < octaves; i++)
            {
                var noise = Noise(x * octave, y * octave, z * octave);

                perlin += noise / octave;

                octave *= 2;
            }

            perlin = Math.Abs((float)Math.Pow(perlin,2));
            max = Math.Max(perlin, max);
            min = Math.Min(perlin, min);

            //perlin = 1f - 2 * perlin;

            return perlin;
        }

        public static float Noise(float x, float y, float z)
        {
            int X = (int)Math.Floor(x) % _halfLength;
            int Y = (int)Math.Floor(y) % _halfLength;
            int Z = (int)Math.Floor(z) % _halfLength;

            if (X < 0)
                X += _halfLength;

            if (Y < 0)
                Y += _halfLength;

            if (Z < 0)
                Z += _halfLength;

            x -= (int)Math.Floor(x);
            y -= (int)Math.Floor(y);
            z -= (int)Math.Floor(z);

            var u = Fade(x);
            var v = Fade(y);
            var w = Fade(z);

            int A = p[X] + Y, AA = p[A] + Z, AB = p[A + 1] + Z,      // HASH COORDINATES OF
                B = p[X + 1] + Y, BA = p[B] + Z, BB = p[B + 1] + Z;      // THE 8 CUBE CORNERS,


            return MathHelper.Lerp(
                    MathHelper.Lerp(
                         MathHelper.Lerp(
                            Grad(p[AA], x, y, z) // AND ADD
                            ,
                            Grad(p[BA], x - 1, y, z) // BLENDED
                            ,
                            u
                            )
                        ,
                        MathHelper.Lerp(
                            Grad(p[AB], x, y - 1, z)  // RESULTS
                            ,
                            Grad(p[BB], x - 1, y - 1, z)
                            ,
                            u
                            )
                        ,
                        v
                    )
                    ,
                    MathHelper.Lerp(
                        MathHelper.Lerp(
                            Grad(p[AA + 1], x, y, z - 1) // CORNERS
                            ,
                            Grad(p[BA + 1], x - 1, y, z - 1) // OF CUBE
                            ,
                            u
                            )
                        ,
                        MathHelper.Lerp(
                            Grad(p[AB + 1], x, y - 1, z - 1)
                            ,
                            Grad(p[BB + 1], x - 1, y - 1, z - 1)
                            ,
                            u
                            )
                        ,
                        v
                    )
                    ,
                    w
                );

        }

        static float Fade(float t) { return t * t * t * (t * (t * 6 - 15) + 10); }

        static float Grad(int hash, float x, float y, float z)
        {
            int h = hash & 15;                      // CONVERT LO 4 BITS OF HASH CODE

            float u = h < 8 ? x : y,                 // INTO 12 GRADIENT DIRECTIONS.
                   v = h < 4 ? y : h == 12 || h == 14 ? x : z;

            return ((h & 1) == 0 ? u : -u) + ((h & 2) == 0 ? v : -v);
        }

    }
}

更新

好的,我成功创建了一个可用的 2D 版本。这是该类:

/// implements improved Perlin noise in 2D. 
/// Transcribed from http://www.siafoo.net/snippet/144?nolinenos#perlin2003
/// </summary>
public static class Noise2d
{
    private static Random _random = new Random();
    private static int[] _permutation;

    private static Vector2[] _gradients;

    static Noise2d()
    {
        CalculatePermutation(out _permutation);
        CalculateGradients(out _gradients);
    }

    private static void CalculatePermutation(out int[] p)
    {
        p = Enumerable.Range(0, 256).ToArray();

        /// shuffle the array
        for (var i = 0; i < p.Length; i++)
        {
            var source = _random.Next(p.Length);

            var t = p[i];
            p[i] = p[source];
            p[source] = t;
        }
    }

    /// <summary>
    /// generate a new permutation.
    /// </summary>
    public static void Reseed()
    {
        CalculatePermutation(out _permutation);
    }

    private static void CalculateGradients(out Vector2[] grad)
    {
        grad = new Vector2[256];

        for (var i = 0; i < grad.Length; i++)
        {
            Vector2 gradient;

            do
            {
                gradient = new Vector2((float)(_random.NextDouble() * 2 - 1), (float)(_random.NextDouble() * 2 - 1));
            }
            while (gradient.LengthSquared() >= 1);

            gradient.Normalize();

            grad[i] = gradient;
        }

    }

    private static float Drop(float t)
    {
        t = Math.Abs(t);
        return 1f - t * t * t * (t * (t * 6 - 15) + 10);
    }

    private static float Q(float u, float v)
    {
        return Drop(u) * Drop(v);
    }

    public static float Noise(float x, float y)
    {
        var cell = new Vector2((float)Math.Floor(x), (float)Math.Floor(y));

        var total = 0f;

        var corners = new[] { new Vector2(0, 0), new Vector2(0, 1), new Vector2(1, 0), new Vector2(1, 1) };

        foreach (var n in corners)
        {
            var ij = cell + n;
            var uv = new Vector2(x - ij.X, y - ij.Y);

            var index = _permutation[(int)ij.X % _permutation.Length];
            index = _permutation[(index + (int)ij.Y) % _permutation.Length];

            var grad = _gradients[index % _gradients.Length];

            total += Q(uv.X, uv.Y) * Vector2.Dot(grad, uv);
        }

        return Math.Max(Math.Min(total, 1f), -1f);
    }

}

像这样调用它:

private void GenerateNoiseMap(int width, int height, ref Texture2D noiseTexture, int octaves)
    {
        var data = new float[width * height];

        /// track min and max noise value. Used to normalize the result to the 0 to 1.0 range.
        var min = float.MaxValue;
        var max = float.MinValue;

        /// rebuild the permutation table to get a different noise pattern. 
        /// Leave this out if you want to play with changing the number of octaves while 
        /// maintaining the same overall pattern.
        Noise2d.Reseed();

        var frequency = 0.5f;
        var amplitude = 1f;
        var persistence = 0.25f;

        for (var octave = 0; octave < octaves; octave++)
        {
            /// parallel loop - easy and fast.
            Parallel.For(0
                , width * height
                , (offset) =>
                {
                    var i = offset % width;
                    var j = offset / width;
                    var noise = Noise2d.Noise(i*frequency*1f/width, j*frequency*1f/height);
                    noise = data[j * width + i] += noise * amplitude;

                    min = Math.Min(min, noise);
                    max = Math.Max(max, noise);

                }
            );

            frequency *= 2;
            amplitude /= 2;
        }


        if (noiseTexture != null && (noiseTexture.Width != width || noiseTexture.Height != height))
        {
            noiseTexture.Dispose();
            noiseTexture = null;
        }
        if (noiseTexture==null)
        {
            noiseTexture = new Texture2D(Device, width, height, false, SurfaceFormat.Color);
        }

        var colors = data.Select(
            (f) =>
            {
                var norm = (f - min) / (max - min);
                return new Color(norm, norm, norm, 1);
            }
        ).ToArray();

        noiseTexture.SetData(colors);
    }

请注意,我使用了几个 XNA 结构(Vector2 和 Texture2D),但它们的作用应该非常清楚。

如果您想要具有较少八度音程的更高频率(更多“噪音”)内容,请增加八度音程循环中使用的初始频率值。

此实现使用“改进的”Perlin 噪声,它应该比标准版本快一点。您还可以看看单纯形噪声,它在更高维度上速度要快得多。

I'm in a bit of a rush, but this might be helpful. I adapted Perlin's reference implementation to C#. For 2D, just use the 3D Noise() function with a fixed z parameter. (public static float Noise(float x, float y, float z) towards the end of the class.)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using System.Diagnostics;

namespace GoEngine.Content.Entities
{
    public class NoiseMaker
    {
        /// adapted from http://cs.nyu.edu/~perlin/noise/
        // JAVA REFERENCE IMPLEMENTATION OF IMPROVED NOISE - COPYRIGHT 2002 KEN PERLIN.

        private static int[] p = new int[512];
        private static int[] permutation = { 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
               };

        static NoiseMaker()
        {
            CalculateP();
        }

        private static int _octaves;
        private static int _halfLength = 256;

        public static void SetOctaves(int octaves)
        {
            _octaves = octaves;

            var len = (int)Math.Pow(2, octaves);

            permutation = new int[len];

            Reseed();
        }

        private static void CalculateP()
        {
            p = new int[permutation.Length * 2];
            _halfLength = permutation.Length;

            for (int i = 0; i < permutation.Length; i++)
                p[permutation.Length + i] = p[i] = permutation[i];
        }

        public static void Reseed()
        {
            var random = new Random();
            var perm = Enumerable.Range(0, permutation.Length).ToArray();

            for (var i = 0; i < perm.Length; i++)
            {
                var swapIndex = random.Next(perm.Length);

                var t = perm[i];

                perm[i] = perm[swapIndex];

                perm[swapIndex] = t;
            }

            permutation = perm;

            CalculateP();

        }

        public static float Noise(Vector3 position, int octaves, ref float min, ref float max)
        {
            return Noise(position.X, position.Y, position.Z, octaves, ref min, ref max);
        }

        public static float Noise(float x, float y, float z, int octaves, ref float min, ref float max)
        {

            var perlin = 0f;
            var octave = 1;

            for (var i = 0; i < octaves; i++)
            {
                var noise = Noise(x * octave, y * octave, z * octave);

                perlin += noise / octave;

                octave *= 2;
            }

            perlin = Math.Abs((float)Math.Pow(perlin,2));
            max = Math.Max(perlin, max);
            min = Math.Min(perlin, min);

            //perlin = 1f - 2 * perlin;

            return perlin;
        }

        public static float Noise(float x, float y, float z)
        {
            int X = (int)Math.Floor(x) % _halfLength;
            int Y = (int)Math.Floor(y) % _halfLength;
            int Z = (int)Math.Floor(z) % _halfLength;

            if (X < 0)
                X += _halfLength;

            if (Y < 0)
                Y += _halfLength;

            if (Z < 0)
                Z += _halfLength;

            x -= (int)Math.Floor(x);
            y -= (int)Math.Floor(y);
            z -= (int)Math.Floor(z);

            var u = Fade(x);
            var v = Fade(y);
            var w = Fade(z);

            int A = p[X] + Y, AA = p[A] + Z, AB = p[A + 1] + Z,      // HASH COORDINATES OF
                B = p[X + 1] + Y, BA = p[B] + Z, BB = p[B + 1] + Z;      // THE 8 CUBE CORNERS,


            return MathHelper.Lerp(
                    MathHelper.Lerp(
                         MathHelper.Lerp(
                            Grad(p[AA], x, y, z) // AND ADD
                            ,
                            Grad(p[BA], x - 1, y, z) // BLENDED
                            ,
                            u
                            )
                        ,
                        MathHelper.Lerp(
                            Grad(p[AB], x, y - 1, z)  // RESULTS
                            ,
                            Grad(p[BB], x - 1, y - 1, z)
                            ,
                            u
                            )
                        ,
                        v
                    )
                    ,
                    MathHelper.Lerp(
                        MathHelper.Lerp(
                            Grad(p[AA + 1], x, y, z - 1) // CORNERS
                            ,
                            Grad(p[BA + 1], x - 1, y, z - 1) // OF CUBE
                            ,
                            u
                            )
                        ,
                        MathHelper.Lerp(
                            Grad(p[AB + 1], x, y - 1, z - 1)
                            ,
                            Grad(p[BB + 1], x - 1, y - 1, z - 1)
                            ,
                            u
                            )
                        ,
                        v
                    )
                    ,
                    w
                );

        }

        static float Fade(float t) { return t * t * t * (t * (t * 6 - 15) + 10); }

        static float Grad(int hash, float x, float y, float z)
        {
            int h = hash & 15;                      // CONVERT LO 4 BITS OF HASH CODE

            float u = h < 8 ? x : y,                 // INTO 12 GRADIENT DIRECTIONS.
                   v = h < 4 ? y : h == 12 || h == 14 ? x : z;

            return ((h & 1) == 0 ? u : -u) + ((h & 2) == 0 ? v : -v);
        }

    }
}

Update

Okay, I managed to create a working 2D version. Here's the class:

/// implements improved Perlin noise in 2D. 
/// Transcribed from http://www.siafoo.net/snippet/144?nolinenos#perlin2003
/// </summary>
public static class Noise2d
{
    private static Random _random = new Random();
    private static int[] _permutation;

    private static Vector2[] _gradients;

    static Noise2d()
    {
        CalculatePermutation(out _permutation);
        CalculateGradients(out _gradients);
    }

    private static void CalculatePermutation(out int[] p)
    {
        p = Enumerable.Range(0, 256).ToArray();

        /// shuffle the array
        for (var i = 0; i < p.Length; i++)
        {
            var source = _random.Next(p.Length);

            var t = p[i];
            p[i] = p[source];
            p[source] = t;
        }
    }

    /// <summary>
    /// generate a new permutation.
    /// </summary>
    public static void Reseed()
    {
        CalculatePermutation(out _permutation);
    }

    private static void CalculateGradients(out Vector2[] grad)
    {
        grad = new Vector2[256];

        for (var i = 0; i < grad.Length; i++)
        {
            Vector2 gradient;

            do
            {
                gradient = new Vector2((float)(_random.NextDouble() * 2 - 1), (float)(_random.NextDouble() * 2 - 1));
            }
            while (gradient.LengthSquared() >= 1);

            gradient.Normalize();

            grad[i] = gradient;
        }

    }

    private static float Drop(float t)
    {
        t = Math.Abs(t);
        return 1f - t * t * t * (t * (t * 6 - 15) + 10);
    }

    private static float Q(float u, float v)
    {
        return Drop(u) * Drop(v);
    }

    public static float Noise(float x, float y)
    {
        var cell = new Vector2((float)Math.Floor(x), (float)Math.Floor(y));

        var total = 0f;

        var corners = new[] { new Vector2(0, 0), new Vector2(0, 1), new Vector2(1, 0), new Vector2(1, 1) };

        foreach (var n in corners)
        {
            var ij = cell + n;
            var uv = new Vector2(x - ij.X, y - ij.Y);

            var index = _permutation[(int)ij.X % _permutation.Length];
            index = _permutation[(index + (int)ij.Y) % _permutation.Length];

            var grad = _gradients[index % _gradients.Length];

            total += Q(uv.X, uv.Y) * Vector2.Dot(grad, uv);
        }

        return Math.Max(Math.Min(total, 1f), -1f);
    }

}

Call it like this:

private void GenerateNoiseMap(int width, int height, ref Texture2D noiseTexture, int octaves)
    {
        var data = new float[width * height];

        /// track min and max noise value. Used to normalize the result to the 0 to 1.0 range.
        var min = float.MaxValue;
        var max = float.MinValue;

        /// rebuild the permutation table to get a different noise pattern. 
        /// Leave this out if you want to play with changing the number of octaves while 
        /// maintaining the same overall pattern.
        Noise2d.Reseed();

        var frequency = 0.5f;
        var amplitude = 1f;
        var persistence = 0.25f;

        for (var octave = 0; octave < octaves; octave++)
        {
            /// parallel loop - easy and fast.
            Parallel.For(0
                , width * height
                , (offset) =>
                {
                    var i = offset % width;
                    var j = offset / width;
                    var noise = Noise2d.Noise(i*frequency*1f/width, j*frequency*1f/height);
                    noise = data[j * width + i] += noise * amplitude;

                    min = Math.Min(min, noise);
                    max = Math.Max(max, noise);

                }
            );

            frequency *= 2;
            amplitude /= 2;
        }


        if (noiseTexture != null && (noiseTexture.Width != width || noiseTexture.Height != height))
        {
            noiseTexture.Dispose();
            noiseTexture = null;
        }
        if (noiseTexture==null)
        {
            noiseTexture = new Texture2D(Device, width, height, false, SurfaceFormat.Color);
        }

        var colors = data.Select(
            (f) =>
            {
                var norm = (f - min) / (max - min);
                return new Color(norm, norm, norm, 1);
            }
        ).ToArray();

        noiseTexture.SetData(colors);
    }

Note that I've used a couple of XNA structures (Vector2 and Texture2D), but it should be pretty clear what they do.

If you want higher frequency (more "noisy") content with fewer octaves, increase the initial frequency value that used in the octave loop.

This implementation uses "improved" Perlin noise, which should be a bit faster than the standard version. You might also have a look at Simplex noise, which is quite a bit faster at higher dimensions.

赴月观长安 2024-12-30 08:15:38

我必须将其更改

            n00 = dot2D(g00 & 7, x, y),
            n10 = dot2D(g10 & 7, x, y),
            n01 = dot2D(g01 & 7, x, y),
            n11 = dot2D(g11 & 7, x, y);

            n00 = dot2D(g00 & 7, x    , y    ),
            n10 = dot2D(g10 & 7, x - 1, y    ),
            n01 = dot2D(g01 & 7, x    , y - 1),
            n11 = dot2D(g11 & 7, x - 1, y - 1);

:基本上只需在需要的地方从 x 和 y 中减去 1 即可。

I had to change this:

            n00 = dot2D(g00 & 7, x, y),
            n10 = dot2D(g10 & 7, x, y),
            n01 = dot2D(g01 & 7, x, y),
            n11 = dot2D(g11 & 7, x, y);

to this:

            n00 = dot2D(g00 & 7, x    , y    ),
            n10 = dot2D(g10 & 7, x - 1, y    ),
            n01 = dot2D(g01 & 7, x    , y - 1),
            n11 = dot2D(g11 & 7, x - 1, y - 1);

Basically just subtracting 1 from the x and y where needed.

Smile简单爱 2024-12-30 08:15:38

如果您将 z 的零值插入到 3D 方程中,然后简单地进行数学运算,删除项,您会发现最终得到一个更简单的方程。

不过,您的实现看起来与我正在使用的有点不同。

以下是我正在使用的 3D 和 2D 函数(在 JavaScript 中)的比较:

noise3d: function(x, y, z)
{
    // Find unit cube that contains point.
    var X = Math.floor(x) & 255,
        Y = Math.floor(y) & 255,
        Z = Math.floor(z) & 255;
    // Find relative x,y,z of point in cube.
    x -= Math.floor(x);
    y -= Math.floor(y);
    z -= Math.floor(z);
    // Compute fade curves for each of x,y,z.
    var u = fade(x),
        v = fade(y),
        w = fade(z);
    // Hash coordinates of the corners.
    var A = p[X    ] + Y, AA = p[A] + Z, AB = p[A + 1] + Z,
        B = p[X + 1] + Y, BA = p[B] + Z, BB = p[B + 1] + Z;

    // Add blended results from 8 corners of cube.
    return scale(
        lerp(
            w,
            lerp(
                v,
                lerp(
                    u,
                    grad(p[AA], x, y, z),
                    grad(p[BA], x - 1, y, z)
                ),
                lerp(
                    u,
                    grad(p[AB], x, y - 1, z),
                    grad(p[BB], x - 1, y - 1, z)
                )
            ),
            lerp(
                v,
                lerp(
                    u,
                    grad(p[AA + 1], x, y, z - 1),
                    grad(p[BA + 1], x - 1, y, z - 1)
                ),
                lerp(
                    u,
                    grad(p[AB + 1], x, y - 1, z - 1),
                    grad(p[BB + 1], x - 1, y - 1, z - 1)
                )
            )
        )
    );
}

2D 版本涉及较少的计算。

noise2d: function(x, y)
{
    // Find unit square that contains point.
    var X = Math.floor(x) & 255,
        Y = Math.floor(y) & 255;
    // Find relative x,y of point in square.
    x -= Math.floor(x);
    y -= Math.floor(y);
    // Compute fade curves for each of x,y.
    var u = fade(x),
        v = fade(y);
    // Hash coordinates of the corners.
    var A = p[X    ] + Y, AA = p[A], AB = p[A + 1],
        B = p[X + 1] + Y, BA = p[B], BB = p[B + 1];

    // Add blended results from the corners.
    return scale(
            lerp(
                v,
                lerp(
                    u,
                    grad(p[AA], x, y, 0),
                    grad(p[BA], x - 1, y, 0)
                ),
                lerp(
                    u,
                    grad(p[AB], x, y - 1, 0),
                    grad(p[BB], x - 1, y - 1, 0)
                )
            )
    );
}

If you plug in a zero value for z into your 3D equation and simply follow the math through, removing terms, you'll see that you end up with a simpler equation in the end.

Your implementation looks kind of different to the one I'm using though.

Here's a comparison of a 3D and 2D function I'm using (in JavaScript):

noise3d: function(x, y, z)
{
    // Find unit cube that contains point.
    var X = Math.floor(x) & 255,
        Y = Math.floor(y) & 255,
        Z = Math.floor(z) & 255;
    // Find relative x,y,z of point in cube.
    x -= Math.floor(x);
    y -= Math.floor(y);
    z -= Math.floor(z);
    // Compute fade curves for each of x,y,z.
    var u = fade(x),
        v = fade(y),
        w = fade(z);
    // Hash coordinates of the corners.
    var A = p[X    ] + Y, AA = p[A] + Z, AB = p[A + 1] + Z,
        B = p[X + 1] + Y, BA = p[B] + Z, BB = p[B + 1] + Z;

    // Add blended results from 8 corners of cube.
    return scale(
        lerp(
            w,
            lerp(
                v,
                lerp(
                    u,
                    grad(p[AA], x, y, z),
                    grad(p[BA], x - 1, y, z)
                ),
                lerp(
                    u,
                    grad(p[AB], x, y - 1, z),
                    grad(p[BB], x - 1, y - 1, z)
                )
            ),
            lerp(
                v,
                lerp(
                    u,
                    grad(p[AA + 1], x, y, z - 1),
                    grad(p[BA + 1], x - 1, y, z - 1)
                ),
                lerp(
                    u,
                    grad(p[AB + 1], x, y - 1, z - 1),
                    grad(p[BB + 1], x - 1, y - 1, z - 1)
                )
            )
        )
    );
}

The 2D version involves fewer computations.

noise2d: function(x, y)
{
    // Find unit square that contains point.
    var X = Math.floor(x) & 255,
        Y = Math.floor(y) & 255;
    // Find relative x,y of point in square.
    x -= Math.floor(x);
    y -= Math.floor(y);
    // Compute fade curves for each of x,y.
    var u = fade(x),
        v = fade(y);
    // Hash coordinates of the corners.
    var A = p[X    ] + Y, AA = p[A], AB = p[A + 1],
        B = p[X + 1] + Y, BA = p[B], BB = p[B + 1];

    // Add blended results from the corners.
    return scale(
            lerp(
                v,
                lerp(
                    u,
                    grad(p[AA], x, y, 0),
                    grad(p[BA], x - 1, y, 0)
                ),
                lerp(
                    u,
                    grad(p[AB], x, y - 1, 0),
                    grad(p[BB], x - 1, y - 1, 0)
                )
            )
    );
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文