使菱形方形分形算法无限

时间:2011-02-12 12:03:43

标签: python algorithm infinite terrain

我正在尝试生成无限地图。我在Python中这样做,我无法让噪声库正常工作(他们似乎没有找到我的VS2010,而在原始Python中执行它会太慢)。因此,我正在尝试使用Diamond-Square Algorithm

在某种程度上,是否有可能使技术无限?

如果没有,我应该回去尝试让其中一个Python噪声绑定工作吗?

4 个答案:

答案 0 :(得分:1)

这可以做到。将您的景观划分为“图块”,并在每个图块中应用中点位移算法(我更喜欢称之为)。

每个瓷砖必须足够大,以使一个角落的高度不会明显取决于另一个角落的高度。通过这种方式,您可以动态创建和销毁切片,将新出现的角落的高度设置为独立的随机值。

该值(以及图块中的随机性)必须从图块的位置播种,以便每次都获得相同的图块。

答案 1 :(得分:1)

最新版本的噪音模块(http://pypi.python.org/pypi/noise/1.0b3)提到它“修复了在Windows上使用Visual C ++编译的问题” - 您可能想再试一次。它安装并适用于我(Python 2.7.1,MinGW,Windows 7)。

如果你在一个x,y网格中安排你的图块并为每个图块播种随机数生成器,如random.seed((x,y)),那么每次你返回相同的世界时它将重新创造相同的地形。

使用菱形算法,每个图块的两边取决于相邻的图块;然而,依赖性不会一直传播到瓷砖上。如果让每个tile依赖于它前面的tile(即上方和左侧)并编写一个create_fake_tile函数,该函数假设所有前面的tile值都是0,那么你会得到一个“bad”tile,其中右列和底部row是下一个tile所依赖的正确值。如果您从屏幕上第一行和第一列之前的图块行和列开始绘制每个屏幕,那么您的世界的可见部分将是正确的。

答案 2 :(得分:1)

我解决了Diamond Squared算法的无限景观。并且实际上发现了这个问题做我的尽职调查。是。虽然这里提供的答案大多是错误的,但完全可以做到。从概念上讲,您需要做的是将算法的每次迭代视为无限字段,将基本情况视为完全随机数的无限字段。所以每次迭代都是前一次的菱形平方版本。

为了在迭代7中解决一系列的瓷砖[100-200] [100-200],你需要的是整个块的一半大小(大小为h * w的四分之一)在迭代6加一每边额外的边缘。因此,如果您有一个解决给定图块的函数:

getTerrain(x0,y0,x1,y1,iteration)...如果您在迭代6中有[49,101] [49,101],则可以为该图块解决此问题。然后您只需在任何地方应用菱形平方,即赢得&# 39;在边缘工作,但在其他任何地方工作,并为你提供足够的数据来解决迭代7中的图块[100-200] [100-200]。

要在迭代6获得该瓦片,它将从迭代5请求[23-52] [23-52]。这将继续直到迭代0只给你[-1,3] [ - 1,3]将是16个完全随机的值。如果你要求的瓷砖如此之大,以至于迭代0还没有达到那个尺寸和位置,那么你只需要一个不同的条子就可以了,因为你无论如何只是在制作它们。

通过这种方式,您可以完全摆脱播种步骤,简化算法,使其无限,使其占用合理的内存。使用从坐标中散列的确定性伪随机数,它将允许您动态生成和重新生成相同的图块,因为您实际上生成了较低的较低迭代,并且只是以集中的方式执行此操作。

我的博客有更多相关信息, http://godsnotwheregodsnot.blogspot.com/2013/11/field-diamond-squared-fractal-terrain.html

实际上,算法的大多数复杂性来自于在循环中具有基本情况四个点,因为这不是最接近最简单的情况。它显然逃脱了大多数人,你甚至可以通过在循环中做1点来简化它(尽管这仍然是毫无意义的复杂)。或任何使算法似乎至少有4x4点的东西。您甚至可以以4x4点迭代开始一次,删除具有未知值(所有边缘)的任何行或列,然后具有5x5块,然后是7x7块然后是11x11块...(2n-3)x(2n) -3)。

简而言之,如果你的基本情况有一个无限字段,你实际上可以有一个无限字段,迭代只是确定你的东西是如何混合的。如果你对伪随机注入的偏移使用确定性的东西,你几乎有一个非常快速的无限景观生成器。

在这里用javascript演示它, http://tatarize.nfshost.com/FieldDiamondSquare.htm

这是javascript中的一个实现。它只是为您提供该迭代​​,位置和大小的正确字段的视图。上面链接的示例在可移动画布中使用此代码。它不存储任何数据并重新计算每个帧。

