Szukam przyjemnego i łatwego sposobu na wygenerowanie maski mapy wyspy za pomocą C #.
Zasadniczo używam z losową mapą wysokości generowaną z hałasem perlin, gdzie teren NIE jest otoczony wodą.
Następnym krokiem byłoby wygenerowanie maski, aby rogi i granice były tylko wodą.
Następnie mogę odjąć maskę od obrazu szumu perlin, aby uzyskać wyspę.
i bawić się z kontrastem ...
i krzywą gradientu, mogę uzyskać mapę wysokości wyspy tak, jak chcę.
(są to tylko przykłady)
więc, jak widać, „krawędzie” wyspy są po prostu odcięte, co nie jest dużym problemem, jeśli wartość koloru nie jest zbyt biała, ponieważ po prostu podzielę skalę szarości na 4 warstwy (woda, piasek, trawa i skała).
Moje pytanie brzmi: jak mogę wygenerować dobrze wyglądającą maskę, jak na drugim zdjęciu?
AKTUALIZACJA
Znalazłem tę technikę, wydaje mi się, że jest to dobry punkt wyjścia dla mnie, ale nie jestem pewien, jak dokładnie mogę ją wdrożyć, aby uzyskać pożądany efekt. http://mrl.nyu.edu/~perlin/experiments/puff/
AKTUALIZACJA 2
to jest moje ostateczne rozwiązanie.
Zaimplementowałem makeMask()
funkcję w mojej pętli normalizacyjnej w następujący sposób:
//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 ] );
}
}
i to jest ostatnia funkcja:
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;
}
da to wynik jak na obrazku nr 3.
z niewielką zmianą w kodzie, możesz uzyskać pierwotnie poszukiwany wynik, jak na obrazku # 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;
}
}
Odpowiedzi:
Generuj regularny hałas z nastawieniem na wyższe wartości w kierunku centrum. Jeśli chcesz mieć kwadratowe kształty wysp, takie jak pokazano w twoim przykładzie, użyłbym odległości do najbliższej krawędzi jako czynnika.
Dzięki temu współczynnikowi podczas generowania szumu maski można użyć czegoś takiego:
Gdzie
distanceToNearestEdge
zwraca odległość do najbliższej krawędzi mapy od tej pozycji. IisSolid
decyduje, czy wartość między 0 a 1 jest stała (niezależnie od tego, jaka jest twoja wartość odcięcia). Jest to bardzo prosta funkcja, może wyglądać następująco:isSolid (liczba zmiennoprzecinkowa) zwraca wartość <solidCutOffValue
Gdzie
solidCutOffValue
jest jakakolwiek wartość, której używasz, aby zdecydować się na solidny lub nie. Może to być.5
równomierny podział, lub.75
bardziej solidny lub.25
mniej solidny.Wreszcie to trochę
(1 - (d/maxDVal)) * noiseAt(x,y)
. Po pierwsze, otrzymujemy współczynnik od 0 do 1 z tego:(1 - (d/maxDVal))
Gdzie
0
jest na zewnętrznej krawędzi i1
na wewnętrznej krawędzi. Oznacza to, że nasz hałas jest bardziej solidny w środku i nie jest stały na zewnątrz. Jest to czynnik, który wpływamy na hałas, który otrzymujemynoiseAt(x,y)
.Oto bardziej wizualna reprezentacja wartości, ponieważ nazwy mogą wprowadzać w błąd w stosunku do rzeczywistych wartości:
źródło
isSolid
Jeśli chcesz poświęcić na to trochę mocy obliczeniowej, możesz użyć techniki podobnej do tej, którą zrobił autor tego bloga . ( Uwaga: jeśli chcesz bezpośrednio skopiować jego kod, jest on w języku ActionScript). Zasadniczo generuje quasi-losowe punkty (tzn. Wygląda względnie jednolicie), a następnie wykorzystuje je do tworzenia wielokątów Voronoi .
Następnie ustawia zewnętrzne wielokąty na wodę i iteruje przez resztę wielokątów, czyniąc je wodami, jeśli pewien procent sąsiadujących wielokątów jest wodą . Następnie pozostaje maska wielokąta z grubsza reprezentująca wyspę.
Na tej podstawie możesz zastosować szum do krawędzi, w wyniku czego coś będzie to przypominać (kolory pochodzą z innego, niezwiązanego kroku):
Pozostaje ci (całkiem) realistycznie wyglądająca maska w kształcie wyspy, która spełni twoje oczekiwania. Możesz użyć go jako maski dla hałasu Perlin, lub możesz wygenerować wartości wysokości na podstawie odległości od morza i dodać hałas (choć wydaje się to niepotrzebne).
źródło
Jedną z bardzo prostych metod jest utworzenie odwrotnego gradientu promieniowego lub sferycznego ze środkiem na szerokości / 2 i wysokości / 2. W celu maskowania chcesz odjąć gradient od szumu zamiast go pomnożyć. Daje to bardziej realistycznie wyglądające brzegi z wadą polegającą na tym, że wyspy niekoniecznie są połączone.
Różnicę między odejmowaniem a mnożeniem hałasu przez gradient można zobaczyć tutaj: http://www.vfxpedia.com/index.php?title=Tips_and_Techniques/Natural_Phenomena/Smoke
Jeśli nie masz pewności, jak utworzyć gradient radialny, możesz użyć tego jako punktu początkowego:
Nie zapomnij przeskalować gradientu do tej samej wysokości, co mapa wysokości, a nadal musisz w jakiś sposób uwzględnić linię wodną.
Problem z tą metodą polega na tym, że twoje pole wysokości będzie wyśrodkowane wokół środka mapy. Metoda ta jednak powinna Ci zacząć z dodawaniem funkcji i podejmowaniu krajobraz bardziej różnorodna, jak można wykorzystać dodatek dodawać funkcje do swojego wzrostu mapie.
źródło
Druga sugestia Ollipekki: chcesz odjąć odpowiednią funkcję odchylenia od mapy wysokości, aby zagwarantować, że krawędzie są pod wodą.
Istnieje wiele odpowiednich funkcji odchylenia, ale jedna dość prosta to:
gdzie x i y to wartości współrzędnych, skalowane leżeć w zakresie od 0 do 1. Funkcja ta przyjmuje wartość 0 w środku mapy (przy x = y = 0,5) i ma tendencję do nieskończoności na brzegach. W ten sposób odjęcie go (skalowane odpowiednim współczynnikiem stałym) od mapy wysokości zapewnia, że wartości wysokości będą również miały tendencję do minus nieskończoności w pobliżu krawędzi mapy. Wystarczy wybrać dowolną dowolną wysokość i nazwać ją poziomem morza.
Jak zauważa ollipekka, takie podejście nie gwarantuje, że wyspa będzie przyległa. Jednak przeskalowanie funkcji odchylania za pomocą dość małego współczynnika skali powinno sprawić, że będzie ona głównie płaska w środkowej części mapy (a zatem nie wpłynie znacząco na twój teren), a znaczące odchylenie pojawi się tylko w pobliżu krawędzi. Dlatego zrobienie tego powinno dać ci kwadratową, w większości przylegającą wyspę z co najwyżej możliwym kilkoma małymi sub wyspami w pobliżu krawędzi.
Oczywiście, jeśli nie przeszkadza ci możliwość odłączenia terenu, nieco większy współczynnik skalowania powinien dać ci więcej wody i bardziej naturalny kształt wyspy. Dostosowania poziomu morza i / lub skali oryginalnej mapy wysokości można także użyć do zmiany wielkości i kształtu powstałej wyspy (wysp).
źródło