Bagaimana saya bisa menemukan bola terbesar yang pas di dalam frustum?

12

Bagaimana Anda menemukan bola terbesar yang bisa Anda gambar dalam perspektif?

Dilihat dari atas, akan seperti ini:

masukkan deskripsi gambar di sini

Ditambahkan: pada frustum di sebelah kanan, saya telah menandai empat poin saya pikir kita tahu sesuatu tentang. Kita dapat memproyeksikan kedelapan sudut frusum, dan pusat-pusat ujung dekat dan jauh. Jadi kita tahu titik 1, 3 dan 4. Kita juga tahu bahwa titik 2 adalah jarak yang sama dari 3 seperti 4 dari 3. Jadi kita dapat menghitung titik terdekat pada baris 1 ke 4 ke titik 2 untuk mendapatkan pusat? Tetapi matematika dan kode yang sebenarnya luput dari saya.

Saya ingin menggambar model (yang kira-kira berbentuk bola dan yang saya miliki untuk bola miniball yang mengikat) sebesar mungkin.

Pembaruan: Saya sudah mencoba menerapkan pendekatan incircle-on-two-pesawat seperti yang disarankan oleh bobobobo dan Nathan Reed :

function getFrustumsInsphere(viewport,invMvpMatrix) {
    var midX = viewport[0]+viewport[2]/2,
        midY = viewport[1]+viewport[3]/2,
        centre = unproject(midX,midY,null,null,viewport,invMvpMatrix),
        incircle = function(a,b) {
            var c = ray_ray_closest_point_3(a,b);
            a = a[1]; // far clip plane
            b = b[1]; // far clip plane
            c = c[1]; // camera
            var A = vec3_length(vec3_sub(b,c)),
                B = vec3_length(vec3_sub(a,c)),
                C = vec3_length(vec3_sub(a,b)),
                P = 1/(A+B+C),
                x = ((A*a[0])+(B*a[1])+(C*a[2]))*P,
                y = ((A*b[0])+(B*b[1])+(C*b[2]))*P,
                z = ((A*c[0])+(B*c[1])+(C*c[2]))*P;
            c = [x,y,z]; // now the centre of the incircle
            c.push(vec3_length(vec3_sub(centre[1],c))); // add its radius
            return c;
        },
        left = unproject(viewport[0],midY,null,null,viewport,invMvpMatrix),
        right = unproject(viewport[2],midY,null,null,viewport,invMvpMatrix),
        horiz = incircle(left,right),
        top = unproject(midX,viewport[1],null,null,viewport,invMvpMatrix),
        bottom = unproject(midX,viewport[3],null,null,viewport,invMvpMatrix),
        vert = incircle(top,bottom);
    return horiz[3]<vert[3]? horiz: vert;
}

Aku akui aku menikmatinya; Saya mencoba mengadaptasi kode 2D dengan memperluasnya menjadi 3 dimensi. Itu tidak menghitung insphere dengan benar; titik tengah bola tampaknya berada di garis antara kamera dan kiri atas setiap kali, dan terlalu besar (atau terlalu dekat). Apakah ada kesalahan nyata dalam kode saya? Apakah pendekatannya, jika diperbaiki, berhasil?

Akan
sumber
Apakah bola harus sepenuhnya berada di sisi kemari dari pesawat jauh seperti pada gambar?
Mikael Högström
@ MikaelHögström Saya membayangkan mereka akan menjadi, untuk menjadi sebesar mungkin?
Will
Hmm saya kira itu tergantung pada tujuan Anda ... Jika Anda menggambar bola dengan setengah di luar pesawat jauh maka itu akan lebih besar tapi mungkin itu bertentangan dengan tujuan Anda?
Mikael Högström
@ MikaelHögström aha Saya mengerti pertanyaan Anda; ya saya ingin seluruh model ditarik, tidak ada pesawat yang memotongnya.
Will

Jawaban:

12

Saya akan berasumsi bahwa frustum Anda simetris, karena gambar Anda tampaknya menyarankan demikian. Ada tiga kendala (dua jika frustum Anda adalah 2D):

A. bola tidak bisa lebih besar dari jarak antara pesawat dekat dan jauh

Jika Djarak dekat-jauh, kendala pertama adalah:

R  D / 2

B. bola tidak bisa tumbuh lebih luas dari bidang samping

Sekarang untuk kendala lain, katakanlah αadalah setengah sudut frustum dan Lsetengah lebar dari pesawat jauh, seperti yang ditunjukkan dalam gambar ini:

frustum

Rumus pertama diberikan oleh trigonometri dalam segitiga. Yang kedua berasal dari jumlah sudut segitiga. Yang memberi kita batasan kedua:

R  L tan((π - 2α) / 4)

Jika frustum Anda adalah 3D, Anda akan memiliki batasan ketiga dengan nilai Ldan baru α.

Hasil akhir

The Rnilai yang Anda cari adalah mindari tiga batas.

Cara mendapatkan parameter

