문제

Solved

I'm working on a 2D procedural island generator. So far I've used several noise functions and blending to achieve the desired island shapes but currently they are only 300 x 200 pixels in size. What I want to do is scale the image data (NOT THE IMAGE) up to the desired map size of somewhere in the range of 400,000 x 400,000. I considered simply changing the noise functions to a lower frequency resulting in a larger island but I'm also blending them with a generated gradient that is already computationally heavy. Increasing the size of my generated gradient would hurt the generation time greatly.

My island shape/image: http://4.bp.blogspot.com/-ed7nK7jwcJc/UxYjNMxUF-I/AAAAAAAAASc/sG-BygA4usA/s1600/PopulationMap1.png

The data behind the island is an array of floats ranging from 0 - 1 that represent the water/beach/land. All I need is to scale THAT data to the range I need for continuing with the generation of roads and so on. Basically each pixel is a tile, and I need somewhere around 400k in width from the current 300 in width. I realize that there is probably too little input data to scale it that high but I would still like to attempt it.

I've read up on all the image filtering and interpolating methods but I can't get a grasp on any of them. I noticed this question already asked Array interpolation but I can't understand how to get it working on a grid larger than 4x4, but the OP describes exactly what I'm looking for.

Basically linear or bicubic filtering/scaling on a large grid of float value. I just need some example code to outline the process..

If all else fails I'll switch my gradient to a more simple approach and just generate the entirety of the map data at the size I want it to begin with. But I really would like to try scaling it. Thanks in advance.

EDIT - I've gotten to the point where I have a list of weighted value pairs for each tile surrounding the point I'm querying at. So If I was querying for the value at 5.5, 5.5 I would get the value and weight of the tiles surrounding (and including) 5,5. So I have a list of 9 {0.9, 1.27} { 0.3, 0.77} etc. value pairs where the first is the element in the array and the second is it's weight (inverse of the distance between it and the queried point). Now I'm struggling with a way to add all these weighted value pairs together to reach a final value. I'm pretty sure it has to do with some sort of interpolation but I can't quite grasp the way to do it. If I can get past this part then I should be good with the rest.

도움이 되었습니까?

해결책

I figured out a method that works well for me. I'll try to explain my solution down here in-case anyone else needs help with this topic. Note that this probably isn't the best solution but it does get the job done.

I started with my source array float[,] sourceArray and a larger array that I want to scale to float[,] targetArray

I then loop through every tile(array element) in my target array and get the percentage value between it's center and the total height and width of the scale target. I then call a method that returns the calculated value, passing in the percentage, source array, the width of the source array, and the height of the source array.

for(float x = 0, x < scaleWidth, x++)
{
    for(float y = 0, y < scaleHeight, y++)
    {
        Vector2 percentage = new Vector2((x+0.5)/scaleWidth, (y+0.5)/scaleHeight);
        targetArray[x,y] = GetScaleValueAt(percentage, sourceArray, sourceWidth, sourceHeight);
    }
}

My GetScaleValueAt() method returns the requested, correctly scaled value. First it finds a corresponding location in the source array based on the percentage. Also it finds the tile that the location is inside of.

public float GetScaleValueAt(float percent, float[,] source, int width, int height)
{
    Vector2 location = new Vector2(width * percent.X, height * percent.Y);
    Point centerTile = new Point((int)Math.Floor(loc.X), (int)Math.Floor(loc.Y));\

Then I use another method to get all the tiles adjacent to the center one (including the center tile)

    List<Point> tiles = GetIncNeighborhood(centerTile.X, centerTile.Y, width, height);

Next I iterate through this list and populate a second list with WeightedPair's which is just a class or struct with two float values - float value, weight that are accesible. The weight is calculated from the inverse of the distance between location and each tile. I also save the total of weighted values.

    List<WeightedPair> weightValues = new List<WeightedPair>();
    float weightedTotal = 0;
    foreach(Point p in tiles)
    {
        Vector2 distance = new Vector2();
        distance.X = System.Math.Abs(loc.X - p.X);
        distance.Y = System.Math.Abs(loc.Y - p.Y);
        float inverseDist = 1 / distance.Length();

        float value = source[p.X, p.Y];

        weightedTotal += invDist;
        weightValues.Add(new WeightedPair(value, invDist));
    }

Finally, I loop through weightValues list and calculate the value by multiplying each value by it's corresponding weight/weightedTotal.

    float finalValue = 0;
    foreach(WeightedPair w in weightValues)
    {
        finalValue += w.value * (w.weight/weightedTotal);
    }
    return finalValue;
}

The result is an image similar to the smaller version in overall shape and complexity with some minor artifacts and fidelity issues. It's not as good as standard image processing techniques but it works perfectly for me. Hope this helps someone.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top