Một phương pháp đơn giản để tạo mặt nạ đảo bản đồ


21


Tôi đang tìm kiếm một cách hay và dễ dàng để tạo mặt nạ cho bản đồ đảo với C #.


Về cơ bản tôi đang sử dụng với một sơ đồ chiều cao ngẫu nhiên được tạo ra với nhiễu perlin, trong đó địa hình KHÔNG được bao quanh bởi nước.

nhập mô tả hình ảnh ở đây

Bước tiếp theo sẽ là tạo mặt nạ, để đảm bảo các góc và đường viền chỉ là nước.

nhập mô tả hình ảnh ở đây

Sau đó, tôi chỉ có thể trừ mặt nạ từ hình ảnh nhiễu perlin để có được một hòn đảo.

nhập mô tả hình ảnh ở đây

và chơi xung quanh với sự tương phản ..

nhập mô tả hình ảnh ở đây

và đường cong gradient, tôi có thể có được một sơ đồ chiều cao đảo giống như tôi muốn ..

nhập mô tả hình ảnh ở đây

(đây chỉ là ví dụ của khóa học)

Vì vậy, như bạn có thể thấy, các "cạnh" của hòn đảo chỉ bị cắt đi, đó không phải là vấn đề lớn nếu giá trị màu không quá trắng, vì tôi sẽ chỉ chia màu xám thành 4 lớp (nước, cát, cỏ và đá).

Câu hỏi của tôi là, làm thế nào tôi có thể tạo ra một mặt nạ đẹp như trong hình ảnh thứ hai?


CẬP NHẬT

Tôi đã tìm thấy kỹ thuật này, nó có vẻ là một điểm khởi đầu tốt đẹp đối với tôi, nhưng tôi không chắc chắn làm thế nào tôi có thể thực hiện nó một cách xuất sắc để có được đầu ra mong muốn. http://mrl.nyu.edu/~perlin/experiment/puff/


CẬP NHẬT 2

đây là giải pháp cuối cùng của tôi

Tôi đã thực hiện makeMask()chức năng bên trong vòng lặp chuẩn hóa của mình như thế này:

        //normalisation
        for( int i = 0; i < width; i++ ) {
            for( int j = 0; j < height; j++ ) {
                perlinNoise[ i ][ j ] /= totalAmplitude;
                perlinNoise[ i ][ j ] = makeMask( width, height, i, j, perlinNoise[ i ][ j ] );
            }
        }

và đây là chức năng cuối cùng:

    public static float makeMask( int width, int height, int posX, int posY, float oldValue ) {
        int minVal = ( ( ( height + width ) / 2 ) / 100 * 2 );
        int maxVal = ( ( ( height + width ) / 2 ) / 100 * 10 );
        if( getDistanceToEdge( posX, posY, width, height ) <= minVal ) {
            return 0;
        } else if( getDistanceToEdge( posX, posY, width, height ) >= maxVal ) {
            return oldValue;
        } else {
            float factor = getFactor( getDistanceToEdge( posX, posY, width, height ), minVal, maxVal );
            return oldValue * factor;
        }
    }

    private static float getFactor( int val, int min, int max ) {
        int full = max - min;
        int part = val - min;
        float factor = (float)part / (float)full;
        return factor;
    }

    public static int getDistanceToEdge( int x, int y, int width, int height ) {
        int[] distances = new int[]{ y, x, ( width - x ), ( height - y ) };
        int min = distances[ 0 ];
        foreach( var val in distances ) {
            if( val < min ) {
                min = val;
            }
        }
        return min;
    }

điều này sẽ cho một đầu ra như trong hình ảnh # 3.

