Splines dalam grafik 3d, opsi paling otomatis

Sekitar sebulan yang lalu, saya mulai belajar Python dari buku Dawson dan bangun dalam proses menulis permainan saya di bawah pygame. TK sedemikian rupa sehingga tampak paling menjanjikan untuk membuat game dengan grafis pseudo-tiga dimensi dengan memasukkan permukaan spline 3d yang tersimpan menjadi sprite. Saya akan menulis tentang yang terakhir.

Jadi, ada poligon (paling mudah untuk bekerja dengan segi empat) di mana kami ingin meregangkan permukaan kubik sehingga mereka cocok bersama dengan sangat lancar - permukaan ini adalah splines.



Paling mudah menyajikan spline untuk satu poligon sebagai fungsi

g(u,v)=v cdotg1(u)+(1v) cdotg2(u)+u cdotg3(v)+(1u) cdotg4(v)(1)


Di sini

g1,g2,g3,g4


- polinomial kubik yang memenuhi beberapa kondisi batas (pada gambar di bawah ini - kurva hijau dan merah muda, dan kondisi turunan-awal - vektor lilac dan biru); u dan v bervariasi dari 0 hingga 1.



Dalam interpretasi ini, beberapa derajat hilang (produk dari 2 dan 3 derajat parameter), tetapi koefisien polinom dapat ditemukan dengan menetapkan hanya 12 vektor kondisi awal (4 titik poligon dan vektor turunan sehubungan dengan u dan v untuk setiap titik). Pada persimpangan, splines bertepatan jika kondisi awal yang sama ditetapkan untuk poligon tetangga (vektor turunan haruslah collinear, permukaan melewati titik-titik yang sama dari poligon).

Satu masalah - turunan dengan pernyataan masalah seperti itu di seluruh perbatasan mungkin tidak bersamaan - akan ada artefak kecil di persimpangan. Anda dapat memikirkan 4 kondisi lagi untuk memperbaikinya dan dengan hati-hati menambahkan istilah lain ke rumus

u cdotv cdot(u1) cdot(v1) cdotg5(u,v)


yang tidak merusak perbatasan, tetapi ini adalah topik untuk artikel terpisah.

Alternatif adalah permukaan Bezier , tetapi ia mengusulkan untuk menetapkan 16 parameter makna matematika yang tidak dapat dipahami (bagi saya), yaitu diasumsikan bahwa artis akan bekerja dengan tangannya sendiri. Ini tidak cocok untuk saya, jadi sepeda dengan kruk mengikuti.

Koefisien (1) paling mudah dihitung dengan mencari matriks invers ke matriks nilai dan turunannya di sudut-sudut dan mengalikannya dengan kondisi input (tiga kali, untuk setiap koordinat). Matriksnya mungkin seperti ini:

Teks tersembunyi
[[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], #g (0,0)
[1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0], #g (1,0)
[1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0], #g (0,1)
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], #g (1,1)
[0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0], # dg / du (0,0)
[0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 3, 0], # dg / du (1,0)
[0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0], # dg / du (0,1)
[0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 3, 3], # dg / du (1,1)
[0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0], # dg / dv (0,0)
[0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1], # dg / dv (1,0)
[0, 1, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0], # dg / dv (0,1)
[0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 0, 1]] # dg / dv (1,1)

NumPy melakukan tugasnya dengan sangat baik.

Masih ada satu pertanyaan - di mana mendapatkan vektor turunan. Diasumsikan bahwa mereka entah bagaimana harus dipilih berdasarkan posisi titik tetangga (untuk poligon) dan untuk alasan kelancaran.
Bagi saya pribadi, itu masih sepenuhnya berlawanan dengan intuisi bagaimana menangani panjang vektor turunan. Arahnya bisa dimengerti, tapi panjangnya?

Alhasil, algoritma berikut ini lahir:

1. Pada tahap pertama, beberapa klasifikasi poin terjadi. Dalam grafik (titik-titik mana dari poligon dan koneksinya menentukan), siklus dengan panjang 4 dicari dan disimpan, dan di samping itu, tetangga yang paling cocok untuk peran memperpanjang tepi poligon dicatat (ditentukan terlebih dahulu tepi mana yang sesuai dengan perubahan pada parameter u dan yang sesuai dengan v). Berikut adalah sepotong kode yang mencari, mengurutkan, dan mengingat tetangga untuk titik ke-0 dari sebuah siklus:

Teks tersembunyi
"""   : cykle[5] cykle[7] cykle[4]--cykle[0] --u-- cykle[1]-cykle[6] |v |v cykle[10]-cykle[3] --u-- cykle[2]-cykle[8] cykle[11] cykle[9] """ sosed = [] for i in range(0, N): if self.connects[i][ind] == 1 and i != cykle[0] and i != cykle[1] and i != cykle[3]: sosed += [i] #    ... if(len(sosed) < 2): continue #    else: #      cykle[0] vs0c3 = MinusVecs(self.dots[sosed[0]], self.dots[cykle[3]]) vs1c1 = MinusVecs(self.dots[sosed[1]], self.dots[cykle[1]]) #        vs0c1 = MinusVecs(self.dots[sosed[0]], self.dots[cykle[1]]) vs1c3 = MinusVecs(self.dots[sosed[1]], self.dots[cykle[3]]) Rvsc = [ScalMult(vs0c3,vs0c3),ScalMult(vs1c1,vs1c1),ScalMult(vs0c1,vs0c1),ScalMult(vs1c3,vs1c3) ] n1 = Rvsc.index(max(Rvsc)) if n1 < 2: cykle += [sosed[1],sosed[0]] #  u   v else: cykle += [sosed[0],sosed[1]] 


