中点位移2D算法产生不寻常的模式

时间:2014-11-12 01:19:03

标签: algorithm haxe terrain fractals procedural-generation

我使用Haxe的中点位移算法遇到了困难。我按照here找到的步骤实现了这一点。

  

首先,创建一个表示空白地图的数组。首先,给四个角一个随机值。

     

在此方块中,通过平均四个角并添加一个小“错误”或随机值来创建中间点。然后通过平均每个中间的两个角来创建4个边的中点。完成这些步骤后,您将获得4个方格。重复以下步骤:

     
      
  1. 通过对四个角进行平均并添加一个小“错误”来创建中间点。

  2.   
  3. 通过对每个点之间的两个角进行平均来创建每一边的中点。

  4.         

    每次迭代,使RNG的范围变小。这样原来的几个点可以有很大的变化,但后面的点只能进行微小的调整。这样可以确保图像中的细节数量。

这是我编写的用于执行这些步骤然后规范化值的函数:

public static function generateFloatMatrix(Columns:Int, Rows:Int, RangeModifier:Float = 0.65):Array<Array<Float>>
{
    //Blank 2D Array
    var matrix:Array<Array<Float>> = InitFloatMatrix(Columns, Rows);
    var range:Float = 1;

    //Set Values for all four corners
    matrix[0][0] = Math.random() * range;
    matrix[Rows-1][0] = Math.random() * range;
    matrix[0][Columns-1] = Math.random() * range;
    matrix[Rows - 1][Columns - 1] = Math.random() * range;

    //Calculates the amount of segments in base 2
    var length = Math.sqrt((Columns * Columns) + (Rows * Rows));
    var power:Int = Std.int(Math.pow(2, Math.ceil(Math.log(length) / Math.log(2))));

    //Stores largest calculated value for normalization
    var max:Float = 0;

    var width:Int = Std.int(Columns);
    var height:Int = Std.int(Rows);

    var i:Int = 1;
    while (i < power)
    {
        //Segment Size
        width = Std.int(Columns / i);
        height = Std.int(Rows / i);

        for (y in 0...i)
        {
            for (x in 0...i)
            {
                //Top Left Coordinates per segment
                var left = width * x;
                var top = height * y;

                //Find Midpoint
                var xMid = Math.ceil(left + (width / 2));
                var yMid = Math.ceil(top + (height / 2));

                //Make sure right and bottom do not go out of bounds
                var right:Int = (left + width < Columns ? left + width : Columns - 1);
                var bottom:Int = (top + height < Rows ? top + height : Rows - 1);

                //Sets midpoint value to average of all four corners.
                matrix[yMid][xMid] = 
                    (matrix[top][left] + 
                        matrix[bottom][left] + 
                        matrix[bottom][right] + 
                        matrix[top][right]) / 4;

                //trace ("Top: " + top + " - Left: " + left + " - Bottom: " + bottom + " - Right: " + right);

                //Adds random value to midpoint
                matrix[yMid][xMid] += Math.random() * range;

                //Set side values to average of adjacent corners
                matrix[top][xMid] = (matrix[top][left] + matrix[top][right]) / 2;
                matrix[bottom][xMid] = (matrix[bottom][left] + matrix[bottom][right]) / 2;
                matrix[yMid][left] = (matrix[top][left] + matrix[bottom][left]) / 2;
                matrix[yMid][right] = (matrix[top][right] + matrix[bottom][right]) / 2;

                max = Math.max(matrix[top][left], max);
            }
        }

        //Reduces range
        range *= RangeModifier;
        i *= 2;
    }

    //Normalizes all values in matrix
    for (y in 0...Rows)
    {
        for (x in 0...Columns)
        {
            matrix[y][x] /= max;
        }
    }

    return matrix;
}

如果我使用每个值将每个像素渲染到指定的坐标,则会生成这些图像。所有呈现为白色的像素的值都为0,黑色的值为1。

Rendered as 8x8 pixels

Rendered as 1x1 pixels

1 个答案:

答案 0 :(得分:1)

您的问题是,如果您的地图尺寸不是2的幂,那么您不必通过计算来点击已填充的像素。例如,如果您的地图宽度为30个单位,则第一遍中的网格宽度为15,第二遍中的网格宽度为7,其中计算基于尚未触及的单位14。

解决方案是使用浮点运算进行所有计算,直到确定单位索引,当然必须是整数:

while (i < power)
{
    var width:Float = Columns / i;    // Floating-point division
    var height:Float = Rows / i;

    for (y in 0...i)
    {
        for (x in 0...i)
        {
            var left:Int = Math.floor(width * x);
            var top:Int = Math.floor(height * y);

            var xMid:Int = Math.floor(width * (x + 0.5));
            var yMid:Int = Math.floor(height * (y + 0.5));

            var right:Int = Math.floor(width * (x +1));
            var bottom:Int = Math.floor(height * (y + 1));

            //Make sure right and bottom do not go out of bounds
            if (right > Columns - 1) right = Columns - 1;
            if (bottom > Rows - 1) bottom = Rows - 1;

            // Do offset and interpolation stuff
        }
    }
}

这应该给你一个随机的地图,图纸效果和所有。

(警告:我对Haxe并不熟悉,但已经在Javascript中对此进行了测试,其中没有整数类型。我已经使用了Math-floor,你在哪里&# 39;我想以Haxe的方式去做。)

最后,我认为你做了太多的传球。我将功率基于两个维度的最大值而不是对角线。您也可以跳过wthe width接近1的最后一步。