Come posso compensare / ridurre un poligono triangolare in GLSL?


8

Ho bisogno di compensare tutti i triangoli (blu), ciascuno indipendentemente dagli altri, usando il vertice-shader. Per manipolare il triangolo nel suo insieme, ho creato attributi personalizzati (vec3) per ciascun vertice (rosso) che rappresenta i vertici vicini a sinistra (viola) e a destra (verde). Da questo, ho bisogno di derivare il punto arancione, equidistante (nello spazio dello schermo ) da entrambi i bordi adiacenti. Con tre di questi punti arancioni derivati ​​da ciascun triangolo, il triangolo elaborato (arancione) viene passato allo shader di frammenti.

per operazione vertice triangoli offset

Idealmente, il triangolo verrà eliminato (come nel backfacing / non renderizzato) se gli offset negano lo spazio disponibile all'interno del triangolo, come nel secondo triangolo nella seconda immagine.

Sto usando THREE.BufferGeometry () come struttura dei miei dati.

Ecco una schermata dell'effetto a cui sto puntando:

inserisci qui la descrizione dell'immagine


Potresti aggiungere qualcosa in più sul contesto più ampio? I triangoli offset devono rimanere attaccati come nella mesh originale? "Abbattuto" significa che il triangolo originale viene scartato o semplicemente che la compensazione viene abbandonata, lasciando il triangolo alla sua dimensione originale?
trichoplax,

1
Quindi ... come funziona con le mesh? Perché in una mesh, un vertice ha più di 2 vicini. O è solo per singoli triangoli?
Nicol Bolas,

La mia implementazione è tale che tutti i triangoli sono disposti in un buffer continuo: [P1.x, P1.y, P1.z, P2.x, P2.y, P2.z ... Pn.x, Pn.y, Pn.z] con punti vicini anche esplicitamente attribuiti agli attributi. In questo modo, ogni vertice di ciascuna faccia può essere calcolato e manipolato senza influire sulle facce vicine. Nicol Bolas, sì, si occupa di ogni triangolo separatamente.
Jackalope,

trichoplax - "Abbattuto" significa espulso, non reso, come in una primitiva fronte retro a faccia singola.
Jackalope,

1
@Jackalope: " Entrambi sembrano suggerire che la GPU vede i volti come" legati "ad altri volti. " Questo perché, in generale, questo è vero. La maggior parte delle maglie non hanno semplicemente triangoli vicini che usano "attributi identici"; riutilizzano gli stessi vertici . Ciò potrebbe avvenire attraverso elenchi di triangoli che utilizzano lo stesso indice più volte o tramite strisce di triangoli o altro. Ma in generale, le mesh riutilizzano i vertici vicini. Le tue maglie non lo fanno, ma il tuo caso specifico non cambia il caso generale. Ecco perché ho chiesto chiarimenti.
Nicol Bolas,

Risposte:


9

Dato triangolo ▲ ABC, biseciamo l'angolo ∠BAC con la linea AD, derivata dal teorema della bisettrice angolare :

BA / BD = CA / CD Il Schema inserto triangolo punto E rappresenta la nostra posizione raffinata oggettiva sul triangolo dell'inserto risultante desiderato. Come si trova sulla bisettrice angolare AD, è equidistante dai lati BA e CA, formando identici triangoli retti ▲ AFE & ▲ AGE. Ora possiamo usare Sine for Right Triangles per trovare la lunghezza di AE:

AE = EG / Sin (∠EAG)

Questa è tutta la matematica di cui abbiamo bisogno, quindi prepariamo un po 'di GLSL!

Iniziamo con tutti gli attributi tipici: matrici di posizione, normali e di trasformazione, ma poiché lo shader di vertici funziona solo su un singolo vertice, è necessario aggiungere i vertici vicini come attributi aggiuntivi. In questo modo, ciascun vertice troverà il proprio "punto E", creando il triangolo dell'inserto risultante. (Nota: non li chiamo "B" e "C" qui, perché non sono ancora nello spazio dello schermo .)

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

Parlando di spazio sullo schermo, sto anche includendo le proporzioni del display (e rendendolo uniforme, nel caso in cui la finestra venga ridimensionata).

Dopo aver preparato varie normali per lo shader di frammenti e trasformato la faccia in spazio di ritaglio, possiamo dedicarci all'applicazione della matematica di cui sopra:

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

Questo codice ci fornisce i risultati seguenti.

Immagine dello schermo

Nota, ci sono alcuni casi limite che hanno a che fare con il ribaltamento di triangoli quasi abbattuti sul retro da questo processo, e ho iniziato ad occuparmene in codice, ma per ora ho deciso di evitare questi casi. Forse lo rivisiterò quando avrò finito questo progetto.


1
Bel lavoro per capirlo! Mi piace molto la descrizione matematica all'inizio.
user1118321

0

Ciò può essere ottenuto senza funzioni trigonometriche ridimensionando l' incircolo del triangolo.

incircle()calcola l'incircolo del triangolo formato dai vertici A,B,C, restituisce centro e raggio comevec4 . I vertici X=A,B,Cvengono quindi spostati verso l'interno della frazione della loro distanza dal centro del cerchio ( Q-X) che è uguale al rapporto tra margine desiderato e raggio del cerchio ( 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);

Molto interessante, Adam! Non avevo sentito parlare di questa funzione.
Jackalope
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.