Selanjutnya, ketika membangun spline, turunan di sepanjang tepi (misalnya, berkenaan dengan parameter u) pada titik poligon dipilih berdasarkan lokasi dua titik tepi dan satu titik yang berdekatan (biarkan itu menjadi titik vec1, vec2 dan vec3; titik di mana turunan dicari adalah yang kedua).



Pada awalnya saya mencoba menggunakan vektor dinormalisasi vec3 - vec1 (seperti saya menerapkan teorema Lagrange) untuk peran ini, tetapi masalah muncul justru dengan panjang vektor turunan - membuatnya konstan adalah ide yang buruk.

Penyimpangan lirik:

Untuk ilustrasi kecil tentang apa vektor turunan dalam versi parametrik, kita beralih ke analogi dua dimensi sederhana - di sini adalah sepotong busur melingkar:



g(u)=(Rsin(u pi/2),Rcos(u pi/2))


g(u)=(R pi/2 cdotcos(u pi/2),R pi/2 cdotsin(u pi/2))


Yaitu modulus turunan = R * pi / 2 dan, secara umum, tergantung pada ukuran potongan busur, yang kami atur melalui parameter.

Apa yang harus dilakukan dengan itu sekarang? Leo Nikolaevich Tolstoy mewariskan kepada kita bahwa semuanya bulat = baik, oleh karena itu, jika kita menetapkan turunan pada suatu titik seolah-olah kita ingin membuat busur di sana, kita akan mendapatkan kurva indah yang halus.

Akhir dari penyimpangan.

Tahap kedua dari pencarian turunan:

2. Melalui tiga titik kami vec1, vec2, vec3 kami membangun sebuah pesawat, di pesawat ini kami mencari lingkaran yang melewati ketiga titik. Derivatif yang diinginkan akan diarahkan sepanjang garis singgung ke lingkaran pada titik vec2, dan modulus vektor turunan harus sama dengan produk jari-jari lingkaran dan sudut sektor, yang membentuk dua titik permukaan poligon (mirip dengan analogi datar kami yang sederhana dari penyimpangan lirik).

g=R cdot phi


Segalanya tampak sederhana, di sini adalah kode fungsi, misalnya (NumPy digunakan lagi):

bukan kode, tapi ...
 def create_dd(vec1, vec2, tuda, vec3): """     1-2       3   == 1""" #0.         -         :( vecmult = VecMult(MinusVecs(vec2,vec1),MinusVecs(vec3,vec1)) #  vec2-vec1 x vec3 - vec1 if vecmult[0]*vecmult[0] + vecmult[1]*vecmult[1] + vecmult[2]*vecmult[2] < 0.000001: #  0 if tuda == 1: return NormVec(MinusVecs(vec3,vec2)) else: return NormVec(MinusVecs(vec1,vec2)) #1.   ,     #           2   M = np.zeros((3,3),float) C = np.zeros((3,1),float) M[0][0] = 2*vec2[0] - 2*vec1[0] M[0][1] = 2*vec2[1] - 2*vec1[1] M[0][2] = 2*vec2[2] - 2*vec1[2] C[0] = -vec1[0]*vec1[0] - vec1[1]*vec1[1] - vec1[2]*vec1[2] + vec2[0]*vec2[0] + vec2[1]*vec2[1] + vec2[2]*vec2[2] M[1][0] = 2*vec3[0] - 2*vec2[0] M[1][1] = 2*vec3[1] - 2*vec2[1] M[1][2] = 2*vec3[2] - 2*vec2[2] C[1] = -vec2[0]*vec2[0] - vec2[1]*vec2[1] - vec2[2]*vec2[2] + vec3[0]*vec3[0] + vec3[1]*vec3[1] + vec3[2]*vec3[2] #   ,   4     M[2][0] = vecmult[0] M[2][1] = vecmult[1] M[2][2] = vecmult[2] C[2] = vecmult[0]*vec1[0] + vecmult[1]*vec1[1] + vecmult[2]*vec1[2] # M *    = C Mobr = np.linalg.inv(M) Rvec = np.dot(Mobr,C) res = NormVec(VecMult(MinusVecs(Rvec,vec2), vecmult)) #    ka #R = math.sqrt((Rvec[0]-vec1[0])**2 + (Rvec[1]-vec1[1])**2 + (Rvec[2]-vec1[2])**2) #   = 3.14*2*asin(l/(2*R))/180 * R,      l  ,    l = 1.2*math.sqrt((vec1[0]-vec2[0])**2 + (vec1[1]-vec2[1])**2 + (vec1[2]-vec2[2])**2) if ScalMult(res, MinusVecs(vec2, vec1)) > 0: #     return MultVxC(res, tuda*l) else: return MultVxC(res, -tuda*l) 


Nah, secara umum, semuanya entah bagaimana berhasil. Untuk demonstrasi, saya mengambil kubus 5x5x5 dan mengubah posisi poin dengan pengacak:

GIF, 8Mb

Source: https://habr.com/ru/post/id473790/


All Articles