Ich suche nach einer schönen und einfachen Möglichkeit, mit C # eine Maske für eine Inselkarte zu generieren.
Grundsätzlich benutze ich eine zufällige Höhenkarte, die mit Perlinrauschen erzeugt wurde, wobei das Gelände NICHT von Wasser umgeben ist.
Der nächste Schritt wäre, eine Maske zu generieren, um sicherzustellen, dass die Ecken und Ränder nur Wasser sind.
Dann kann ich einfach die Maske vom Perlin-Rauschbild subtrahieren, um eine Insel zu erhalten.
und mit dem Kontrast herumspielen ..
und der Gradientenkurve kann ich eine Inselhöhenkarte bekommen, wie ich es will ..
(Dies sind nur Beispiele natürlich)
Wie Sie sehen können, werden die "Ränder" der Insel einfach abgeschnitten, was kein großes Problem ist, wenn der Farbwert nicht zu weiß ist, da ich die Graustufen einfach in 4 Schichten (Wasser, Sand, Gras und Rock).
Meine Frage ist, wie kann ich eine gut aussehende Maske wie im zweiten Bild erzeugen?
AKTUALISIEREN
Ich habe diese Technik gefunden, sie scheint ein guter Ausgangspunkt für mich zu sein, aber ich bin mir nicht sicher, wie genau ich sie implementieren kann, um die gewünschte Ausgabe zu erzielen. http://mrl.nyu.edu/~perlin/experiments/puff/
UPDATE 2
Das ist meine endgültige Lösung.
Ich habe die makeMask()
Funktion in meiner Normalisierungsschleife folgendermaßen implementiert :
//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 ] );
}
}
und das ist die letzte Funktion:
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;
}
Dies ergibt eine Ausgabe wie in Bild 3.
Mit ein wenig Änderung im Code können Sie die ursprünglich gewünschte Ausgabe wie in Bild 2 erhalten ->
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;
}
}
Antworten:
Erzeugen Sie regelmäßiges Rauschen mit einer Neigung für höhere Werte zur Mitte. Wenn Sie quadratische Inselformen wie in Ihrem Beispiel wünschen, würde ich den Abstand zur nächsten Kante als Faktor verwenden.
Mit diesem Faktor können Sie beim Erzeugen des Maskenrauschens Folgendes verwenden:
Where
distanceToNearestEdge
gibt die Entfernung von dieser Position zum nächsten Kartenrand zurück. UndisSolid
entscheidet, ob ein Wert zwischen 0 und 1 fest ist (unabhängig davon, wie hoch Ihr Cut-Off ist). Es ist eine sehr einfache Funktion, die so aussehen könnte:isSolid (float value) Rückgabewert <solidCutOffValue
Wo
solidCutOffValue
ist der Wert, mit dem Sie sich zwischen soliden und nicht soliden Werten entscheiden? Es könnte.5
für sogar gespalten sein, oder.75
für fester oder.25
für weniger fest.Zum Schluss dieses kleine bisschen
(1 - (d/maxDVal)) * noiseAt(x,y)
. Zunächst erhalten wir einen Faktor zwischen 0 und 1:(1 - (d/maxDVal))
Wo
0
ist an der Außenkante und1
ist an der Innenkante. Dies bedeutet, dass unsere Geräusche im Inneren eher fest und im Äußeren weniger fest sind. Dies ist der Faktor, den wir auf das Geräusch anwenden, von dem wir kommennoiseAt(x,y)
.Hier ist eine visuellere Darstellung der Werte, da die Namen möglicherweise zu den tatsächlichen Werten irreführend sind:
quelle
isSolid
Wenn Sie bereit sind, Rechenleistung dafür zu sparen, können Sie eine ähnliche Technik anwenden wie der Autor dieses Blogs . ( Hinweis: Wenn Sie seinen Code direkt kopieren möchten, ist er in ActionScript enthalten.) Grundsätzlich erzeugt er quasi zufällige Punkte (sieht also relativ gleichmäßig aus) und erstellt daraus Voronoi-Polygone .
Er setzt dann die äußeren Polygone auf Wasser und iteriert durch die restlichen Polygone, wobei er sie wässern lässt, wenn ein bestimmter Prozentsatz der benachbarten Polygone Wasser ist . Dann bleibt eine Polygonmaske übrig, die ungefähr eine Insel darstellt.
Von hier aus können Sie Rauschen auf die Kanten anwenden, was zu etwas ähnlichem führt (die Farben stammen von einem anderen, nicht verwandten Schritt):
Sie werden dann mit einer (ziemlich) realistisch aussehenden inselförmigen Maske zurückgelassen, die Ihren Zwecken dienen würde. Sie können es als Maske für Ihren Perlin-Lärm verwenden oder anhand der Entfernung zum Meer Höhenwerte generieren und Lärm hinzufügen (obwohl dies unnötig erscheint).
quelle
Eine sehr einfache Methode besteht darin, einen inversen radialen oder sphärischen Gradienten mit Mittelpunkt in der Breite / 2 und Höhe / 2 zu erzeugen. Zum Maskieren möchten Sie den Gradienten vom Rauschen abziehen, anstatt ihn zu multiplizieren. Dies gibt Ihnen realistischere Küsten mit dem Nachteil, dass die Inseln nicht unbedingt miteinander verbunden sind.
Sie können den Unterschied zwischen dem Subtrahieren und Multiplizieren des Rauschens mit dem Gradienten hier sehen: http://www.vfxpedia.com/index.php?title=Tips_and_Techniques/Natural_Phenomena/Smoke
Wenn Sie sich nicht sicher sind, wie Sie einen radialen Verlauf erstellen sollen, können Sie dies als Startpunkt verwenden:
Vergessen Sie nicht, Ihr Gefälle auf die gleiche Höhe wie Ihre Höhenkarte zu skalieren, und Sie müssen Ihre Wasserlinie trotzdem irgendwie berücksichtigen.
Das Problem bei dieser Methode ist, dass Ihr Höhenfeld um den Mittelpunkt der Karte zentriert wird. Dieses Verfahren ist jedoch, sollten Sie mit dem Hinzufügen von Funktionen und machen Sie Ihre Landschaft vielfältiger , wie Sie beginnen können zusätzlich Funktionen Ihre Höhenkarte hinzuzufügen.
quelle
I zweiter Vorschlag von ollipekka: Was Sie tun möchten, ist eine geeignete Bias-Funktion von Ihrer Höhenkarte zu subtrahieren, so dass die Kanten garantiert unter Wasser sind.
Es gibt viele geeignete Bias-Funktionen, aber eine ziemlich einfache ist:
Dabei sind x und y die Koordinatenwerte, die so skaliert sind, dass sie zwischen 0 und 1 liegen. Diese Funktion nimmt den Wert 0 in der Kartenmitte an (bei x = y = 0,5) und tendiert an den Rändern zur Unendlichkeit. Durch Subtrahieren (skaliert mit einem geeigneten konstanten Faktor) von Ihrer Höhenkarte wird sichergestellt, dass die Höhenwerte auch in der Nähe der Kartenränder gegen unendlich tendieren. Wählen Sie einfach eine beliebige Höhe aus und nennen Sie sie den Meeresspiegel.
Wie ollipekka feststellt, garantiert dieser Ansatz nicht, dass die Insel zusammenhängend ist. Wenn Sie die Bias-Funktion jedoch mit einem relativ kleinen Skalierungsfaktor skalieren, sollte sie im mittleren Bereich der Karte größtenteils flach sein (was sich nicht wesentlich auf Ihr Gelände auswirkt). Eine signifikante Bias-Funktion wird nur in der Nähe der Ränder angezeigt. Auf diese Weise erhalten Sie eine quadratische, meist zusammenhängende Insel mit höchstens ein paar winzigen Unterinseln in der Nähe der Ränder.
Wenn Sie die Möglichkeit eines unverbundenen Geländes nicht stören, sollte Ihnen ein etwas größerer Skalierungsfaktor mehr Wasser und eine natürlichere Inselform verleihen. Durch Anpassen des Meeresspiegels und / oder des Maßstabs Ihrer ursprünglichen Höhenkarte können Sie auch die Größe und Form der resultierenden Insel (n) ändern.
quelle