Comment puis-je réparer des artefacts de mappage UV en zigzag sur un maillage généré qui s'effile?

10

Je crée un maillage procédural basé sur une courbe et la taille du maillage diminue sur toute la courbe comme vous le voyez ci-dessous.

entrez la description de l'image ici

Et le problème est que les UV sont zigzagués lorsque la taille change (cela fonctionne parfaitement lorsque la taille du maillage est la même tout au long de la courbe).

entrez la description de l'image ici

 Vertices.Add(R);
 Vertices.Add(L);
 UVs.Add(new Vector2(0f, (float)id / count));
 UVs.Add(new Vector2(1f, (float)id / count));
 start = Vertices.Count - 4;
          Triangles.Add(start + 0);
                 Triangles.Add(start + 2);
                 Triangles.Add(start + 1);
                 Triangles.Add(start + 1);
                 Triangles.Add(start + 2);
                 Triangles.Add(start + 3);

 mesh.vertices = Vertices.ToArray();
         //mesh.normals = normales;
         mesh.uv = UVs.ToArray();
         mesh.triangles = Triangles.ToArray();

Comment puis-je réparer cela?

fkkcloud
la source
Quels sont idet count? Que fait UVs.ToArray()-il? Comment téléchargez-vous les sommets et les coordonnées de texture sur la carte?
user1118321
@ user1118321 à partir du contexte, idest l'indice du segment actuel sur toutes les barres transversales le long de la bande, d'où il y counten a au total. List<T>.ToArray()renvoie un tableau typé de toutes les entrées de la liste (alors ici, a Vector2[]). Ces tampons de données sont mis à la disposition du système de rendu en les affectant à l' Meshinstance meshdans les dernières lignes. Mais rien de tout cela n'est la partie particulièrement intéressante du code: comment les points de sommet sont choisis. Ces détails seraient plus utiles à voir. ;)
DMGregory
C'était mon hypothèse, mais j'ai demandé des éclaircissements car parfois les hypothèses sont fausses. J'ai regardé quelque chose comme ça dans mon propre code dans le passé pour réaliser que cela countsignifiait en fait des sommets au lieu de polygones, ou vice-versa. S'assurer que tout ce que nous pensons se passe vraiment.
user1118321

Réponses:

13

La cartographie UV typique est ce qu'on appelle une transformation affine . Cela signifie que le mappage de chaque triangle entre l'espace 3D et l'espace de texture peut inclure la rotation, la translation, la mise à l'échelle / le squash et l'inclinaison (c'est-à-dire tout ce que nous pouvons faire avec une multiplication matricielle homogène)

La chose à propos des transformations affines est qu'elles sont uniformes sur tout leur domaine - la rotation, la translation, l'échelle et l'inclinaison que nous appliquons à la texture près du sommet A sont les mêmes que celles que nous appliquons près du sommet B, dans n'importe quel triangle. Les lignes parallèles dans un espace seront mappées à des lignes parallèles dans l'autre, jamais convergentes / divergentes.

Mais la réduction progressive que vous essayez d'appliquer n'est pas uniforme - elle met en correspondance des lignes parallèles dans la texture avec des lignes convergentes sur le maillage. Cela signifie que l'échelle de la texture mesurée à travers la bande change continuellement à mesure que nous avançons dans la bande. C'est plus que ce que les transformations affines de la cartographie UV 2D peuvent représenter avec précision: l'interpolation des coordonnées UV 2D entre les sommets adjacents obtiendra une échelle cohérente sur tout le bord, même le bord diagonal qui devrait rétrécir à mesure qu'il se déplace le long de la bande. Ce décalage est ce qui crée ce zigzag tordu.

Ce problème survient chaque fois que nous voulons mapper un rectangle à un trapèze - des côtés parallèles à des côtés convergents: il n'y a tout simplement pas de transformation affine qui le fait, nous devons donc l'approcher par morceaux, ce qui conduit à des coutures visibles.

Dans la plupart des cas, vous pouvez minimiser l'effet en ajoutant plus de géométrie. L'augmentation du nombre de subdivisions sur la longueur de la bande et la division de la bande en deux segments ou plus sur toute sa largeur, avec les diagonales des triangles disposées en chevrons, peuvent rendre l'effet beaucoup moins perceptible. Il sera toujours présent dans une certaine mesure tant que nous utiliserons des transformations affines.

Mais il existe un moyen de contourner cela. Nous pouvons utiliser la même astuce que nous utilisons pour le rendu 3D pour dessiner des trapèzes en perspective étant donné les murs et les sols rectangulaires: nous utilisons des coordonnées projectives !

