我正在尝试在Java中实现中点位移算法。也称为菱形平方算法。我的参考是http://www.lighthouse3d.com/opengl/terrain/index.php3?mpd。除了右侧和底部边缘,它似乎正常工作。

See Midpoint Displacement Results

经过仔细检查,可以看到“粗糙”边缘。任何人都可以指出出什么问题了吗?
在此算法的其他在线实现中尚未观察到这种效果。



private void generateWorldMPD() {
    /* The following is my first attempt at the MDP algorithm. */

    // displacement boundary.
    double displacementBound = Constants.DEFAULT_ROUGHNESS_CONSTANT;
    double[][] A = Utilities.get2DDoubleArray(Constants.MPD_PRESET_HEIGHT, 2, 2);
    int iterations =0;
    while (iterations < mPDIterations) {

        // create a new array large enough for the new points being added.
        double [][] B = new double[A.length * 2 - 1][A[0].length * 2 - 1];

        // move the points in A to B, skipping every other element as space for a new point
        for (int i = 0; i < B.length; i +=2)
            for (int j = 0; j < B[i].length; j+=2) {
                B[i][j] = A[i / 2][j / 2];
            }

        //calculate the height of each new center point as the average of the four adjacent elements
        //(diamond step) and add a random displacement to each
        for (int i = 1; i < B.length; i+= 2)
            for (int j = 1; j < B[i].length; j+=2)  {
                averageFromCornersAndDisplace(B, i, j, displacementBound);

            }

        //calculate the height of each new non-center point (square step) and add a random displacement to each
        for (int i = 0; i < B.length; i ++)
            for (int j = 0; j < B[i].length; j++)
                if (i % 2 == 0)         //on every even row, calculate for only odd columns
                    if (j % 2 == 0) continue;
                    else
                        averageFromAdjAndDisplace( B , i, j, displacementBound );

            else                                //on every odd row, calculate for only even columns
                    if (j % 2 == 0)
                        averageFromAdjAndDisplace( B , i, j, displacementBound );
                    else
                        continue;

        displacementBound *= Math.pow(2, -Constants.DEFAULT_ROUGHNESS_CONSTANT);

        // assign B to A
        A = B;

        iterations++;
    }
}

private void averageFromCornersAndDisplace(double[][] A, int i, int j, double displacementBoundary) {
    double nw = A[ wrap(i - 1, 0, A.length - 1) ][ wrap(j - 1, 0, A[i].length - 1) ];
    double ne = A[ wrap(i + 1, 0, A.length - 1) ][ wrap(j - 1, 0, A[i].length - 1) ];
    double sw = A[ wrap(i - 1, 0, A.length - 1) ][ wrap(j + 1, 0, A[i].length - 1) ];
    double se = A[ wrap(i + 1, 0, A.length - 1) ][ wrap(j + 1, 0, A[i].length - 1) ];
    A[i][j] = (nw + ne + sw + se) / 4;
    A[i][j] += randomDisplacement(displacementBoundary);
}

private void averageFromAdjAndDisplace(double[][] A, int i, int j, double displacementBoundary) {
    double north = A[i][ wrap(j - 1, 0, A[i].length - 1)];
    double south = A[i][ wrap(j + 1, 0, A[i].length - 1)];
    double west  = A[ wrap(i - 1, 0, A.length - 1) ][j];
    double east  = A[ wrap(i + 1, 0, A.length - 1) ][j];
    A[i][j] = (north + south + east + west) / 4;
    A[i][j] += randomDisplacement(displacementBoundary);
}

// This function returns a value that is wrapped around the interval if
// it exceeds the given bounds in the negative or positive direction.
private int wrap(int n, int lowerBound, int upperBound) {

    int lengthOfInterval = upperBound - lowerBound;

    if (n < lowerBound)
        return (lowerBound - n) % lengthOfInterval;
    else
        return (n - upperBound) % lengthOfInterval;
}


注解