function diamondSquaredMap(x, y, width, height, iterations) {
    var map = fieldDiamondSquared(x, y, x+width, y+height, iterations);

    var maxdeviation = getMaxDeviation(iterations);

    for (var j = 0; j < width; j++) {
        for (var k = 0; k < height; k++) {
            map[j][k] = map[j][k] / maxdeviation;
        }
    }
    return map;

    function create2DArray(d1, d2) {
        var x = new Array(d1),
                i = 0,
                j = 0;

        for (i = 0; i < d1; i += 1) {
            x[i] = new Array(d2);
        }
        return x;
    }

    function fieldDiamondSquared(x0, y0, x1, y1, iterations) {
        if (x1 < x0) { return null; }
        if (y1 < y0) { return null; }
        var finalwidth  = x1 - x0;
        var finalheight = y1 - y0;
        var finalmap = create2DArray(finalwidth, finalheight);
        if (iterations === 0) {
            for (var j = 0; j < finalwidth; j++) {
                for (var k = 0; k < finalheight; k++) {
                    finalmap[j][k] =  displace(iterations,x0+j,y0+k) ;
                }
            }
            return finalmap;
        }
        var ux0 = Math.floor(x0 / 2) - 1;
        var uy0 = Math.floor(y0 / 2) - 1;
        var ux1 = Math.ceil(x1 / 2) + 1;
        var uy1 = Math.ceil(y1 / 2) + 1;
        var uppermap = fieldDiamondSquared(ux0, uy0, ux1, uy1, iterations-1);

        var uw = ux1 - ux0;
        var uh = uy1 - uy0;

        var cx0 = ux0 * 2;
        var cy0 = uy0 * 2;

        var cw = uw*2-1;
        var ch = uh*2-1;
        var currentmap = create2DArray(cw,ch);

        for (var j = 0; j < uw; j++) {
            for (var k = 0; k < uh; k++) {
                currentmap[j*2][k*2] = uppermap[j][k];
            }
        }
        var xoff = x0 - cx0;
        var yoff = y0 - cy0;
        for (var j = 1; j < cw-1; j += 2) {
            for (var k = 1; k < ch-1; k += 2) {
                currentmap[j][k] = ((currentmap[j - 1][k - 1] + currentmap[j - 1][k + 1] + currentmap[j + 1][k - 1] + currentmap[j + 1][k + 1]) / 4) + displace(iterations,cx0+j,cy0+k);
            }
        }
        for (var j = 1; j < cw-1; j += 2) {
            for (var k = 2; k < ch-1; k += 2) {
                currentmap[j][k] = ((currentmap[j - 1][k]     + currentmap[j + 1][k]     + currentmap[j][k - 1]     + currentmap[j][k + 1]) / 4) + displace(iterations,cx0+j,cy0+k);
            }
        }
        for (var j = 2; j < cw-1; j += 2) {
            for (var k = 1; k < ch-1; k += 2) {
                currentmap[j][k] = ((currentmap[j - 1][k]     + currentmap[j + 1][k]     + currentmap[j][k - 1]     + currentmap[j][k + 1]) / 4) + displace(iterations,cx0+j,cy0+k);
            }
        }

        for (var j = 0; j < finalwidth; j++) {
            for (var k = 0; k < finalheight; k++) {
                finalmap[j][k] = currentmap[j+xoff][k+yoff];
            }
        }

        return finalmap;
    }

    // Random function to offset
    function displace(iterations, x, y) {
        return (((PRH(iterations,x,y) - 0.5)*2)) / (iterations+1);
    }

    function getMaxDeviation(iterations) {
        var dev = 0.5 / (iterations+1);
        if (iterations <= 0) return dev;
        return getMaxDeviation(iterations-1) + dev;
    }

    //This function returns the same result for given values but should be somewhat random.
    function PRH(iterations,x,y) {
        var hash;
        x &= 0xFFF;
        y &= 0xFFF;
        iterations &= 0xFF;
        hash = (iterations << 24);
        hash |= (y << 12);
        hash |= x;
        var rem = hash & 3;
        var h = hash;

        switch (rem) {
            case 3:
                hash += h;
                hash ^= hash << 32;
                hash ^= h << 36;
                hash += hash >> 22;
                break;
            case 2:
                hash += h;
                hash ^= hash << 22;
                hash += hash >> 34;
                break;
            case 1:
                hash += h;
                hash ^= hash << 20;
                hash += hash >> 2;
        }
        hash ^= hash << 6;
        hash += hash >> 10;
        hash ^= hash << 8;
        hash += hash >> 34;
        hash ^= hash << 50;
        hash += hash >> 12;

        return (hash & 0xFFFF) / 0xFFFF;
    }

};

更新后,我继续这样做,并根据相同的范围字段算法制作了我自己的噪声算法,这里是Jsfiddle。

https://jsfiddle.net/rkdzau7o/

您可以点击并拖动周围的噪音。

答案 3 :(得分:0)

您可以自下而上应用中点位移吗?假设您正在处理离散网格,并且想要生成MxN区域。给自己一个加权函数w(i)。首先将权重为w(0)的随机位移应用于每个点。然后将权重w(1)的随机位移应用于每个其他点,并将位移传播到中间点。然后用w(2)做每第四个点,再次传播。您现在可以生成任意大小的网格,而无需对大小进行任何初始假设。

如果有一些N(w> i> N)= 0,那么当你点击它时你就可以停止生成 - 如果你希望这一代能够终止,这一点非常重要!你可能想要一个从1开始的w函数,增加到某个值,然后下降到零;增加的位模拟地形的幂律粗糙度达到100公里左右的尺度,而减少的位模拟整个行星或多或少是球形的事实。如果你在做火星而不是地球,你会希望w(i)更进一步,因为Tharsis凸起。

现在用随机查找但确定性的点坐标函数替换随机函数(例如,将坐标提供给SHA1哈希)。现在,每当你生成网格的某个特定部分时,它看起来都是一样的。

你应该能够以与此相同的方式做钻石广场;你只需要改变位移的形状。