Jika Anda dapat memproyeksikan frustum dalam pandangan atau ruang dunia, Anda dapat menghitung L, D dan α dengan cara berikut, di mana Ptitik-titik itu dari bidang yang dekat, dan Qtitik-titik itu dari bidang yang jauh:

formula2

Panah berarti vektor, “.” adalah produk titik, dan || menunjukkan panjang vektor. Ganti Q2dengan Q3dan P2dengan P3untuk mendapatkan L dan α dalam dimensi vertikal.

sam hocevar
sumber
Bagaimana, dari frustum (dihitung dengan tidak memproyeksikan titik pandang untuk mendekat dan jauh), Anda menentukan bidang pandang? Dan dalam 3D hanya ada dua pilihan bukan tiga, kan? Upaya saya untuk memasukkan algoritme Anda ke dalam kode selalu memberi saya R. yang sangat besar
Will
@Saya akan menambahkan gambar kedua dengan formula yang mudah-mudahan akan membantu.
sam hocevar
2

Dalam 2D: anggap frustum sebagai segitiga (2D)

masukkan deskripsi gambar di sini

Anda kemudian ingin menemukan incircle of the triangle.

Sebagai masalah 3D, Anda perlu menemukan insphere dari piramida berbasis persegi.

Jika saya memiliki formula saya akan mencetaknya di sini, tetapi sayangnya, saya tidak tahu formula itu.

bobobobo
sumber
2
Mungkin cukup untuk menemukan incircle dari frustum vertikal atau horizontal dalam 2D, mana yang memiliki FOV lebih kecil, setidaknya untuk frustra "standar" (tidak dicukur atau apa pun).
Nathan Reed
1

The Biggest Sphere mungkin harus menyentuh bidang jauh (menggunakan istilah untuk pandangan-frustrasi di sini) tepat di tengah. Ini juga akan menyentuh bidang atas / bawah atau bidang kiri / kanan, tergantung pada sudut FoV mana yang lebih kecil. Saya harus mengatakan bahwa saya tidak memiliki bukti matematika aktual untuk asumsi-asumsi itu, tetapi mereka harus benar. Mungkin seseorang memiliki ide tentang cara membuktikannya.

Sphere dapat ditentukan oleh titik pusat dan jari-jarinya. Cx dan Cy sama dengan pusat farplane.

Cz dan jari-jari dapat diperoleh dengan menyelesaikan sistem persamaan berdasarkan asumsi yang tercantum di atas.

T adalah salah satu dari bidang bawah / atas atau bidang kiri / kanan (lihat di atas) dengan t1, t2 dan t3 sebagai vektor normal yang dinormalisasi dan t4 sebagai jarak dari asalnya. f adalah pusat dari pesawat jauh.

t1 * cx + t2 * cy + t3 * cz - t4 = r

-fz + cz = r

t1 * cx + t2 * cy + t3 * cz - t4 = -fz + cz

t1 * cx + t2 * cy + fz - t2 = + cz - t3 * cz

t1 * cx + t2 * cy - fz - t2 = cz * (1 - t3)

cz = (t1 * cx + t2 * cy - fz - t2) / (1 - t3)

r kemudian dihitung dengan memasukkan cz ke dalam ini: -fz + cz = r

Anda dapat memperoleh semua pesawat dari Proyeksi Matriks yang Anda gunakan. (Tidak ViewProjection dalam hal ini)

setelah itu Anda harus memindahkan bola ke ruang yang tepat: C '= terbalik (Lihat) * C

Luis W
sumber
1

Saya mencoba melakukan sesuatu yang serupa, dan dalam kasus saya, kecepatan lebih penting daripada akurasi selama bola tidak ada di luar batas frustrasi.

Jika Anda menghitung jarak terpendek di antara garis-garis (atau wajah dalam 3d), jarak terpendek yang ditemukan dapat digunakan sebagai diameter incircle / insphere yang terletak sepenuhnya di dalam frustum. Asal-usul incircle / insphere bisa saja merupakan rata-rata dari semua simpul (jumlah & pembagian). Ini akan sangat cepat dan juga berfungsi untuk semua jenis polyhedra cembung.

Satu-satunya kelemahan adalah bahwa lingkaran atau bola tidak harus menjadi incircle atau insphere terbesar yang mungkin. Untuk frustum dengan banyak volume dan satu tepi yang sangat pendek, lingkaran / bola akan berbagi ruang frustum jauh lebih sedikit daripada yang mungkin.

Ide lain

Jika Anda ingin insphere dari view-frustum 3D dan Anda memiliki matriks perspektif yang digunakan untuk membangun frustum ini, maka Anda bisa menggunakan matriks itu pada insphere unit kubus, dan itu harus menjadi tempat yang sempurna untuk frustum. (Diameter insphere kubus adalah panjang salah satu ujung kubus, pusatnya adalah tengah kubus yang merupakan rata-rata dari simpul kubus)

nol
sumber