Comment puis-je compenser / rétrécir un polygone triangulaire dans GLSL?

8

J'ai besoin de décaler tous les triangles (bleus), chacun indépendamment des autres, en utilisant le vertex-shader. Afin de manipuler le triangle dans son ensemble, j'ai créé des attributs personnalisés (vec3) pour chaque sommet (rouge) représentant les sommets voisins gauche (violet) et droit (vert). De cela, je dois dériver le point orange, équidistant (dans l'espace d'écran ) des deux bords adjacents. Avec trois de ces points orange dérivés de chaque triangle, le triangle traité (orange) est transmis au shader de fragment.

par opération de sommet triangles décalés

Idéalement, le triangle sera éliminé (comme dans le dos / non rendu) si les décalages annulent tout espace disponible dans le triangle, comme dans le deuxième triangle de la deuxième image.

J'utilise THREE.BufferGeometry () comme structure de données.

Voici une capture d'écran de l'effet que je vise:

entrez la description de l'image ici

Jackalope
la source
Pourriez-vous ajouter un peu plus sur le contexte plus large? Les triangles décalés doivent-ils rester attachés comme dans le maillage d'origine? «Abattu» signifie-t-il que le triangle d'origine est rejeté ou simplement que la compensation est abandonnée, laissant le triangle à sa taille d'origine?
trichoplax
1
Alors ... comment ça marche avec les maillages? Parce que dans un maillage, un sommet a plus de 2 voisins. Ou est-ce juste pour des triangles individuels?
Nicol Bolas
Mon implémentation est telle que tous les triangles sont disposés dans un tampon continu: [P1.x, P1.y, P1.z, P2.x, P2.y, P2.z ... Pn.x, Pn.y, Pn.z] avec des points voisins également disposés explicitement aux attributs. De cette façon, chaque sommet de chaque face peut être calculé et manipulé sans affecter les faces voisines. Nicol Bolas, oui, traitant chaque triangle séparément.
Jackalope
trichoplax - "Culled" signifie jeté, non rendu, comme dans une primitive simple face tournée vers l'arrière.
Jackalope
1
@Jackalope: " Vous semblez tous les deux suggérer que le GPU voit les visages comme" attachés "à d'autres visages. " C'est parce que, d'une manière générale, c'est vrai. La plupart des maillages n'ont pas simplement des triangles voisins qui utilisent des "attributs identiques"; ils réutilisent les mêmes sommets . Cela peut être par le biais de listes triangulaires qui utilisent le même index plusieurs fois, ou par le biais de bandes triangulaires, ou autre chose. Mais de manière générale, les mailles réutilisent les sommets voisins. Vos maillages ne changent pas, mais votre cas spécifique ne change pas le cas général. C'est pourquoi j'ai demandé des éclaircissements.
Nicol Bolas

Réponses:

9

Étant donné le triangle ▲ ABC, nous divisons l'angle ∠BAC avec la droite AD, dérivé du théorème de l' angle bissecteur :

BA / BD = CA / CD Le Diagramme d'encart triangulaire point E représente notre position raffinée objective sur le triangle incrusté résultant souhaité. Comme il repose sur la bissectrice d'angle AD, il est équidistant des côtés BA & CA, formant des triangles rectangles identiques ▲ AFE & ▲ AGE. Nous pouvons maintenant utiliser Sine for Right Triangles pour trouver la longueur de AE:

AE = EG / Sin (∠EAG)

C'est tout le calcul dont nous avons besoin, alors préparons du GLSL!

Nous commençons avec tous les attributs typiques: les matrices de position, normale et de transformation, mais comme le vertex shader ne fonctionne que sur un seul sommet, nous devons ajouter les sommets voisins comme attributs supplémentaires. De cette façon, chaque sommet trouvera son propre "point E", créant le triangle incrusté résultant. (Remarque: je ne les appelle pas "B" et "C" ici, car ils ne sont pas encore dans l'espace écran .)

    attribute vec3 left; //vertex to the left of this vertex
    attribute vec3 right; //vertex to the right of this vertex

En parlant d'espace d'écran, j'inclus également le rapport d'aspect de l'affichage (et en le rendant uniforme, au cas où la fenêtre serait redimensionnée.)

Après avoir préparé différentes normales pour le fragment shader et transformé le visage en espace de détourage, nous pouvons passer à l'application des mathématiques ci-dessus:

        attribute vec3 left; //vertex to the left of this vertex
        attribute vec3 right; //vertex to the right of this vertex
        uniform float aspect;
        varying vec3 vNormal;
        varying vec2 vUv;

        void main() {
            vNormal = normal;
            vUv = uv;

            mat4 xform= projectionMatrix * modelViewMatrix;
            vec4 A = xform * vec4( position, 1.0 );
            vec4 B = xform * vec4( left, 1.0 );
            vec4 C = xform * vec4( right, 1.0 );

            vec3 CB = C.xyz - B.xyz;
            vec2 BA = B.xy - A.xy;
            vec2 CA = C.xy - A.xy;
            float lengthBA = length(BA);
            float lengthCA = length(CA);
            float ratio = lengthBA / ( lengthBA + lengthCA );
            vec3 D = B.xyz + ratio * CB.xyz;
            vec3 AD = D - A.xyz;
            vec3 bisect = normalize(AD);

            float theta = acos( dot(BA, CA) / (lengthBA * lengthCA) ) / 2.0;
            float AE = 1.0/(sin(theta)*aspect);
            newPos.z += AE/length(AD) * (D.z - A.z);
            newPos.x += bisect.x*AE;
            newPos.y += bisect.y*AE;

            gl_Position = newPos;
        }

Ce code nous donne les résultats ci-dessous.

Capture d'écran

Remarque, il y a quelques cas de bord liés à des triangles presque triés sur la face arrière qui sont retournés par ce processus, et j'ai commencé à traiter cela dans le code, mais j'ai décidé de simplement éviter ces cas pour l'instant. Je vais peut-être y revenir lorsque j'aurai terminé ce projet.

Jackalope
la source
1
Beau travail pour comprendre cela! Vraiment comme la description mathématique au début.
user1118321
0

Ceci peut être réalisé sans fonctions trigonométriques en réduisant l' échelle du triangle.

incircle()calcule le cercle du triangle formé par les sommets A,B,C, il renvoie le centre et le rayon comme vec4. Les sommets X=A,B,Csont ensuite déplacés vers l'intérieur par la fraction de leur distance au centre incircle ( Q-X) qui est égal au rapport de la marge souhaitée au rayon incircle ( m/Q.w).

vec4 incircle(vec3 A, vec3 B, vec3 C) {
    float a = length(B - C), b = length(C - A), c = length(A - B);
    float abc = a + b + c;
    // http://mathworld.wolfram.com/Incenter.html
    vec3 I = (a * A + b * B + c * C) / abc;
    // http://mathworld.wolfram.com/Inradius.html
    float r = 0.5
            * sqrt((-a + b + c) * (a - b + c) * (a + b - c) / abc);
    return vec4(I, r);
}

vec3 A,B,C; // vertices
float m; // margin
vec4 Q = incircle(A,B,C);
A += clamp(m / Q.w, 0.0, 1.0) * (Q.xyz - A);
B += clamp(m / Q.w, 0.0, 1.0) * (Q.xyz - B);
C += clamp(m / Q.w, 0.0, 1.0) * (Q.xyz - C);
Adam
la source
Très intéressant, Adam! Je n'avais pas entendu parler de cette fonction.
Jackalope