1

这个问题让我发疯,我已经搜索并加厚但无法解决。我有一些 2D 浮点数组 [,] (顺便说一句,我使用的是 C#),并且总是放在一起形成一个正方形。有 1,4,9,16... 数组。每个二维浮点数组都代表一个高度图,因此它们是 129x129、513x513、1025x1025 ......并且它们都是相同的。我通过高度图文件中的算法将一些值导入数组。高度图是 8 位原始的,所以我在高度图中得到了一个阶梯状的外观,因为只有 256 种灰色阴影。现在我在每个数组中使用平滑算法(这里的算法:http: //nic-gamedev.blogspot.com.es/2013/02/simple-terrain-smoothing.html) 看起来不错,但是,它应用于每个数组,因此它们之间的边界不匹配,并且在将数组实现到地形时,它们之间存在间隙。

我已经考虑过各种解决方案,其中一种是将阵列连接成一个大阵列并对其进行平滑处理,但我不想使用这种技术,因为我可能拥有大量地形,而且我需要一个 gigantinc 阵列。

所以......关于如何做到这一点的任何想法?请这是我的第一个问题,因为我不知道该怎么做。问我是否需要更多信息,对不起我的英语。

编辑:

插图:

在此处输入图像描述

4

1 回答 1

1

您正在使用的算法正在做的不是查看二维数组(从现在开始我们将其称为“块”),而是进行平滑处理(这些是if ((x - 1) > 0) // Check to left条件)。
当然,这可以防止边界错误的数组索引,并且对于没有任何邻居的块来说这很好,但如果您希望该块与其邻居混合,则不能。

解决方案非常简单:如果您希望块的边缘与其相邻块平滑融合,则在平滑块时必须使用它们。
也就是说,您仍然可以将要平滑的值限制为一个块,但您必须能够读取其相邻块的边缘以计算正确的平均值。

我希望这个解释足够清楚,如果不只是让我知道,我会写一些示例代码。

顺便说一句,您参考的博客上描述的平滑算法实现得相当粗糙。您可以尝试使用单个更大的过滤器,而不是使用 3x3 卷积过滤器的多次传递。我发现这个页面(参见前几段,直到“Alpha 通道”)解释得很好,尽管它是在 java.lang.


好的,我已经编写了一些示例代码。一个警告:我将我的 2d 数组视为行优先,即myArray[y, x],但我将我的索引器属性声明为[x, y].

首先,让我们为 Block 创建一个类,这允许一些辅助方法,并且可以很容易地在我们正在处理 Block 的其余代码中看到:

public class Block
{
    public const int Size = 8;

    private float[,] _values;

    public float this[int x, int y]
    {
        get { return _values[y, x]; }
        set { _values[y, x] = value; }
    }

    public Block(float value)
    {
        //Initialize all cells with the given value, this makes it easier to demo the code.
        _values = new float[Size, Size];
        for (int y = 0; y < Size; y++)
        {
            for (int x = 0; x < Size; x++)
                _values[y, x] = value;
        }
    }

    public string[] TextLines
    {
        get
        {
            List<string> lines = new List<string>();
            for (int y = 0; y < Size; y++)
            {
                StringBuilder sbLine = new StringBuilder();
                for (int x = 0; x < Size; x++)
                    sbLine.AppendFormat("{0:00} ", _values[y, x]);

                lines.Add(sbLine.ToString());
            }

            return lines.ToArray();
        }
    }
}

接下来,我们将定义一个抽象层,它允许我们处理 3x3 块,就好像它是一个大区域一样。坐标范围从 -8 到 15(含)。

/// <summary>
/// Wrapper around 3x3 Blocks, allows reading from a range of [-8, 15] x [-8, 15].
/// Not that writing is not supported.
/// </summary>
public class BlockContainer
{
    private Block[,] _blocks;

    public const float DefaultValue = 128;

    public float this[int x, int y]
    {
        get
        {
            int blockX = 1, blockY = 1;

            //If the coordinates exceed the center Block, move to the adjacent Block.
            if (x < 0)
            {
                blockX--;
                x += Block.Size;
            }
            else if (x >= Block.Size)
            {
                blockX++;
                x -= Block.Size;
            }

            if (y < 0)
            {
                blockY--;
                y += Block.Size;
            }
            else if (y >= Block.Size)
            {
                blockY++;
                y -= Block.Size;
            }

            //Get the Block to read from - if there is no Block, just return the DefaultValue.
            //This is not ideal, but for now it works.
            Block block = _blocks[blockY, blockX];
            return (block != null) 
                ? block[x, y] 
                : DefaultValue;        
        }
    }

    public BlockContainer(Block[,] blocks)
    {
        if (blocks == null || blocks.GetLength(0) != 3 || blocks.GetLength(1) != 3)
            throw new ArgumentException("Expected 3x3 Blocks.");        

        _blocks = blocks;
    }
}

最后,(控制台)程序将 4x2 块定义为地形,并使用以下类平滑 1 个块:

class Program
{
    private const float OneNinth = 1.0f / 9;

    /// <summary>
    /// Simple convolution filter that does a rectangle blur.
    /// </summary>
    private static float[,] Filter = new float[,]
    {
        {OneNinth, OneNinth, OneNinth},
        {OneNinth, OneNinth, OneNinth},
        {OneNinth, OneNinth, OneNinth},
    };

    /// <summary>
    /// Simple 4x2 terrain example. For demo purposes, each block consists of only 1 value.
    /// </summary>
    private static Block[,] Terrain = new Block[,]
    {
        { new Block(8f), new Block(16f), new Block(24f), new Block(32f) },
        { new Block(8f), new Block(32f), new Block(16f), new Block(8f) },
    };

    public static void Main(string[] args)
    {
        BlockContainer container = GetBlockContainer(2, 0);    //The Block with only 24f values.
        Block result = Apply3x3Filter(Filter, container);

        Console.WriteLine(string.Join(Environment.NewLine, result.TextLines));

        Console.WriteLine("Press enter to exit...");
        Console.ReadLine();
    }

    /// <summary>
    /// Gets the 3x3 Block area around (terrainX, terrainY) as a BlockContainer.
    /// </summary>
    private static BlockContainer GetBlockContainer(int terrainX, int terrainY)
    {
        Block[,] readBlocks = new Block[3, 3];
        for (int blockY = -1; blockY <= 1; blockY++)
        {
            for (int blockX = -1; blockX <= 1; blockX++)
            {
                int sourceX = terrainX + blockX;
                int sourceY = terrainY + blockY;

                if (sourceX >= 0 && sourceX < 4 && sourceY >= 0 && sourceY < 2)
                    readBlocks[blockY + 1, blockX + 1] = Terrain[sourceY, sourceX];
            }
        }

        return new BlockContainer(readBlocks);
    }

    private static Block Apply3x3Filter(float[,] filter, BlockContainer container)
    {
        Block resultBlock = new Block(0.0f);
        for (int y = 0; y < Block.Size; y++)
        {
            for (int x = 0; x < Block.Size; x++)
            {
                //Read the 3x3 area around (x, y) and multiply them with the values in the 
                //convolution filter.
                float sum = 0.0f;
                for (int fy = -1; fy <= 1; fy++)
                {
                    for (int fx = -1; fx <= 1; fx++)
                        sum += (container[x + fx, y + fy] * filter[fy + 1, fx + 1]);
                }

                //The sum is our averaged value for (x, y).
                resultBlock[x, y] = sum;
            }
        }

        return resultBlock;
    }
}

输出是:

57 59 59 59 59 59 59 60
21 24 24 24 24 24 24 27
21 24 24 24 24 24 24 27
21 24 24 24 24 24 24 27
21 24 24 24 24 24 24 27
21 24 24 24 24 24 24 27
21 24 24 24 24 24 24 27
21 21 21 21 21 21 21 22
Press enter to exit...

这可以通过它的左侧和下方具有 16f 的块和右侧的 32f 来解释。它上面没有 Block,但我们将 DefaultValue 定义为 128,因此平均值为 59。

我希望这能解释事情:) 如果您有任何问题,请告诉我。

于 2014-02-02T15:38:05.520 回答