Noktalar ve bu noktaları birleştiren çizgilerden oluşan modelleme sistemi Tel-Kafes Modelleme olarak adlandırılır. Diğer modelleme sistemlerinden daha basit ve kullanışsızdır ama diğer modelleme sistemlerinin de temelini oluşturur. Aşağıda tel-kafes ile modellenen Utah Teapot görülmektedir.
Bu yazımda basit ve kısa bir şekilde tel-kafes modelinin C diline aktarılmasını, dönüşüm matrisi ile eksenler etrafında nasıl dönüşüm yapılacağını ve bunların Stm32f4 kiti üzerinde gösterimini yapacağım.
Modelin tanımlanması
Bir objenin tel-kafes modelini struct ile şu şekilde tanımlayabiliriz:
typedef struct { //Kullanılan nokta ve cizgi sayisi uint16_t NoktaSayisi; uint16_t CizgiSayisi; //Noktaların koordinatları(x,y,z) float (*Noktalar)[3]; //Hangi noktaların birbiri ile birleşecegini //belirten dizi(nokta0,nokta1) uint16_t (*Cizgiler)[2]; //Objenin koordinatı(x,y,z) float Koordinat[3]; //Objenin merkez koordinatı(x,y,z) float Merkez[3]; //Objenin cizgilerinin rengi(RGB565) uint16_t Renk; }GL_Obje;
NoktaSayisi ve CizgiSayisi, objede kullanılan toplam nokta ve cizgi sayisi hakkinda bilgi verir. Bu sayilar modeli çizerken ve döndürürken işimize yarayacaktır. uint16_t şeklinde tanımlandığından dolayı en fazla tane nokta ve çizgi tanımlayabiliriz. Noktalar 3 elemanlı bir dizi olup her elemanında noktanın koordinatlarını içeren değerler bulunur. Çizgiler 2 elemanlı bir dizi olup, hangi noktanın hangi nokta ile birleşeceğini gösterir. Örneğin cizgi[0] = {0, 1} şeklinde bir tanımlama varsa bu nokta[0]'dan nokta[1]'e bir çizgi olduğu anlamına gelir. Koordinat ve Merkez de 3 elemanlı bir dizi olup objenin koordinatı ve merkezini gösterir. Bu değerler obje döndürülürken ve yeniden boyutlandırılırken kullanılabilir. Son olarak Renk de çizgilerin rengini belirtir. Obje çizilirken bu değer kullanılır.
Modelin Çizilmesi
Bütün çizgileri aşağıdaki fonksiyon ile çizdirebiliriz.
//Ekrana (x0, y0) noktasindan (x1, y1) noktasina //Renk renginde cizgi cizen fonksiyon void GL_2DCizgiCiz(x0, y0, x1, y1, Renk); /** @Tanim : Obje'yi ekrana cizgiler yardimi(Tel-Kafes) ile cizen fonksiyon * @Parametreler : Obje = Cizilecek objenin adresi */ void GL_3DObjeCiz(GL_Obje *Obje) { //Toplam cizgi sayisi kadar dongude kal for(int i = 0; i < Obje->CizgiSayisi; i++) //Cizgiler dizisinin indeksledigi noktaları birbiri ile birleştir GL_2DCizgiCiz(Obje->Noktalar[Obje->Cizgiler[i][0]][0], //nokta0(x) Obje->Noktalar[Obje->Cizgiler[i][0]][1], //nokta0(y) Obje->Noktalar[Obje->Cizgiler[i][1]][0], //nokta1(x) Obje->Noktalar[Obje->Cizgiler[i][1]][1], //nokta1(y) Obje->Renk); //renk(RGB565) }
Küp Modeli
Birim kübü el ile aşağıdaki şekilde tanımlayabiliriz.
//Noktaların koordinatlari float Kup_Noktalar[8][3] = { 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1 }; //Hangi noktalarin birbiri ile birleşeceğini //gösteren dizi uint16_t Kup_Cizgiler[12][2] = { 0,1, 1,2, 2,3, 3,0, 4,5, 5,6, 6,7, 7,4, 0,4, 1,5, 2,6, 3,7 }; //Kup objesi yarat GL_Obje Kup; //Onceden belirlenmis degerleri bu objeye at Kup.NoktaSayisi = 8; Kup.CizgiSayisi = 12; Kup.Noktalar = Kup_Noktalar; Kup.Cizgiler = Kup_Cizgiler; Kup.Merkez[0] = 0.5; Kup.Merkez[1] = 0.5; Kup.Merkez[2] = 0.5; Kup.Koordinat[0] = 0; Kup.Koordinat[1] = 0; Kup.Koordinat[2] = 0; Kup.Renk = 0xFFFF; //Beyaz
Her obje için tek tek koordinatları ve çizgileri hesaplamak çok zaman alacağından bu işi bir fonksiyon ile yapabiliriz.
/** @Tanım : Istenen buyuklukte, koordinatta ve renkte kup yaratan fonksiyon * @Parametreler : Kup = yaratilmak istenen Objenin adresi, * x, y, z = Objenin koordinatlari * Uzunluk = Kupun bir kenarinin uzunlugu * Renk = Objenin cizgilerinin rengi */ void GL_3DKupYarat(GL_Obje* Kup, float x, float y, float z, float Uzunluk, uint16_t Renk) { //Kupun noktalarinin tanimlanmasi float Kup_Noktalari[8][3] = { x, y, z, x + Uzunluk, y, z, x + Uzunluk, y, z + Uzunluk, x, y, z + Uzunluk, x, y + Uzunluk, z, x + Uzunluk, y + Uzunluk, z, x + Uzunluk, y + Uzunluk, z + Uzunluk, x, y + Uzunluk, z + Uzunluk }; //Kupun cizgilerinin tanimlanamasi uint16_t Kup_Cizgileri[12][2] = { 0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6, 6, 7, 7, 4, 0, 4, 1, 5, 2, 6, 3, 7 }; //Dinamik bellekte yer ayrilmasi ve bu ayrilan yere //önceden tanimlanmis noktalarin ve cizgilerin kopyalanmasi float *_Kup_Noktalari = (float *)malloc(sizeof(Kup_Noktalari)); memcpy(_Kup_Noktalari, Kup_Noktalari, sizeof(Kup_Noktalari)); uint16_t *_Kup_Cizgileri = (uint16_t *)malloc(sizeof(Kup_Cizgileri)); memcpy(_Kup_Cizgileri, Kup_Cizgileri, sizeof(Kup_Cizgileri)); //Tanimlanmis özelliklerin Kup objesine aktarilmasi Kup->NoktaSayisi = 8; Kup->CizgiSayisi = 12; Kup->Noktalar = (float (*)[3])_Kup_Noktalari; Kup->Cizgiler = (uint16_t (*)[2])_Kup_Cizgileri; Kup->Koordinat[0] = x; Kup->Koordinat[1] = y; Kup->Koordinat[2] = z; Kup->Merkez[0] = x + Uzunluk / 2; Kup->Merkez[1] = y + Uzunluk / 2; Kup->Merkez[2] = z + Uzunluk / 2; Kup->Renk = Renk; }
Objenin bir eksen etrafında döndürülmesi
Dönüşüm matrisi bir noktayı belli bir eksen etrafında döndüren matrise verilen isimdir. düzleminde bir noktayı ekseni etrafında döndüren matris aşağıdaki gibidir.
Benzer şekilde ve ekseni etrafında döndüren matrisler :
Bir noktayı hem hem hem de ekseni etrafında döndürmek için bu 3 matrisin çarpılması gerekir.
Matrisler yukarıdaki sıra ile çarpıldığında, ilk olarak sonra ve en son ekseni etrafında dönüşüm yapılır.
Bu dönüşüm matrisini fonksiyona dönüştürürsek:
/** @Tanim : Objeyi belirlenen nokta etrafinda belirlenen acida döndüren fonksiyon * @Parametreler : Obje = Donusume ugrayacak obje * DonusumMerkezi = Donusumun yapilacagi referans(merkez) noktasi, * eğer tanimlanmamis(NULL) ise Obje'de tanimlanan merkez noktasi * Donunum_x, Donunum_y, Donunum_z = Derece cinsinden donusum acisi */ void GL_3DDonusum(GL_Obje *Obje, float *DonusumMerkezi, float Donusum_x, float Donusum_y, float Donusum_z) { //Derecenin radyana donusturulmesı float Aci_x = Donusum_y * PI / 180; float Aci_y = Donusum_x * PI / 180; float Aci_z = Donusum_z * PI / 180; //Donusum matrisine girecek noktalar float Cor_x, Cor_y, Cor_z; //Butun noktalar icin dongude kal for(int i = 0; i < Obje->NoktaSayisi; i++){ //Donusum matrisine girecek noktalari bul //Belirlenen noktada donusum yapabilmek icin referans koordinatini, //objenin nokta koordinatindan cikar //(Donunsum merkezini, (x, y, z) = (0, 0, 0) noktasina otele) if(Donusummerkezi != NULL){ Cor_x = Obje->Noktalar[i][0] - DonusumMerkezi[0]; Cor_y = Obje->Noktalar[i][1] - DonusumMerkezi[1]; Cor_z = Obje->Noktalar[i][2] - DonusumMerkezi[2];} else{ //DonusumMerkezi tanimlanmamis(NULL) ise objenin merkezi etrafında donusum yap Cor_x = Obje->Noktalar[i][0] - Obje->Merkez[0]; Cor_y = Obje->Noktalar[i][1] - Obje->Merkez[1]; Cor_z = Obje->Noktalar[i][2] - Obje->Merkez[2]; } //Noktalari donusum matrisi ile carp #ifndef ARM_MATH_CM4 Obje->Noktalar[i][0] = cos(Aci_x) * cos(Aci_z) * Cor_x + (cos(Aci_z) * sin(Aci_y) * sin(Aci_x) - cos(Aci_y) * sin(Aci_z)) * Cor_y + (cos(Aci_y) * cos(Aci_z) * sin(Aci_x) + sin(Aci_y) * sin(Aci_z)) * Cor_z; Obje->Noktalar[i][1] = cos(Aci_x) * sin(Aci_z) * Cor_x + (cos(Aci_y) * cos(Aci_z) + sin(Aci_y) * sin(Aci_x) * sin(Aci_z)) * Cor_y + (-cos(Aci_z) * sin(Aci_y) + cos(Aci_y) * sin(Aci_x) * sin(Aci_z)) * Cor_z; Obje->Noktalar[i][2] = -sin(Aci_x) * Cor_x + cos(Aci_x) * sin(Aci_y) * Cor_y + cos(Aci_y) * cos(Aci_x) * Cor_z; //CM4 DSP icin daha hizli fonksiyonlar #else Obje->Noktalar[i][0] = arm_cos_f32(Aci_x) * arm_cos_f32(Aci_z) * Cor_x + (arm_cos_f32(Aci_z) * arm_sin_f32(Aci_y) * arm_sin_f32(Aci_x) - arm_cos_f32(Aci_y) * arm_sin_f32(Aci_z)) * Cor_y + (arm_cos_f32(Aci_y) * arm_cos_f32(Aci_z) * arm_sin_f32(Aci_x) + arm_sin_f32(Aci_y) * arm_sin_f32(Aci_z)) * Cor_z; Obje->Noktalar[i][1] = arm_cos_f32(Aci_x) * arm_sin_f32(Aci_z) * Cor_x + (arm_cos_f32(Aci_y) * arm_cos_f32(Aci_z) + arm_sin_f32(Aci_y) * arm_sin_f32(Aci_x) * arm_sin_f32(Aci_z)) * Cor_y + (-arm_cos_f32(Aci_z) * arm_sin_f32(Aci_y) + arm_cos_f32(Aci_y) * arm_sin_f32(Aci_x) * arm_sin_f32(Aci_z)) * Cor_z; Obje->Noktalar[i][2] = -arm_sin_f32(Aci_x) * Cor_x + arm_cos_f32(Aci_x) * arm_sin_f32(Aci_y) * Cor_y + arm_cos_f32(Aci_y) * arm_cos_f32(Aci_x) * Cor_z; #endif //ARM_MATH_CM4 //Cikarilan degerleri tekrar topla if(Donusummerkezi != NULL){ Obje->Noktalar[i][0] += DonusumMerkezi[0]; Obje->Noktalar[i][1] += DonusumMerkezi[1]; Obje->Noktalar[i][2] += DonusumMerkezi[2];} else{ Obje->Noktalar[i][0] += Obje->Merkez[0]; Obje->Noktalar[i][1] += Obje->Merkez[1]; Obje->Noktalar[i][2] += Obje->Merkez[2]; } } }
Stm32f4 Kitinde Örnek Uygulama
Bu uygulamada bir kenarı 100 birim olan küpü her 30ms de kendi merkezinde, bütün eksenler etrafında 2 derece döndürdük ve bunu kitin ekranında gösterdik.
#include "stm32f4xx.h" #include "donanim.h" #include "3D.h" #define BEYAZ 0xFFFF int main() { //GPIO, LTDC, FMC... donanimlarini aktif et DonanimiHazirla(); //Kup objesini olustur GL_Obje Kup; //Kup objesini gercek bir kup objesine donustur GL_3DKupYarat(&Kup, 100, 60, 0, 120, BEYAZ); while(1){ //Ekrani temizle GL_EkraniTemizle(); //Kup objesini ciz GL_3DObjeCiz(&Kup); //Kup objesini kendi merkezinde butun eksenler etrafinda 2 derece dondur GL_3DDonusum(&Kup, NULL, 2, 2, 2); //30ms bekle ve basa don Bekle(30); } }
Video :
Hello!
Its very impressive the word and how you manage the display. Do you think I can get the project for a school work? I need to do preety much the same but with a sphere, so far I printed one in a 2d reference but this kind of modeling gets me in trouble. Thanks!
https://www.dropbox.com/s/3rzrbsppk0h3pp4/IMG_20161211_155536.jpg?dl=0
Hey, thanks for your comment, can you reach me via iletisim@bertantaskin.com and we can discuss details.
Valla tebrik ederim. Çok güzel işler yapmışsın helal olsun.