private void generateWorldMPD() {
    /* The following is my first attempt at the MDP algorithm. */

    // displacement boundary.
    double displacementBound = Constants.DEFAULT_ROUGHNESS_CONSTANT;
    double[][] A = Utilities.get2DDoubleArray(Constants.MPD_PRESET_HEIGHT, 2, 2);
    int iterations =0;


这部分定义一个变量displacementBound,一个二维双精度数组(已初始化为默认值)和另一个称为迭代的变量。

while (iterations < mPDIterations) {

    // create a new array large enough for the new points being added.
    double [][] B = new double[A.length * 2 - 1][A[0].length * 2 - 1];

    // move the points in A to B, skipping every other element as space for a new point
    for (int i = 0; i < B.length; i +=2)
        for (int j = 0; j < B[i].length; j+=2) {
            B[i][j] = A[i / 2][j / 2];
        }


这部分是声明循环的地方。它将在mPDIterations循环中运行。创建了临时数组B来容纳A的更新版本,使B大于A来容纳新数据点。之后,有两个for循环,一个循环嵌套在另一个循环中,该循环将A的当前值放入临时B中,请注意将每隔一行和每隔一列保留为空白。看一下这个例子:

// The '*'s represent a cell in an array that is populated with a value.
// The '_'s represent a cell in an array that is empty.

// This is 'A'.
* *
* *

// This is 'B'. At the moment, completely empty.
_ _ _
_ _ _
_ _ _

// The elements of 'A' are tranferred to 'B'.
// Blank cells are inserted in every other row, and every other column.
* _ *
_ _ _
* _ *


现在来看下一段代码:

        //calculate the height of each new center point as the average of the four adjacent elements
        //(diamond step) and add a random displacement to each
        for (int i = 1; i < B.length; i+= 2)
            for (int j = 1; j < B[i].length; j+=2)  {
                averageFromCornersAndDisplace(B, i, j, displacementBound);

            }


在本节中,中心的每个点(指的是在北,南,东和西的每个基本方向上都有一个空的相邻像元的像元)的值均取自四个相邻角点的平均值,并且具有随机位移值添加到其中。这称为菱形台阶。为了澄清“中心”是什么:

// The big "O" indicates the 'center' in this 2D array.
* _ *
_ O _
* _ *


接下来的代码部分:

//calculate the height of each new non-center point (square step) and add a random displacement to each
for (int i = 0; i < B.length; i ++)
    for (int j = 0; j < B[i].length; j++)
        if (i % 2 == 0)         //on every even row, calculate for only odd columns
            if (j % 2 == 0) continue;
            else
                averageFromAdjAndDisplace( B , i, j, displacementBound );

    else                                //on every odd row, calculate for only even columns
            if (j % 2 == 0)
                averageFromAdjAndDisplace( B , i, j, displacementBound );
            else
                continue;


这部分与上一节代码类似。它为每个非中心和空点分配一个新值;该值是基本方向北,南,东和西相邻元素的平均值,并加上了另一个随机位移值。这称为平方步。上面的代码确保只有非中心点和空点才被赋予新值。这些要点等同于要点,下面将进行说明:

// The big 'O's indicate the 'side points' in this 2D array.
* O *
O * O
* O *


以下是总结while循环的部分:

        displacementBound *= Math.pow(2, -Constants.DEFAULT_ROUGHNESS_CONSTANT);

        // assign B to A
        A = B;

        iterations++;
    } // end of while loop


根据上述文章中提供的信息,在上面的部分(包括while循环的末尾)中减小了变量distanceBound。在开始循环的另一次迭代或终止循环之前,通过将B的更新内容分配给A来更新A的内容。

最后,还包括了辅助方法averageFromCornersAndDisplace(),averageFromSidesAndDisplace()和wrap(),但是没有必要对它们进行附加说明。完全没有包含randomDisplacement()方法。供您参考,它返回一个以给定数字b为边界的随机浮点数x:

// The method returns a double x, where -b <= x < b
double randomDisplacement(double b);

最佳答案

我刚刚看到您的帖子弹出,我想您已经对它进行了整理。无论如何,如果您想进行这样的包装,则有一个巧妙的技巧可以解决否定mod在C / Java中无法正常工作的事实。您要做的只是将模数的倍数加倍(注意不要溢出),以确保该数为非负数。然后,您可以照常进行改装,而不会损坏。这是一个例子:

private int wrap(int n, int lowerBound, int upperBound) {
    int lengthOfInterval = upperBound - lowerBound;
    return lowerBound + ((n - lowerBound + lengthOfInterval) % lengthOfInterval);
}

10-06 03:19