我一直在生成噪点纹理,以用作地形生成的高度图。在这个应用程序中,最初有一个256x256的噪声纹理,用于创建一个用户可以自由漫游的土地块。当用户在游戏中到达某个边界时,应用程序会生成一个新纹理,从而产生另一个地形块。
在代码中,生成一个64x64随机值的表格,纹理中的值是这些点在各种频率之间进行插值的结果'和波长'使用smoothstep函数,然后组合形成最终的噪声纹理;最后,纹理中的值除以其最大值,以有效地将其标准化。当玩家处于边界并且创建新纹理时,创建的随机数表重新使用来自先前纹理的适当边缘的值(例如,如果新纹理是针对打开的一块土地)前一个纹理的每一行中的最后一个值被用作下一个随机数的每一行中的第一个值。)
我的问题是:即使在相邻纹理的边缘使用相同的值,它们也无处接近 - 地形上的一些相邻点与许多米不匹配。我的猜测是,用于对随机数表进行采样的频率变化可能对纹理的所有区域产生显着影响。那么怎样才能产生分形噪声,即。根据需要,并使它与相邻的值看起来是连续的吗?
以下是代码的一部分,它返回在给定点P的随机数表上的点之间插值的值:
float MainApp::assessVal(glm::vec2 P){
//Integer component of P
int xi = (int)P.x;
int yi = (int)P.y;
//Decimal component ofP
float xr = P.x - xi;
float yr = P.y - yi;
//Find the grid square P lies inside of
int x0 = xi % randX;
int x1 = (xi + 1) % randX;
int y0 = yi % randY;
int y1 = (yi + 1) % randY;
//Get random values for the 4 nodes
float r00 = randNodes->randNodes[y0][x0];
float r10 = randNodes->randNodes[y0][x1];
float r01 = randNodes->randNodes[y1][x0];
float r11 = randNodes->randNodes[y1][x1];
//Smoother interpolation so
//texture appears less blocky
float sx = smoothstep(xr);
float sy = smoothstep(yr);
//Find the weighted value of the 4
//random values. This will be the
//final value in the noise texture
float sx0 = mix(r00, r10, sx);
float sx1 = mix(r01, r11, sx);
return mix(sx0, sx1, sy);
}
其中randNodes是包含随机值的二维数组。
以下是获取上述函数返回的所有值并构造纹理数据的代码:
int layers = 5;
float wavelength = 1, frequency = 1;
for (int k = 0; k < layers; k++) {
for (int i = 0; i < stepsY; i++) {
for(int j = 0; j < stepsX; j++){
//Compute value for (stepsX * stepsY) interpolation points
//across the grid of random numbers
glm::vec2 P = glm::vec2((float)j/stepsX * randX, (float)i/stepsY * randY);
buf[i * stepsY + j] += assessVal(P * wavelength) * frequency;
}
}
//repeat (layers) times with different signals
wavelength *= 0.5;
frequency *= 2;
}
for(int i = 0; i < buf.size(); i++){
//divide all data by the largest value.
//this normalises the data to avoid saturation
buf[i] /= largestVal;
}
最后,这是一个由这些函数生成的两个纹理的示例,应该是无缝的,但不是:
现在并排放置的2张图像显然是不匹配的。
答案 0 :(得分:0)
当然,根本原因是你的平滑会改变像素以匹配它们的邻居,但是你以后会添加新邻居并且不会重新平滑获得新邻居的像素。
一个简单而常见的解决方法是保留不可见像素的边缘,其宽度是平滑内核的一半。现在,在扩展区域时,可以在它们被揭示之前重新平滑这些不可见像素。别忘了添加不可见像素的新边缘!
答案 1 :(得分:0)
您的代码仅在您读取的噪声纹理的域中包装值,但不会在正在生成的纹理的域中包装。
对于可重复的大小T(0) == T(stepX)
的纹理T(为简单起见,我们考虑1-d)你必须
j = 0
或者在您的情况下(替换j = stepX
和assessVal(0) == assessVal(randX * wavelength)
):
k >= 1
对于(randX / pow(2, k)) % randX != 0
,在您的代码中显然不是这样,因为
randX
一种解决方案是在频率上升时减少randY
和2x2
。
但我的典型方法是从4x4
随机纹理开始,使用GL_REPEAT
将其升级到8x8
,每像素噪点添加一点,继续升级到{{ 1}}等等。直到达到所需的大小。