我正在尝试在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);
}