với một chút thay đổi trong mã, bạn có thể nhận được đầu ra mong muốn ban đầu như trong hình ảnh # 2 ->

    public static float makeMask( int width, int height, int posX, int posY, float oldValue ) {
        int minVal = ( ( ( height + width ) / 2 ) / 100 * 2 );
        int maxVal = ( ( ( height + width ) / 2 ) / 100 * 20 );
        if( getDistanceToEdge( posX, posY, width, height ) <= minVal ) {
            return 0;
        } else if( getDistanceToEdge( posX, posY, width, height ) >= maxVal ) {
            return 1;
        } else {
            float factor = getFactor( getDistanceToEdge( posX, posY, width, height ), minVal, maxVal );
            return ( oldValue + oldValue ) * factor;
        }
    }

bất kỳ cơ hội bạn có thể liên kết với youur nguồn đầy đủ?
khắc kỷ

Câu trả lời:


12

Tạo tiếng ồn thường xuyên với độ lệch cho các giá trị cao hơn về phía trung tâm. Nếu bạn muốn hình dạng đảo vuông như bạn thể hiện trong ví dụ của mình, tôi sẽ sử dụng khoảng cách đến cạnh gần nhất làm yếu tố của bạn.

nhập mô tả hình ảnh ở đây

Với yếu tố đó, bạn có thể sử dụng một cái gì đó như sau khi tạo tiếng ồn mặt nạ:

maxDVal = (minIslandSolid - maxIslandSolid)

getMaskValueAt(x, y)
    d = distanceToNearestEdge(x,y)
    if(d < maxIslandSolid)
        return isSolid(NonSolidValue) //always non-solid for outside edges
    else if(d < minIslandSolid)
        //noisy edges
        return isSolid((1 - (d/maxDVal)) * noiseAt(x,y))
    else
        return isSolid(SolidValue) //always return solid for center of island

Nơi distanceToNearestEdgetrả về khoảng cách đến cạnh gần nhất của bản đồ từ vị trí đó. Và isSolidquyết định xem giá trị từ 0 đến 1 là vững chắc (bất kể giá trị cắt của bạn là gì). Đây là một chức năng rất đơn giản, nó có thể trông như thế này:

Giá trị trả về isSolid (giá trị float) <solidCut OfferValue

Trong trường hợp solidCutOffValuelà bất cứ điều gì giá trị mà bạn đang sử dụng để quyết định giữa rắn hay không. Nó có thể được .5chia đều, hoặc .75cho rắn hơn hoặc .25ít rắn hơn.

Cuối cùng, chút này (1 - (d/maxDVal)) * noiseAt(x,y). Đầu tiên, chúng ta có được một yếu tố từ 0 đến 1 với điều này:

(1 - (d/maxDVal))

nhập mô tả hình ảnh ở đây

Trường hợp 0ở cạnh bên ngoài và 1ở cạnh bên trong. Điều này có nghĩa là tiếng ồn của chúng ta có nhiều khả năng là rắn ở bên trong và không rắn ở bên ngoài. Đây là yếu tố chúng tôi áp dụng cho tiếng ồn chúng tôi nhận được noiseAt(x,y).

Dưới đây là một đại diện trực quan hơn về các giá trị là gì, vì các tên có thể gây hiểu nhầm với các giá trị thực tế:

nhập mô tả hình ảnh ở đây


Thx cho câu trả lời nhanh chóng đó, tôi sẽ cố gắng thực hiện kỹ thuật này. hy vọng sẽ có được đầu ra mong muốn với điều này.
Ace

Có thể bạn sẽ thực hiện một số điều chỉnh để có được các cạnh ồn ào theo cách bạn muốn. Nhưng điều này nên là một cơ sở vững chắc cho bạn. Chúc may mắn!
MichaelHouse

Cho đến nay tôi đã triển khai cơ sở để làm việc, bạn có thể chỉ cho tôi một ví dụ về hàm IsSolid không? tôi không biết làm thế nào tôi có thể nhận được một giá trị từ 0 đến 1 dựa trên khoảng cách tối thiểu và tối đa từ cạnh. xem cập nhật cho mã của tôi cho đến nay.
Ace

