简体   繁体   English

中点位移二维算法产生异常图案

[英]Midpoint Displacement 2D algorithm producing unusual patterns

I'm having difficulties with the Midpoint Displacement Algorithm using Haxe. 我在使用Haxe的中点位移算法上遇到困难。 I am implementing this by following the steps found here . 我正在按照此处找到的步骤实施此操作。

First, create an array that represents a blank map. 首先,创建一个表示空白地图的数组。 You begin by giving the four corners a random value. 首先给四个角一个随机值。

In this square, create the middle point by averaging the four corners and adding a small 'error', or random value. 在这个正方形中,通过平均四个角并添加一个小的“错误”或随机值来创建中间点。 Then create the midpoints of the 4 sides by averaging the two corners each is between. 然后,通过平均两个边之间的两个角来创建4个边的中点。 After these steps, you are left with 4 squares. 完成这些步骤后,您剩下4个正方形。 Repeat the steps: 重复步骤:

  1. Create the middle point by averaging the four corners and adding a small 'error'. 通过平均四个角并添加一个小的“错误”来创建中间点。

  2. Create the midpoint of each side by averaging the two corners each point is between. 通过平均每个点之间的两个角来创建每个边的中点。

Each iteration, make the range of the RNG smaller. 每次迭代,使RNG的范围变小。 That way the original few points can have pretty large variation, but the later points only get tiny adjustments. 这样,最初的几个点可能会有相当大的变化,但后来的点只能进行很小的调整。 This ensures the right amount of detail in an image. 这样可以确保图像中适当的细节量。

Here is the function I've written to perform these steps and then normalize the values: 这是我编写的用于执行这些步骤然后将值归一化的函数:

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;
}

These are the images it is producing if I use each value to render each pixel to the specified coordinate. 如果我使用每个值将每个像素渲染到指定的坐标,则它们是正在生成的图像。 All the pixels that are rendered white have the value 0, black is value 1. 所有渲染为白色的像素的值为0,黑色为值1。

渲染为8x8像素

渲染为1x1像素

Your problem is that you don't necessarily hit the already populated pixels with your calculations if your map dimensions are not a power of two. 您的问题是,如果地图尺寸不是2的幂,则计算时不一定会打中已经填充的像素。 For example if your map is 30 units wide, your grid width is 15 in the first pass and 7 in the second pass, where it bases its calculations on the yet untouched unit 14. 例如,如果地图的宽度为30个单位,则第一遍的网格宽度为15,第二遍的网格宽度为7,在此基础上,计算仍以未触及的单位14为基础。

A solution is to do all calculations with floating-point arithmetic until you determine the unit indices, which must of course be integer: 一种解决方案是使用浮点算术进行所有计算,直到确定单位索引为止,该索引当然必须是整数:

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
        }
    }
}

This should give you a random map, graph-paper effect and all. 这应该为您提供随机贴图,方格纸效果等。

(Caveat: I'm not familiar with Haxe, but have tested this in Javascript, which doesn't have an integer type. I've used Math-floor throughout, where you'll want to do it the Haxe way.) (注意:我不熟悉Haxe,但是已经在Javascript中进行了测试,而Javascript中没有整数类型。我一直在使用Math-floor ,在这里您将以Haxe的方式进行操作。)

Finally, it looks to me that you do too many passes. 最后,在我看来,您执行了太多遍。 I'd base the power on the maximum of the two dimensions instead of the diagonal. 我将功率基于二维的最大值而不是对角线。 You can also skip the last step where wthe width is near one. 您也可以跳过宽度接近1的最后一步。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM