使用等离子体样式分形生成高度图期间的伪影
本文关键字:伪影 高度 等离子体 样式 分形 | 更新日期: 2023-09-27 17:58:40
我今天花了几个小时研究如何生成随机地形,在阅读了等离子体分形(中点位移和菱形平方算法)后,我决定尝试实现一个。事实上,我的结果并不可怕,但我有这些可怕的方形/线条/网格型文物,我似乎无法摆脱!
当渲染为灰度图像时,我的高度图看起来像:高度图http://sphotos-d.ak.fbcdn.net/hphotos-ak-ash3/535816_10151739010123327_225111175_n.jpg
显然,这涉及到相当多的代码,但我会尽量发布只相关的内容。例如,我还没有发布将其转化为纹理的代码,但不用担心,我已经尝试过用平滑的梯度填充我的高度数组,纹理效果很好:)
我首先将地图的四个角设置为0到1之间的随机值,然后开始递归位移算法:
public void GenerateTerrainLayer()
{
//set the four corners of the map to have random values
TerrainData[0, 0] = (float)RandomGenerator.NextDouble();
TerrainData[GenSize, 0] = (float)RandomGenerator.NextDouble();
TerrainData[0, GenSize] = (float)RandomGenerator.NextDouble();
TerrainData[GenSize, GenSize] = (float)RandomGenerator.NextDouble();
//begin midpoint displacement algorithm...
MidPointDisplace(new Vector2_I(0, 0), new Vector2_I(GenSize, 0), new Vector2_I(0, GenSize), new Vector2_I(GenSize, GenSize));
}
TerrainData只是一个浮动的2D数组*。Vector2_I只是我自己的整数向量类。最后四个函数是MidPointDisplace,它是递归函数,CalculateTerrainPointData,它平均2个数据值并添加一些噪声,CalculateTrainPointData2,它平均了4个数据值,并添加了一些噪声,具有略高的标度值(仅用于中心点),最后是我的噪声函数,atm只是一些随机噪声,而不是像perlin等那样的真实噪声。它们看起来像这样:
private void MidPointDisplace(Vector2_I topleft, Vector2_I topright, Vector2_I bottomleft, Vector2_I bottomright)
{
//check size of square working on.. if its shorter than a certain amount stop the algo, we've done enough
if (topright.X - topleft.X < DisplacementMaxLOD)
{
return;
}
//calculate the positions of all the middle points for the square that has been passed to the function
Vector2_I MidLeft, MidRight, MidTop, MidBottom, Center;
MidLeft.X = topleft.X;
MidLeft.Y = topleft.Y + ((bottomleft.Y - topleft.Y) / 2);
MidRight.X = topright.X;
MidRight.Y = topright.Y + ((bottomright.Y - topright.Y) / 2);
MidTop.X = topleft.X + ((topright.X - topleft.X) / 2);
MidTop.Y = topleft.Y;
MidBottom.X = bottomleft.X + ((bottomright.X - bottomleft.X) / 2);
MidBottom.Y = bottomleft.Y;
Center.X = MidTop.X;
Center.Y = MidLeft.Y;
//collect the existing data from the corners of the area passed to algo
float TopLeftDat, TopRightDat, BottomLeftDat, BottomRightDat;
TopLeftDat = GetTerrainData(topleft.X, topleft.Y);
TopRightDat = GetTerrainData(topright.X, topright.Y);
BottomLeftDat = GetTerrainData(bottomleft.X, bottomleft.Y);
BottomRightDat = GetTerrainData(bottomright.X, bottomright.Y);
//and the center
//adverage data and insert for midpoints..
SetTerrainData(MidLeft.X, MidLeft.Y, CalculateTerrainPointData(TopLeftDat, BottomLeftDat, MidLeft.X, MidLeft.Y));
SetTerrainData(MidRight.X, MidRight.Y, CalculateTerrainPointData(TopRightDat, BottomRightDat, MidRight.X, MidRight.Y));
SetTerrainData(MidTop.X, MidTop.Y, CalculateTerrainPointData(TopLeftDat, TopRightDat, MidTop.X, MidTop.Y));
SetTerrainData(MidBottom.X, MidBottom.Y, CalculateTerrainPointData(BottomLeftDat, BottomRightDat, MidBottom.X, MidBottom.Y));
SetTerrainData(Center.X, Center.Y, CalculateTerrainPointData2(TopLeftDat, TopRightDat, BottomLeftDat, BottomRightDat, Center.X, Center.Y));
debug_displacement_iterations++;
//and recursively fire off new calls to the function to do the smaller squares
Rectangle NewTopLeft = new Rectangle(topleft.X, topleft.Y, Center.X - topleft.X, Center.Y - topleft.Y);
Rectangle NewTopRight = new Rectangle(Center.X, topright.Y, topright.X - Center.X, Center.Y - topright.Y);
Rectangle NewBottomLeft = new Rectangle(bottomleft.X, Center.Y, Center.X - bottomleft.X, bottomleft.Y - Center.Y);
Rectangle NewBottomRight = new Rectangle(Center.X , Center.Y, bottomright.X - Center.X, bottomright.Y - Center.Y);
MidPointDisplace(new Vector2_I(NewTopLeft.Left, NewTopLeft.Top), new Vector2_I(NewTopLeft.Right, NewTopLeft.Top), new Vector2_I(NewTopLeft.Left, NewTopLeft.Bottom), new Vector2_I(NewTopLeft.Right, NewTopLeft.Bottom));
MidPointDisplace(new Vector2_I(NewTopRight.Left, NewTopRight.Top), new Vector2_I(NewTopRight.Right, NewTopRight.Top), new Vector2_I(NewTopRight.Left, NewTopRight.Bottom), new Vector2_I(NewTopRight.Right, NewTopRight.Bottom));
MidPointDisplace(new Vector2_I(NewBottomLeft.Left, NewBottomLeft.Top), new Vector2_I(NewBottomLeft.Right, NewBottomLeft.Top), new Vector2_I(NewBottomLeft.Left, NewBottomLeft.Bottom), new Vector2_I(NewBottomLeft.Right, NewBottomLeft.Bottom));
MidPointDisplace(new Vector2_I(NewBottomRight.Left, NewBottomRight.Top), new Vector2_I(NewBottomRight.Right, NewBottomRight.Top), new Vector2_I(NewBottomRight.Left, NewBottomRight.Bottom), new Vector2_I(NewBottomRight.Right, NewBottomRight.Bottom));
}
//helper function to return a data value adveraged from two inputs, noise value added for randomness and result clamped to ensure a good value
private float CalculateTerrainPointData(float DataA, float DataB, int NoiseX, int NoiseY)
{
return MathHelper.Clamp(((DataA + DataB) / 2.0f) + NoiseFunction(NoiseX, NoiseY), 0.0f, 1.0f) * 1.0f;
}
//helper function to return a data value adveraged from four inputs, noise value added for randomness and result clamped to ensure a good value
private float CalculateTerrainPointData2(float DataA, float DataB, float DataC, float DataD, int NoiseX, int NoiseY)
{
return MathHelper.Clamp(((DataA + DataB + DataC + DataD) / 4.0f) + NoiseFunction(NoiseX, NoiseY), 0.0f, 1.0f) * 1.5f;
}
private float NoiseFunction(int x, int y)
{
return (float)(RandomGenerator.NextDouble() - 0.5) * 0.5f;
}
好的,谢谢你花时间看——希望有人知道这个网格状的图案是从哪里出现的:)
*edit-意外写入int,更正为浮动
我在您的代码中发现了3个问题。(其中2个相关)
你不会在每一步中缩小随机性。每一步都必须减少随机性。否则你会得到白色的噪音。你选择一个因子(0.5-0.7对我来说很好),在每次递归中用阿尔法乘以减少,并用该因子缩放生成的随机数。
你把菱形台阶和方形台阶互换了。先是钻石,然后是方块。反过来是不可能的(见下一页)。
你的方步只使用一个方向上的点。这可能是你所说的矩形结构的原因。正方形必须将所有四边的值取平均值。这意味着方形步长取决于菱形步长生成的点。不仅是您当前查看的矩形的菱形步长,还有它旁边的矩形。对于地图之外的值,您可以换行、使用固定值或仅平均3个值。
我在您的CalculateTerrainPointData
实现中看到了一个问题:您没有在每次迭代中缩小NoiseFunction
的结果。
请参阅中点位移算法的描述:
- 从一条水平线段开始。
- 重复足够多次:
- 在场景中的每个线段上重复:
- 找到线段的中点
- 将Y中的中点移动一个随机量
- 减小随机数的范围
在代码中快速执行此操作而不需要更改太多的方法是在MidPointDisplace
(默认设置为1.0f)和CalculateTerrainPointData
中添加一些scale
参数;在CalculateTerrainPointData
中使用它来乘以NoiseFunction
的结果;并通过每次递归调用将其缩减为CCD_ 8。
但不确定这是导致您的图像看起来错误的唯一原因还是存在其他问题
根据维基百科对中点位移的总结,只有最中心点的平均值才会被添加到其中——尝试仅通过CalculateTerrainPointData2
&去除CCD_ 10中的噪声。