Bords bruyants, lissant les bords entre les faces via le fragment shader

8

J'ai un terrain généré, avec une géométrie hexagonale, selon la capture d'écran ci-dessous:

entrez la description de l'image ici

Je génère ensuite des biomes, mais comme vous pouvez le voir, les frontières entre eux sont vraiment laides et droites. Pour cacher cette origine hexagonale, il me faudrait lisser les frontières entre les biomes. Voici à quoi cela ressemble maintenant en filaire avec de vraies faces tringulaires:

entrez la description de l'image ici

Ce que je vise, c'est quelque chose de plus comme ceci:

entrez la description de l'image ici

Chaque sommet a un attribut qui contient le type de biome, je peux également ajouter des attributs spéciaux aux sommets sur le bord entre deux biomes, mais je ne semble pas être en mesure de comprendre comment retirer cela dans le code du shader, évidemment du bruit est impliqué ici, mais comment puis-je le rendre continu sur plusieurs faces et sur toute la frontière de plusieurs biomes?

Je rend avec WebGL en utilisant THREE.js

user1617735
la source
Avez-vous essayé MSAA?
Milo Lu
@Milo Avez-vous essayé de lire la question?
Bálint
Comment envoyer les informations du biome au shader?
Bálint
@ Bálint Biome En ce moment, je passe la couleur via l'attribut vertex, quand je vais réellement échantillonner les couleurs de textures réelles, au lieu de couleurs simples, je passe le type de biome comme entier.
user1617735
Les attributs de sommet ne peuvent pas être utilisés comme vous le souhaitez, si vous passez aux textures, cela sera beaucoup plus facile. Téléchargez les informations du biome sous forme de texture, afin que vous puissiez obtenir les biomes à proximité
Bálint

Réponses:

9

D'autres réponses suggèrent ici l'utilisation d'une texture. Voici une technique qui n'utilise pas de textures.

Vous voulez que les frontières entre les hexagones soient intéressantes. Il est plus facile de créer des limites intéressantes lorsque vous les déplacez au centre de ce que vous dessinez. Au lieu de dessiner les tuiles directement, vous dessinez le "double" de la tuile. Cette technique est appelée «tuiles d'angle» ( ici et ici et ici ). Le dual d'un hexagone est un triangle, donc nous dessinerions ces triangles au lieu des hexagones:

triangle duals d'un hexagone

Les frontières entre les hexagones sont maintenant au milieu des triangles rendus, ce qui nous permettra de faire des choses plus intéressantes avec eux. Bonus: il vous suffit de dessiner deux triangles par hexagone, au lieu de six (ou vingt-quatre comme vous le faites actuellement).

À l'intérieur de chacun de ces triangles, nous voulons que le fragment shader dessine les hexagones. Nous pouvons le faire avec des coordonnées barycentriques . Mettez (1,0,0), (0,1,0) et (0,0,1) à chaque sommet du triangle. À l'intérieur du triangle, ces coordonnées seront interpolées. Le fragment shader recevra (a, b, c) et peut regarder pour voir quelle valeur est la plus grande - cela nous indiquera lequel des trois hexagones devrait être dessiné à ce point.

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; }

C'est pour les lignes droites.

Si vous voulez des bords bruyants, vous pouvez ajouter du bruit aux coordonnées barycentriques:

hexagone avec bords bruyants

En jouant avec la longueur d'onde / fréquence d'amplitude du bruit, vous pouvez obtenir des effets sympas:

hexagone avec bords encore plus bruyants

Vous devez être prudent avec le bruit, en vous assurant qu'il est cohérent à travers les limites du triangle. Une façon de le faire est de passer un identifiant hexadécimal et de l'utiliser comme valeur de départ pour chacune des trois valeurs de bruit ajoutées aux coordonnées barycentriques.

J'ai fait une démo interactive ici . (Pour la démo, je n'ai pas implémenté l'identifiant hexadécimal ou certaines des autres choses dont vous pourriez avoir besoin si vous faisiez ce travail dans un vrai projet - c'est juste une démo rapide et sale)

amitp
la source
Voilà du matériel de réponse de première qualité. Pointe du chapeau
Quentin
Très bonne réponse! Correction subtile: les polygones réguliers, y compris les hexagones, sont auto-doubles. Cependant, les pavages de triangles et d'hexagones sont des duels les uns des autres, comme l'illustre votre réponse.
Erik Foss
0

Je suis sûr que "pourrait" être résolu avec un algorithme d'image mais si c'était moi, je le résoudrais probablement avec des textures. Je ferais des textures hexagonales, les mettrais toutes dans un atlas de texture, puis pour chaque hexagone, je regarderais ses voisins et déciderais quelle texture appliquer.

Les textures devraient avoir des versions pour chaque type de terrain et des versions pour chaque type de transition.

Ceci est similaire au nombre de systèmes basés sur des tuiles qui font du terrain. Voici un exemple de jeux 2D .

Une autre possibilité serait d'avoir juste vos textures pour vos différents types de terrain (eau, neige, terre, herbe) et d'ajouter des quantités de mélange à chaque sommet d'hexagone pour décider comment les mélanger.

Cet article montre l'idée de mélanger des textures de terrain. Je ne suggère pas de suivre leur mise en œuvre mais cela montre l'idée.

gman
la source
Eh bien, dans mon cas, il n'est pas simple de préparer des textures, car les hexagones ne sont pas uniformes, la forme et la taille diffèrent sur toute la carte. Malheureusement, c'est le prix que je dois payer pour avoir une planète, pas seulement une carte plate. L'article semble également très intéressant, merci. Bien que dans mon cas, je rende le terrain d'en haut, probablement à partir d'une altitude où, par exemple, vous ne pouvez pas voir un seul arbre et des saillies de forêt entières ressemblent à une masse verte.
user1617735
0

Tout d'abord, donnez à vos biomes une texture. Mappez les triangles aux texcoords. Vous pouvez le faire en utilisant une projection de mercator ou, mieux, une carte de cube . Maintenant, dans le shader de fragment, faites quelque chose comme ceci:

// 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));
}

où se noisetrouve une fonction pseudo-aléatoire (utilisant, disons, des sinusoïdes) sur la position 3D du sommet dans l'espace objet qui renvoie un décalage bruyant à la coordonnée de la texture. Échantillonnez la texture en utilisant GL_NEARESTpour garder des bordures nettes.

mklingen
la source