Tôi đã có một số logic hỗn hợp trong đó. Tôi đã sửa nó để có ý nghĩa hơn. Và cung cấp một ví dụ vềisSolid
MichaelHouse

Để có được giá trị từ 0 đến 1, bạn chỉ cần tìm ra giá trị tối đa có thể là bao nhiêu và chia giá trị hiện tại của bạn cho nó. Sau đó, tôi đã trừ nó từ một, bởi vì tôi muốn số 0 ở cạnh bên ngoài và 1 ở cạnh bên trong.
MichaelHouse

14

Nếu bạn sẵn sàng sử dụng một số khả năng tính toán cho việc này, thì bạn có thể sử dụng một kỹ thuật tương tự như những gì tác giả của blog này đã làm. ( NB: Nếu bạn muốn sao chép trực tiếp mã của anh ấy, thì đó là ActionScript). Về cơ bản, anh ta tạo ra các điểm bán ngẫu nhiên (nghĩa là trông tương đối đồng đều) và sau đó sử dụng các điểm này để tạo đa giác Voronoi .

Đa giác Voronoi

Sau đó, anh ta đặt các đa giác bên ngoài thành nước và lặp qua các đa giác còn lại, biến chúng thành nước nếu một tỷ lệ nhất định của các đa giác liền kề là nước . Sau đó, bạn rời đi với một mặt nạ đa giác đại diện cho một hòn đảo.

Bản đồ đa giác

Từ điều này, bạn có thể áp dụng nhiễu cho các cạnh, dẫn đến một cái gì đó tương tự như thế này (màu sắc là từ một bước khác, không liên quan):

Bản đồ đa giác với các cạnh nhiễu

Sau đó, bạn được để lại với một mặt nạ hình hòn đảo (khá) trông giống như thật, sẽ phục vụ cho mục đích của bạn. Bạn có thể chọn sử dụng nó làm mặt nạ cho tiếng ồn Perlin của mình hoặc sau đó bạn có thể tạo các giá trị độ cao dựa trên khoảng cách với biển và thêm tiếng ồn (mặc dù điều đó có vẻ không cần thiết).


thx cho câu trả lời của bạn, nhưng đây là giải pháp đầu tiên (khá nhiều) tôi nhận được từ việc tìm kiếm trên web. Giải pháp này sẽ rất tốt, nhưng tôi muốn thử cách "đơn giản".
Ace

@Ace Đủ công bằng, nó có thể là một chút quá mức cho bất cứ điều gì bạn sẽ làm: P Tuy nhiên, nó đáng để ghi nhớ nếu bạn cần nó.
Cực

Vui mừng khi ai đó liên kết với điều này - trang đó luôn nằm trong danh sách "những bài viết thực sự tuyệt vời về cách ai đó thực hiện một cái gì đó".
Tim Holt

+1. Điều này thật tuyệt Cảm ơn bạn vì điều này, nó chắc chắn sẽ hữu ích cho tôi!
Andre

1

Một phương pháp rất đơn giản là tạo độ dốc xuyên tâm hoặc hình cầu ngược với tâm ở chiều rộng / 2 và chiều cao / 2. Để che giấu, bạn muốn trừ độ dốc khỏi nhiễu hơn là nhân nó. Điều này mang đến cho bạn những bờ biển trông thực tế hơn với nhược điểm là các đảo không nhất thiết phải kết nối.

Bạn có thể thấy sự khác biệt giữa trừ và nhân tiếng ồn với độ dốc tại đây: http://www.vfxpedia.com/index.php?title=Tips_and_T kỹ thuật / Natural_Phenomena /Soke

