Bagaimana saya bisa mengimbangi / mengecilkan poligon segitiga di GLSL?

8

Saya perlu mengimbangi semua segitiga (biru), masing-masing secara independen dari yang lain, menggunakan vertex-shader. Untuk memanipulasi segitiga secara keseluruhan, saya telah membuat atribut khusus (vec3) untuk setiap simpul (merah) yang mewakili simpul tetangga sebelah kiri (ungu) dan kanan (hijau). Dari ini, saya perlu menurunkan titik oranye, berjarak sama (dalam ruang layar ) dari kedua sisi yang berdampingan. Dengan tiga titik oranye tersebut berasal dari setiap segitiga, segitiga yang diproses (oranye) diteruskan ke shader fragmen.

per operasi simpul mengimbangi segitiga

Idealnya, segitiga akan dimusnahkan (seperti di backfacing / tidak diberikan) jika offset meniadakan ruang yang tersedia dalam segitiga, seperti pada segitiga kedua di gambar kedua.

Saya menggunakan THREE.BufferGeometry () sebagai struktur data saya.

Ini adalah cuplikan layar dari efek yang saya inginkan:

masukkan deskripsi gambar di sini

Jackalope
sumber
Bisakah Anda menambahkan sedikit lebih banyak tentang konteks yang lebih luas? Apakah segitiga offset tetap terpasang seperti pada mesh asli? Apakah "dimusnahkan" berarti segitiga asli dibuang, atau hanya bahwa penyeimbangan ditinggalkan, meninggalkan segitiga pada ukuran aslinya?
trichoplax
1
Jadi ... bagaimana cara kerjanya dengan jerat? Karena dalam mesh, sebuah simpul memiliki lebih dari 2 tetangga. Atau ini hanya untuk segitiga individu?
Nicol Bolas
Implementasi saya sedemikian rupa sehingga semua segitiga diletakkan dalam buffer berkelanjutan: [P1.x, P1.y, P1.z, P2.x, P2.y, P2.z ... Pn.x, Pn.y, Pn.z] dengan titik-titik tetangga juga ditata secara eksplisit di atribut. Dengan cara ini, setiap simpul dari setiap wajah dapat dihitung dan dimanipulasi tanpa memengaruhi wajah tetangga. Nicol Bolas, ya, berurusan dengan setiap segitiga secara terpisah.
Jackalope
trichoplax - "Culled" berarti dibuang, tidak diberikan, seperti pada primitif satu sisi yang menghadap ke belakang.
Jackalope
1
@Jackalope: " Anda berdua sepertinya menyarankan bahwa GPU melihat wajah sebagai" tertambat "ke wajah lain. " Itu karena, secara umum, ini benar. Sebagian besar jerat tidak hanya memiliki segitiga tetangga menggunakan "atribut identik"; mereka menggunakan kembali simpul yang sama . Ini bisa melalui daftar segitiga yang menggunakan indeks yang sama beberapa kali, atau melalui strip segitiga, atau apa pun. Tapi secara umum, jerat menggunakan kembali simpul tetangga. Jala Anda tidak, tetapi kasing khusus Anda tidak mengubah kasing umum. Itu sebabnya saya meminta klarifikasi.
Nicol Bolas

Jawaban:

9

Diberi segitiga ▲ ABC, kami membagi dua sudut ∠BAC dengan garis AD, diturunkan dengan Teorema Sudut Bisektor :

BA / BD = CA / CD Diagram Inset Segitiga Point E mewakili posisi objektif kami yang disempurnakan pada segitiga inset yang diinginkan. Karena terletak pada sudut garis-garis AD, ia berjarak sama dari sisi-sisi BA & CA, membentuk segitiga siku-siku yang identik ▲ AFE & ▲ AGE. Kita sekarang dapat menggunakan Sine untuk Segitiga Kanan untuk menemukan panjang AE:

AE = EG / Sin (∠EAG)

Itu semua matematika yang kita butuhkan, jadi mari kita memasak beberapa GLSL!

Kita mulai dengan semua atribut khas: posisi, normal, dan matriks transformasi, tetapi karena vertex shader hanya bekerja pada satu titik, kita perlu menambahkan simpul tetangga sebagai atribut tambahan. Dengan cara ini, setiap titik akan menemukan "titik E" sendiri, menciptakan segitiga inset yang dihasilkan. (Catatan: Saya tidak memanggil mereka "B" & "C" di sini, karena mereka belum di ruang layar .)

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

Berbicara tentang ruang layar, saya juga menyertakan rasio aspek tampilan, (dan menjadikannya seragam, jika jendelanya diubah ukurannya.)

Setelah menyiapkan berbagai normal untuk shader fragmen, dan mengubah wajah menjadi ruang kliping, kita bisa turun ke bisnis menerapkan matematika di atas:

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

Kode ini memberi kita hasil di bawah ini.

Tangkapan Layar

Catatan, ada beberapa kasus tepi yang berkaitan dengan segitiga hampir -belakang-culled dibalik oleh proses ini, dan saya mulai membahas ini dalam kode, namun memutuskan untuk hanya menghindari kasus-kasus ini untuk saat ini. Mungkin saya akan mengunjungi lagi ketika saya menyelesaikan proyek ini.

Jackalope
sumber
1
Kerja bagus mencari tahu ini! Sangat suka dengan deskripsi matematika di awal.
user1118321
0

Ini dapat dicapai tanpa fungsi trigonometri dengan mengurangi incircle pada segitiga.

incircle()menghitung incircle dari segitiga yang dibentuk oleh simpul A,B,C, ia mengembalikan pusat dan jari-jari sebagai vec4. Verteks X=A,B,Ckemudian dipindahkan ke dalam oleh fraksi jaraknya ke pusat incircle ( Q-X) yang sama dengan rasio margin yang diinginkan dengan radius 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
sumber
Sangat menarik, Adam! Saya belum pernah mendengar tentang fungsi ini.
Jackalope