2

我最近一直在玩双三次插值,因为我想根据 Minecraft 中的真实高度图生成地球。我使用插值的原因是因为我想让世界有更多的细节。经过大量的研究和大量的试验和错误,我决定来这里问。:)

由于内存有限,我无法在启动时缩放图像并保持加载,我必须动态进行插值。

我似乎已经让三次插值工作,如下所示: 插值的可视化 但是,我无法让双三次插值工作。出于测试目的,我使用了一个小图像,并将其缩放 4。这就是代码的作用:输入 -> 输出

这是我当前的代码:

 public static double cubicInterpolate(double[] points, double x, double scale)
{
    x /= scale;

    double inBetweenPoint = x;
    int xInHeightmap = (int) x;
    inBetweenPoint -= xInHeightmap;

    double beforePoint1 = safe(points, xInHeightmap - 1);
    double point1 = safe(points, xInHeightmap);
    double point2 = safe(points, xInHeightmap + 1);
    double afterPoint2 = safe(points, xInHeightmap + 2);

    double p = (afterPoint2 - point2) - (beforePoint1 - point1);
    double q = (beforePoint1 - point1) - p;
    double r = point2 - beforePoint1;
    double s = point1;

    return (p * Math.pow(inBetweenPoint, 3)) + (q * Math.pow(inBetweenPoint, 2)) + (r * inBetweenPoint) + s;
}

public static double bicubicInterpolate(double[][] points, double x, double y, double scale)
{
    x /= scale;

    double inBetweenPoint = x;
    int xInHeightmap = (int) x;
    inBetweenPoint -= xInHeightmap;

    double beforePoint1 = cubicInterpolate(safe(points, xInHeightmap - 1), y, scale);
    double point1 = cubicInterpolate(safe(points, xInHeightmap), y, scale);
    double point2 = cubicInterpolate(safe(points, xInHeightmap + 1), y, scale);
    double afterPoint2 = cubicInterpolate(safe(points, xInHeightmap + 2), y, scale);

    return cubicInterpolate(new double[]{beforePoint1, point1, point2, afterPoint2}, inBetweenPoint + 1, scale);
}

public static double[] safe(double[][] p, int i)
{
    return p[Math.max(0, Math.min(i, p.length - 1))];
}

public static double safe(double[] p, int i)
{
    return p[Math.max(0, Math.min(i, p.length - 1))];
}

谢谢您的帮助 :)

4

2 回答 2

3

据我了解,您的实施以完全不同的方式处理给定xy坐标,这不会导致预期的结果。您基本上应该执行以下操作。

首先,您需要确定网格中构成插值基础的四个点(坐标对),以及从网格到插值的两个方向的距离。这可以如下进行。

int xfloor = (int)x;
int yfloor = (int)y;
int xdelta = x - (double)xfloor;
int ydelta = y - (double)yfloor;

然后是所需的坐标对(取决于轴的方向)

P1 = (xfloor,     yfloor    ) // left upper corner
P2 = (xfloor,     yfloor + 1) // left lower corner
P3 = (xfloor + 1 ,yfloor + 1) // right lower corner
P4 = (xfloor + 1, yfloor    ) // left upper corner

最后,您将首先沿平行轴进行插值,然后在中间进行插值,这可以使用中间值按如下方式完成。

val1 = cubic(value(P1), value(P2), deltay) // interpolate cubically on the left edge
val2 = cubic(value(P4), value(P3), deltay) // interpolate cubically on the right edge
val = cubic (val1, val2, deltax) // interpolate cubically between the intermediates

插值方法也在这里讨论。

于 2015-12-01T12:28:16.933 回答
0

在您的双三次插值方法中,您编写:

//double inBetweenPoint = x;
//int xInHeightmap = (int) x;
//inBetweenPoint -= xInHeightmap;

return cubicInterpolate(new double[]{beforePoint1, point1, point2, afterPoint2}, inBetweenPoint, scale);

正如您很容易看到的那样,inBetweenPoint将在[0, 1)调用的区间内cubicInterpolate。这意味着插值将在 beforePoint1 和 point1 之间。不是在point1和point2之间。

简单的解决方法是写

return cubicInterpolate(new double[]{beforePoint1, point1, point2, afterPoint2}, inBetweenPoint + 1, scale);
于 2015-12-01T13:07:03.203 回答