Nếu bạn không chắc chắn cách tạo gradient xuyên tâm, bạn có thể sử dụng điều này làm điểm bắt đầu:

    public static float[] CreateInverseRadialGradient(int size, float heightScale = 1)
    {
        float radius = size / 2;

        float[] heightMap = new float[size * size];

        for (int iy = 0; iy < size; iy++)
        {
            int stride = iy * size;
            for (int ix = 0; ix < size; ix++)
            {
                float centerToX = ix - radius;
                float centerToY = iy - radius;

                float distanceToCenter = (float)Math.Sqrt(centerToX * centerToX + centerToY * centerToY);
                heightMap[iy * size + ix] = distanceToCenter / radius * heightScale;
            }
        }

        return heightMap;
    }

Đừng quên điều chỉnh độ dốc của bạn theo cùng chiều cao với bản đồ chiều cao của bạn và bằng cách nào đó bạn vẫn cần phải tính đến dòng nước của mình.

Vấn đề với phương pháp này là trường chiều cao của bạn sẽ được tập trung xung quanh tâm của bản đồ. Tuy nhiên, phương pháp này sẽ giúp bạn bắt đầu với việc thêm các tính năng và làm cho phong cảnh của bạn đa dạng hơn khi bạn có thể sử dụng bổ sung để thêm các tính năng vào bản đồ chiều cao của mình.


thx cho câu trả lời. Tôi không chắc là tôi hiểu bạn có đúng không, nhưng bạn có nhận thấy rằng mặt nạ hoàn toàn không ảnh hưởng đến dữ liệu chiều cao ban đầu hay không, nó chỉ bị ảnh hưởng trong phủ định, vì vậy nó chỉ xác định các hình ảnh được hiển thị (tính theo%) hay không . nhưng tôi cũng đã thử nó với các bước đơn giản và tôi không hài lòng với kết quả này.
Ace

1

Tôi đề nghị thứ hai của ollipekka: những gì bạn muốn làm là trừ đi một hàm thiên vị phù hợp khỏi sơ đồ chiều cao của bạn, để các cạnh được đảm bảo ở dưới nước.

Có rất nhiều hàm thiên vị phù hợp, nhưng một hàm khá đơn giản là:

f(x, y) = 1 / (x * (1-x) * y * (1-y)) - 16

Trong đó xy là các giá trị tọa độ, được chia tỷ lệ nằm giữa 0 và 1. Hàm này lấy giá trị 0 ở trung tâm của bản đồ (tại x = y = 0,5) và có xu hướng vô cùng ở các cạnh. Do đó, trừ nó (được chia theo tỷ lệ hằng số phù hợp) khỏi bản đồ chiều cao của bạn, đảm bảo rằng các giá trị chiều cao cũng sẽ có xu hướng trừ đi vô cực gần các cạnh của bản đồ. Chỉ cần chọn bất kỳ chiều cao tùy ý bạn muốn và gọi nó là mực nước biển.

Như ollipekka lưu ý, phương pháp này sẽ không đảm bảo rằng hòn đảo sẽ tiếp giáp nhau. Tuy nhiên, việc chia tỷ lệ chức năng thiên vị theo hệ số tỷ lệ khá nhỏ sẽ khiến nó hầu như bằng phẳng ở khu vực giữa của bản đồ (do đó không ảnh hưởng nhiều đến địa hình của bạn), với độ lệch đáng kể chỉ xuất hiện ở gần các cạnh. Do đó, làm như vậy sẽ cung cấp cho bạn một hòn đảo vuông vức, chủ yếu tiếp giáp với, nhiều nhất có thể là một vài hòn đảo nhỏ gần các cạnh.

Tất nhiên, nếu bạn không quan tâm đến khả năng địa hình bị ngắt kết nối, hệ số tỷ lệ lớn hơn một chút sẽ cung cấp cho bạn nhiều nước hơn và hình dạng hòn đảo trông tự nhiên hơn. Điều chỉnh mực nước biển và / hoặc tỷ lệ của sơ đồ chiều cao ban đầu của bạn cũng có thể được sử dụng để thay đổi kích thước và hình dạng của (các) đảo kết quả.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.