中点位移地形伪影
我正在尝试用 Java 实现中点位移算法。它也称为菱形正方形算法。我的参考是http://www.lighthouse3d.com/opengl/terrain/index。 php3?mpd。除了右侧和底部边缘之外,它似乎工作正常。
仔细观察,可以看到“粗糙”的边缘。谁能指出哪里出了问题吗? 在该算法的其他在线实现中尚未观察到这种效果。
代码
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、一个初始化为默认值的二维双精度数组,以及另一个名为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);
}
在本节中,每个点都位于中心,它指的是在 的每个基本方向上都有一个空相邻单元格的单元格>北、南、东和西,给出四个相邻角的平均值 点并具有随机位移值添加到其中。这称为钻石步骤。澄清什么是“中心”:
// 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);
I am trying to implement the midpoint displacement algorithm in Java. It's also called the diamond square algorithm. My reference is http://www.lighthouse3d.com/opengl/terrain/index.php3?mpd. It seems to work correctly except on the right and bottom edges.
See Midpoint Displacement Results
Upon close inspection, the "rough" edges can be seen. Could anyone point out what is wrong?
This effect hasn't been observed in other online implementations of this algorithm.
Code
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;
}
Annotations
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;
This part defines a variable displacementBound, a 2D array of doubles initialized to default values, and another variable called 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];
}
This part is where the loop is declared. It will run for mPDIterations loops. A makeshift array B is created to hold an updated version of A, making B larger than A to hold new data points. After that there are two for loops, one nested inside another, which places the current values of A into the temporary B, taking care to leave every other row and every other column blank. Take a look at this example:
// 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.
* _ *
_ _ _
* _ *
Now for the next bit of code:
//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);
}
In this section, every point at a center, which refers to a cell that has an empty adjacent cell in every cardinal direction of north, south, east, and west, is given a value averaged from the four adjacent corner points and with a random displacement value added to it. This is called the diamond step. To clarify what a 'center' is:
// The big "O" indicates the 'center' in this 2D array.
* _ *
_ O _
* _ *
And the next code section:
//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;
This part does is analogous to the previous section of code. It assigns to each non-center and empty point a new value; this value is the average of the adjacent elements in the cardinal directions north, south, east, and west, with another random displacement value added to it. This is called the square step. The code above assures that only the non-center and empty points are given new values; these points being equivalent to side points, which are clarified below:
// The big 'O's indicate the 'side points' in this 2D array.
* O *
O * O
* O *
The section that concludes the while loop is given below:
displacementBound *= Math.pow(2, -Constants.DEFAULT_ROUGHNESS_CONSTANT);
// assign B to A
A = B;
iterations++;
} // end of while loop
The variable displacementBound is reduced in the section above, which comprises the end of the while loop, according to the information given in the aforementioned article. The contents of A are renewed by assigning the updated contents of B to A prior to beginning another iteration of the loop or terminating it.
Lastly, the ancillary methods averageFromCornersAndDisplace(), averageFromSidesAndDisplace(), and wrap() have been included but additional explanations for them are unnecessary. The method randomDisplacement() has not been included at all. For your information, it returns a random floating-point number x bounded by the given number b:
// The method returns a double x, where -b <= x < b
double randomDisplacement(double b);
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
我刚刚看到你的帖子出现了,我想你已经解决了。无论如何,如果您想做这样的换行,有一个巧妙的技巧可以解决负 mod 在 C/Java 中无法正常工作的事实。您所做的只是将模数的倍数(注意不要溢出)添加回该数字以确保它是非负数。然后你就可以照常进行改装而不会破坏它。这是一个例子:
I just saw your post pop up, and I guess you've already sorted it out. Anyway, if you want to do a wrap like that, there is a neat trick to fix the fact that negative mods don't work right in C/Java. What you do is just add some multiple of the modulus (being careful not to overflow) back to the number to ensure that it is non-negative. Then you can mod out as usual without it breaking. Here is an example:
wrap() 函数是罪魁祸首。当索引超出数组边界时,它会环绕索引,以便在边缘上将两个(通常是不同的)值一起平均。这导致了奇怪的不兼容。 我删除了对wrap()的所有调用,并选择在需要包装时平均三个相邻点而不是四个。
方法wrap()是旨在提供无缝平铺,但在本例中似乎引起了问题。而且平铺看起来也不是无缝的。
The wrap() function is the culprit. It wraps indexes around when they exceed the boundaries of the array, so that on the edges two (often disparate) values are averaged together. Which lead to the weird incompatibility. I deleted all calls to wrap() and chose to average three adjacent points instead of four whenever wrapping was necessary.
The method wrap() was meant to provide seamless tiling, but in this case seems to have caused a problem. And the tiling doesn't even look seamless.