Texturation affine: Exemple de texturation affine normale avec des artefacts en zig-zag

Texturation projective: Exemple de textring projectif corrigeant les artefacts

Pour ce faire, nous devons ajouter une troisième coordonnée uv (uvw) et modifier nos shaders.

Étant donné un facteur d'échelle à chaque point (disons, égal à la largeur de votre bande à cet endroit), vous pouvez construire la coordonnée UV projective 3D à partir de votre coordonnée UV 2D régulière de cette façon:

Vector3 uv3 = ((Vector3)uv2) * scale;
uv3.z = scale;

Pour appliquer ces coordonnées uvw 3D à votre maillage, vous devrez utiliser le Mesh.SetUVs de surcharge Vector3 (canal int, liste uvs)

Et assurez-vous de changer la structure d'entrée de votre shader pour vous attendre à une coordonnée de texture 3D (affichée ici en utilisant le shader non éclairé par défaut):

struct appdata
{
    float4 vertex : POSITION;
    float3 uv : TEXCOORD0; // Change float2 to float 3.
};
// Also do this for the uv sent from the vertex shader to the fragment shader.

Vous devrez également découper la macro TRANSFORM_TEX dans le vertex shader, car elle attend un uv 2D:

// o.uv = TRANSFORM_TEX(v.uv, _MainTex);
o.uv = v.uv;
// If you define a float4 with the special name _MainTex_ST, 
// you can get the same effect the macro had by doing this:
o.uv.xy = o.uv.xy * _MainTex_ST.xy + _MainTex_ST.zw;

Enfin, pour revenir à une coordonnée de texture 2D pour l'échantillonnage de texture, il vous suffit de diviser par la troisième coordonnée dans votre shader de fragment :

float2 uv = i.uv.xy / i.uv.z;

Puisque nous avons fait cette coordonnée uvw 3D à partir de notre coordonnée 2D souhaitée en multipliant par le même nombre, les deux opérations s'annulent et nous revenons à notre coordonnée 2D souhaitée d'origine, mais maintenant avec une interpolation non linéaire entre les sommets. :RÉ

Il est important de faire cette division par fragment et non dans le vertex shader. Si cela se fait par sommet, nous revenons à l'interpolation linéaire des coordonnées résultantes le long de chaque arête, et nous avons perdu la non-linéarité que nous essayions d'introduire avec la coordonnée projective!

DMGregory
la source
Salut, je me bats avec le même problème depuis un certain temps maintenant, et cela semble être précisément ce qui m'arrive. La seule différence est que je travaille en trois et non en unité. Nous avons réussi à résoudre le problème en augmentant le nombre de triangles, mais nous aimerions quand même essayer la texturation projective. Avez-vous une idée de comment même commencer à implémenter cela en trois js? Je vous remercie!
Dživo Jelić
1
De la même manière. Vous avez besoin d'une troisième coordonnée de texture pour stocker le diviseur, puis vous effectuez la division dans le fragment shader. Si vous avez eu du mal à appliquer cela à votre cas, poser une nouvelle question vous permettra d'inclure un exemple de code, montrant comment vous dessinez votre maillage jusqu'à présent, sur lequel les réponses peuvent s'appuyer.
DMGregory
Dois-je stocker le diviseur en tant que troisième coordonnée (et à cause de cette structure de changement et découper la macro TRANSFORM_TEX, ce que je ne sais pas faire en trois j) ou puis-je simplement passer le diviseur en tant qu'attribut au vertex shader puis à partir de là en tant que variations pour fragmenter le shader où il pourrait être utilisé pour la division?
Dživo Jelić
TRANSFORM_TEX est une macro Unity. Vous n'avez pas ça dans three.js donc il n'y a rien à couper. Tant que les coordonnées uv interpolées et les diviseurs atteignent le shader du fragment, peu importe comment vous les avez obtenus. Avez-vous rencontré des problèmes pour le fournir en tant qu'attribut / varier jusqu'à présent?
DMGregory
Je n'ai pas encore essayé car je ne voulais pas perdre de temps jusqu'à ce que je vérifie que c'est possible. Juste une dernière question, le diviseur sera également interpolé lorsqu'il atteindra le fragment shader, n'est-ce pas? Cela signifie que sur un pixel entre deux sommets, ce sera une valeur interpolée et non une valeur d'origine? J'ai du mal à comprendre pourquoi multiplier les UV puis les diviser aide, mais je vais vous croire sur parole et l'essayer. : légèrement_smiling_face: Merci beaucoup de votre aide!
Dživo Jelić