我正在尝试在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 ,一个初始化为默认值的二维2D数组,另一个名为 iterations 的变量。
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);
}
在本节中, center 中的每个点,指的是在北,南<的每个主要方向上都有空相邻单元格的单元格/ strong>, east 和 west ,从四个相邻的角点获得一个平均值,并带有随机位移值添加到它。这被称为钻石步骤。澄清“中心”是什么:
// 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
根据上述文章中给出的信息,变量 displacementBound 在上面的部分中减少,该部分包括while循环的结束。在开始另一次循环迭代或终止循环之前,通过将 B 的更新内容分配给 A 来更新 A 的内容。
最后,已包含辅助方法 averageFromCornersAndDisplace(), averageFromSidesAndDisplace()和 wrap(),但对它们的其他说明是不必要。根本没有包含 randomDisplacement()方法。为了您的信息,它返回由给定数字 b 限定的随机浮点数 x :
// The method returns a double x, where -b <= x < b
double randomDisplacement(double b);
答案 0 :(得分:0)
wrap()函数是罪魁祸首。当它们超出数组的边界时,它会包围索引,因此在边缘上,两个(通常是不同的)值被平均在一起。这导致了奇怪的不兼容性。 我删除了对 wrap()的所有调用,并选择在需要换行时平均三个相邻点而不是四个。
方法 wrap()旨在提供无缝拼接,但在这种情况下似乎导致了问题。而且平铺甚至看起来都不是无缝的。
答案 1 :(得分:0)
我刚刚看到你的帖子弹出,我想你已经把它整理出来了。无论如何,如果你想做这样的包装,有一个巧妙的技巧来解决负面mod在C / Java中不能正常工作的事实。你所做的只是将模数的多个(注意不要溢出)添加回数字,以确保它是非负数。然后你可以照常修改而不会破坏它。这是一个例子:
private int wrap(int n, int lowerBound, int upperBound) {
int lengthOfInterval = upperBound - lowerBound;
return lowerBound + ((n - lowerBound + lengthOfInterval) % lengthOfInterval);
}