Java 柏林噪声的输出范围

Java 柏林噪声的输出范围,java,perlin-noise,Java,Perlin Noise,我正在研究一些相干噪声的各种实现(我知道有库,但这主要是为了我自己的启发和好奇心)以及如何使用它,我对原始的柏林噪声有一个问题 根据,输出范围将介于-1和1之间,但我不明白该值如何在该范围内 据我所知,算法基本上是这样的:每个网格点都有一个长度1的相关随机梯度向量。然后,对于每个点,对于所有四个周围的网格点,计算随机梯度和从该网格点出发的向量的点积。然后,使用一条奇特的缓和曲线和线性插值将其降到一个值 但是,我的问题是:这些点积偶尔会超出范围[-1,1],既然你最终在点积之间进行线性插值,那不意

我正在研究一些相干噪声的各种实现(我知道有库,但这主要是为了我自己的启发和好奇心)以及如何使用它,我对原始的柏林噪声有一个问题

根据,输出范围将介于
-1
1
之间,但我不明白该值如何在该范围内

据我所知,算法基本上是这样的:每个网格点都有一个长度
1
的相关随机梯度向量。然后,对于每个点,对于所有四个周围的网格点,计算随机梯度和从该网格点出发的向量的点积。然后,使用一条奇特的缓和曲线和线性插值将其降到一个值

但是,我的问题是:这些点积偶尔会超出范围
[-1,1]
,既然你最终在点积之间进行线性插值,那不意味着最终值有时会超出范围
[-1,1]

例如,假设其中一个随机向量是
(sqrt(2)/2,sqrt(2)/2)
(长度为1)和
(0.8,0.8)
(单位为平方),则得到的结果大致为
1.131
。如果在线性插值中使用该值,则生成的值完全可能大于
1
。事实上,在我的直截了当的实现中,这种情况经常发生

我是不是遗漏了什么

作为参考,这里是我的Java代码
Vec
是一个简单的类,用于执行简单的2d向量算法,
fade()
是缓和曲线,
lerp()
是线性插值,
gradient(x,y)
将网格点的梯度作为
Vec
提供。
gridSize
变量提供网格的大小(以像素为单位)(其类型为double):

编辑:以下是生成随机渐变的代码:

double theta = gen.nextDouble() * 2 * Math.PI; 
gradients[i] = new Vec(Math.cos(theta), Math.sin(theta));
其中
gen
java.util.Random

你有
y0=(int)d.x,但你的意思是
d.y
。这无疑会影响您的输出范围,这也是您看到如此大量超出范围值的原因


也就是说,柏林噪声的输出范围实际上不是[-1,1]。虽然我自己对数学不是很确定(我一定是老了),但我计算出实际范围是[-sqrt(n)/2,sqrt(n)/2],其中n是维度(在你的例子中是2)。因此,2D柏林噪波函数的输出范围应为[-0.707,0.707]。这在某种程度上与以下事实有关:
d
和插值参数都是
p
的函数。如果你通读这些讨论,你可能会找到你想要的确切解释(特别是,)

我正在使用以下程序测试您的实现(我从您的示例中拼凑而成,请原谅
gridCells
gridSize
的奇怪用法):

import java.util.Random;
公共级柏林{
静态最终int gridSize=200;
静态最终整数网格单元=20;
静态最终Vec[][]梯度=新Vec[gridCells+1][gridCells+1];
静态无效初始化渐变(){
Random rand=新的Random();
对于(int r=0;rmaxz)
maxz=z;
}
}
System.out.println(minz+“”+maxz);
}
}
}
我看到的值在理论范围[-0.707,0.707]内,尽管我通常看到的值在-0.6和0.6之间;这可能是v的结果
double theta = gen.nextDouble() * 2 * Math.PI; 
gradients[i] = new Vec(Math.cos(theta), Math.sin(theta));
import java.util.Random;


public class Perlin {

    static final int gridSize = 200;
    static final int gridCells = 20;
    static final Vec[][] gradients = new Vec[gridCells + 1][gridCells + 1];

    static void initializeGradient () {
        Random rand = new Random();
        for (int r = 0; r < gridCells + 1; ++ r) {
            for (int c = 0; c < gridCells + 1; ++ c) {
                double theta = rand.nextFloat() * Math.PI;
                gradients[c][r] = new Vec(Math.cos(theta), Math.sin(theta));                
            }
        }
    }

    static class Vec {
        double x;
        double y;
        Vec (double x, double y) { this.x = x; this.y = y; }
        double dot (Vec v) { return x * v.x + y * v.y; }
        Vec sub (double x, double y) { return new Vec(this.x - x, this.y - y); }
    }

    static double fade (double v) {
        // easing doesn't matter for range sample test.
        // v = 3 * v * v - 2 * v * v * v;
        return v;
    }

    static double lerp (double p, double a, double b) {
        return (b - a) * p + a;
    }

    static Vec gradient (int c, int r) {
        return gradients[c][r];
    }

    // your function, with y0 fixed. note my gridSize is not a double like yours.     
    public static double getPoint(int x, int y) {

        Vec p = new Vec(x / (double)gridSize, y / (double)gridSize);
        Vec d = new Vec(Math.floor(p.x), Math.floor(p.y));

        int x0 = (int)d.x,
            y0 = (int)d.y;

        double d00 = gradient(x0    , y0    ).dot(p.sub(x0    , y0    )),
               d01 = gradient(x0    , y0 + 1).dot(p.sub(x0    , y0 + 1)),
               d10 = gradient(x0 + 1, y0    ).dot(p.sub(x0 + 1, y0    )),
               d11 = gradient(x0 + 1, y0 + 1).dot(p.sub(x0 + 1, y0 + 1));

        double fadeX = fade(p.x - d.x),
               fadeY = fade(p.y - d.y);

        double i1 = lerp(fadeX, d00, d10),
               i2 = lerp(fadeX, d01, d11);

        return lerp(fadeY, i1, i2);

    }

    public static void main (String[] args) {

        // loop forever, regenerating gradients and resampling for range. 
        while (true) {

            initializeGradient();

            double minz = 0, maxz = 0;

            for (int x = 0; x < gridSize * gridCells; ++ x) {
                for (int y = 0; y < gridSize * gridCells; ++ y) {
                    double z = getPoint(x, y);
                    if (z < minz)
                        minz = z;
                    else if (z > maxz)
                        maxz = z;
                }
            }

            System.out.println(minz + " " + maxz);

        }

    }

}