Verrauschte Kanten, die Kanten zwischen Flächen über den Fragment-Shader glätten

8

Ich habe ein generiertes Gelände mit sechseckiger Geometrie, wie im folgenden Screenshot gezeigt:

Geben Sie hier die Bildbeschreibung ein

Ich generiere dann Biome, aber wie Sie sehen können, sind die Grenzen zwischen ihnen wirklich hässlich und gerade. Um diesen hexagonalen Ursprung zu verbergen, müsste ich die Grenzen zwischen den Biomen glätten. So sieht es jetzt im Drahtmodell mit echten dreisprachigen Flächen aus:

Geben Sie hier die Bildbeschreibung ein

Was ich anstrebe, ist eher so etwas:

Geben Sie hier die Bildbeschreibung ein

Jeder Scheitelpunkt hat ein Attibut, das den Biomtyp enthält. Ich kann den Scheitelpunkten am Rand zwischen zwei Biomes auch spezielle Attribute hinzufügen, aber ich scheine einfach nicht in der Lage zu sein, dies im Shader-Code zu erreichen, offensichtlich Rauschen ist hier beteiligt, aber wie mache ich es kontinuierlich über mehrere Gesichter und die gesamte Grenze mehrerer Biome?

Ich rendere mit WebGL mit THREE.js

user1617735
quelle
Haben Sie MSAA ausprobiert?
Milo Lu
@Milo Hast du versucht, die Frage zu lesen?
Bálint
Wie senden Sie die Biominformationen an den Shader?
Bálint
@ Bálint Biome Im Moment übergebe ich Farbe über das Vertex-Attribut. Wenn ich tatsächlich Farben aus echten Texturen abtasten möchte, übergebe ich anstelle einfacher Farben den Biomtyp als Ganzzahl.
user1617735
Vertex-Attribute können nicht wie gewünscht verwendet werden. Wenn Sie zu Texturen wechseln, wird dies viel einfacher. Laden Sie die Biominformationen als Textur hoch, damit Sie die nahe gelegenen Biome erhalten können
Bálint

Antworten:

9

Andere Antworten hier schlagen vor, eine Textur zu verwenden. Hier ist eine Technik, bei der keine Texturen verwendet werden.

Sie möchten, dass die Grenzen zwischen Sechsecken interessant sind. Es ist einfacher, interessante Grenzen zu setzen, wenn Sie sie in die Mitte des Zeichens verschieben. Anstatt die Kacheln direkt zu zeichnen, zeichnen Sie das „Dual“ der Kachel. Diese Technik wird als "Eckplättchen" bezeichnet ( hier und hier und hier ). Das Dual eines Sechsecks ist ein Dreieck, also würden wir diese Dreiecke anstelle der Sechsecke zeichnen:

Dreieck Duals eines Sechsecks

Die Grenzen zwischen den Sechsecken befinden sich jetzt in der Mitte der gerenderten Dreiecke, sodass wir interessantere Dinge mit ihnen machen können. Bonus: Sie müssen nur zwei Dreiecke pro Sechseck zeichnen , anstatt sechs (oder vierundzwanzig, wie Sie es jetzt tun).

In jedem dieser Dreiecke soll der Fragment-Shader die Sechsecke zeichnen. Wir können das mit Schwerpunktkoordinaten tun . Setzen Sie (1,0,0), (0,1,0) und (0,0,1) an jeden Scheitelpunkt des Dreiecks. Innerhalb des Dreiecks werden diese Koordinaten interpoliert. Der Fragment-Shader empfängt (a, b, c) und kann sehen, welcher Wert am größten ist - das sagt uns, welches der drei Sechsecke an dieser Stelle gezeichnet werden soll.

float max_n = max(barycentric.r, max(barycentric.g, barycentric.b)); if (max_n == barycentric.r) { color = v_color0; } else if (max_n == barycentric.g) { color = v_color1; } else if (max_n == barycentric.b) { color = v_color2; }

Das ist für gerade Linien.

Wenn Sie verrauschte Kanten wünschen, können Sie den Schwerpunktkoordinaten Rauschen hinzufügen:

Sechseck mit lauten Kanten

Wenn Sie mit der Amplitude Wellenlänge / Frequenz des Rauschens spielen, können Sie einige coole Effekte erzielen:

Sechseck mit noch lauteren Kanten

Sie müssen mit dem Rauschen vorsichtig sein und sicherstellen, dass es über Dreiecksgrenzen hinweg konsistent ist. Eine Möglichkeit, dies zu tun, besteht darin, eine Hex-ID einzugeben und diese als Startwert für jeden der drei Rauschwerte zu verwenden, die zu den Schwerpunktkoordinaten hinzugefügt werden.

Ich machte eine interaktive Demo hier . (Für die Demo habe ich weder die Hex-ID noch einige andere Dinge implementiert, die Sie möglicherweise benötigen, wenn Sie diese Arbeit in einem echten Projekt ausführen möchten - es ist nur eine schnelle und schmutzige Demo.)

amitp
quelle
Das ist erstklassiges Antwortmaterial. Hutspitze
Quentin
Gute Antwort! Subtile Korrektur: Regelmäßige Polygone, einschließlich Sechsecke, sind selbstdual. Allerdings Mosaike von Dreiecken und Sechsecken sind duals voneinander, wie Ihre Antwort zeigt.
Erik Foss
0

Ich bin sicher, dass "mit einem Bildalgorithmus" gelöst werden könnte, aber wenn ich es wäre, würde ich es wahrscheinlich mit Texturen lösen. Ich würde sechseckige Texturen erstellen, sie alle in einen Texturatlas einfügen, dann für jedes Sechseck die Nachbarn betrachten und entscheiden, welche Textur angewendet werden soll.

Die Texturen müssten Versionen für jeden Geländetyp sowie Versionen für jeden Übergangstyp enthalten.

Dies ähnelt der Anzahl der kachelbasierten Systeme im Gelände. Hier ist ein Beispiel aus 2D-Spielen .

Eine andere Möglichkeit wäre, einfach Ihre Texturen für Ihre verschiedenen Geländearten (Wasser, Schnee, Schmutz, Gras) zu haben und jedem Scheitelpunkt des Sechsecks Mischmengen hinzuzufügen, um zu entscheiden, wie sie gemischt werden sollen.

Dieser Artikel zeigt die Idee, Geländetexturen zu mischen. Ich schlage nicht vor, ihrer Implementierung zu folgen, aber es zeigt die Idee.

gman
quelle
Nun, in meinem Fall ist es nicht einfach, Texturen vorzubereiten, da Sechsecke nicht einheitlich sind, unterscheiden sich Form und Größe über die gesamte Karte. Leider ist das der Preis, den ich zahlen muss, um einen Planeten zu haben, nicht nur eine flache Karte. Auch der Artikel sieht sehr interessant aus, danke. Obwohl ich in meinem Fall das Gelände von oben rendere, wahrscheinlich aus einer Höhe, in der Sie zum Beispiel keinen einzigen Baum sehen können und ganze Waldvorsprünge wie eine Greem-Masse aussehen.
user1617735
0

Rendern Sie zuerst Ihre Biome zu einer Textur. Ordnen Sie die Dreiecke Texcoords zu. Sie können dies mit einer Mercator-Projektion oder besser mit einer Würfelkarte tun . Führen Sie nun im Fragment-Shader Folgendes aus:

// frequency and magnitude of the noise in texels.
uniform float frequency;
uniform float magnitude;

// This function should just look like random smooth noise in x,y
// This one isn't great, but you can experiment.
vec2 noise(vec3 vertexPos) {
    return vec2(sin(vertexPos.x * frequency + vertexPos.y * frequency) * magnitude, 
                cos(vertexPos.y * frequency * 2 + vertexPos.z * frequency) * magnitude);
}

// Sample the texture and preturb it with noise based on the world
// position of the fragment.
vec4 frag(vec2 texCoord, vec3 fragmentPos) {
   return texture(mySampler, texCoord + noise(fragmentPos));
}

Wo noiseist eine Pseudozufallsfunktion (unter Verwendung von beispielsweise Sinuskurven) an der 3D-Position des Scheitelpunkts im Modellraum, die einen verrauschten Versatz zur Texturkoordinate zurückgibt. Probieren Sie die Textur mit GL_NEAREST, um scharfe Ränder zu erhalten.

mklingen
quelle