Diğer konuları üzerinde uzunca bir gezinimden sonra
3 boyutlu grafikleri OpenGL altında tartışmya varıyoruz. Sıze
yalan söyleyerek bunun kolay olduğunu söylemenin alemi yok , çünkü kolay
değil.
3 boyut uygulamalarının iyi OpenGL programcıları özellikle animasyonlar Cebir , Analitik Geometri
Fizik (Mekanik) ve elbette biraz da sayısal analizde bilgili olmalılar.
Bu serilerin kalan kısmını herkes için elimden geldiğince
anlaşılır düzeye indirmeye çalışacağım.
Maalesef gereken şeylerden sadece bir kaçı olan eğriye polinomik
(polynomial) yaklaşım yüzeyler
ve düzlemin üç boyutlu uzaydaki matematiksel gösterilimi , vektörler,
matrisler hakkındaki bilgileri
size veremem.
Geçen haftalar boyunca bu karışık konuyu nasıl büyük
bir kitlenin anlamasını sağlarım diye
düşündüm. standart kitaplar adım adım yaklaşım izlerler. Ben de geçen
iki makalemde hemen
hemen aynı yöntemi izledim. Bu yolu daha fazla takip etmemeye karar
verdim çünkü her
okuyucunun kendi kodunu yazma noktasına gelmesi uzun zaman alacaktı.
Bunun yerine başka bir metodu kullanmaya cüret
ettim. Bunu "şok tedavi" olarak adlandırmaya karar verdim .Bu makale benim
3 boyutlu similasyonlarımdan bir tanesinin demosunun kodunu içerecek, ve
ben bu kodu bit bit açıklamaya çalışacağım. Aslında standart OpenGL kitaplatında
normal olarak işlenenleri detaylı biçimde açıklayacağım. Fakat inanıyorumki
aniden sona atlamak ve okuyucuya kod örneklerini bazı ilginç şeylerle vermek
kullanıcıların kodları denemesini ve bazı şeylerle uğraşmasını sağlar.Buna
rağmen şimdilik okuyuculara herşeyin tam olarak nasıl çalıştığını anlatamayacağım.
Umarım ki bu methot çalışır ve insanlar bu yöntemi daha hızlı ve daha doğru
bulur.
Geçtiğimiz 6 ay boyunca Pittsburg üniversitesinde
jel ve polimer similasyonlarının geliştirilmesini destekleyen nesne yönelimli
uygulma üzerinde çalıştım. Bu proje çoktan tamamıyla geliştirildi. Bilgisayar
bilimlerinden pek çok insan için fizik ilginçtir , çünkü jel basit olarak
polimerlerden oluşmuş bir sinir ağıdır. Ve sinir ağları için geliştirilmiş
pek çok teknik jel yapılarına uygundur. Bu uygulamanın stripped down edilmiş
birkaç objesini topladım ve ../../common/May1998/example2.tar.gz adı altında basit bir demo
olarak paketledim. Bu paket Linux'ta diğer Unix'lerde veya GLUT kurulmuş
Windows 95 / NT ' de derlenebilir. Bu demo (bağlanmış monomerlerden oluşan
zincir ) hareket halindeki tek polimeri bazı ısılarda çözelti içinde asılı
kalırken gösterir. The dynamics are tantalizing. It looks like a
snake on stimulants! It is very lively due to the collisions of solvent
molecules. You will not see the solvent, as it is taking care of
through the equations of motion of the polymer.
Example2 polimer zinciri boyunca her düğümün (monomerin) koordinatlarını
izler animasyonun her karesinde monomerlerin koordinatlarında bir küre
çizeriz ve sonra
monomerlerden monomerlere giden silindirleri kullanarak onları birleştiririz.
Başlıca iki temel 3 boyutlu öğe var : Küre ve silindir. Herhangi bir molekülde
monomerler arasındaki uzaklık zamanla değiştiğinden bütün bağlantıları
sadece bir silindir kullanarak çözemeyiz, silindir o andaki iki monomer
arası uzaklığa bağlı olarak tekrar ölçeklendirilmelidir.
Soru1: 1 yüksekliğinde
bir yatay silindir ve bir küre olmak üzere iki tane 3 boyutlu nesneye sahipsiniz.
her iki nesnenin merkezi koordinatların orjininde olsun. Eğer polimerler
hakkında bildiğiniz sadece düğümlerin x y z koordinatlarının sırası ise
esas silindirin polimer bağlantılarını oluşturmak için nasıl ölçeklendirecek,
döndürecek ve taşıyacaksınız?
Anlayamadığım bir sebebten
dolayı bilgisayarcılar kartezyen koordinatların anlamını belirginleştirmişler
x: yatay y: düşey z: gözlemciye giden. Buna dikkat edin çünkü matematik
veya bilim tabanınız varsa bu sizi karıştıracaktır. Animasyon penceresinin
üst bölümünde o anki zamanı , polimerin o anki ısısını i ortalama ısısını
, çözeltinin ısısını, friction of the solvent ve dış kameranın dönme açısını
bilmenizi sağlayacak bir bilgilendirme banner 'ı var. Polimerin bütün yüzlerini
tam olarak göstermek yerine kamera polimerin etrafında yavaşça döner.
Gerçekten benim bu
demo için seçtiğim polimer uzunluğu polimerin kendi dönmesini gerektirmeyecek
kadar kısa example2.cxx dosyasındaki POLYMERLENGTH tanımını 2 ile 100 arasında
değiştirebilirsiniz. Kameranın dönmesinin sebebi kullanıcının görüne bir
problemi yani (koordinat sisteminin değişmesi) canlandırmasını istememdir.
Ve hareket denkemlerinde kullanılan düğümlerin koordinatları kullanıcının
bakış açısına bağlı değildir. (Genel kordinatlardadır) Bu koordinatlar
bilgisayar ekranının iki boyutlu x-y koordinatlarına dönüştürülmelidir.
Görüş açısının her değiştiğinde polimer iç koordinatlarını iki boyutlu
ekran koordinatlarına çeviren formüller değişir.
Soru 2: Bu problemi nasıl
çözersiniz? hareket denklemlerinin genel koordinatlardan iki boyutlu viewpoint
koordinatlarına dönüştürülmesi çok fazla cebir gerektirdiğinden konumuzun
dışındadır. Çünkü uygulanması çok zordur ve pek çok hata oluşabilir.
İkinci sorunun cevabı
çok basit sadece bir seçeneğimiz var : 3 boyutlu polimer modelinin genel
koordinatlarda ifadesi ve bu koordinatların çerçevenin (frame) render edilmesi
sırasında iki boyutlu görüş noktası (viewport) koordinatlarına çevrilmesi.
Bu dönüşümü yapmak için OpenGL genelde donanım düzeyinde desteklenir .(OpenGL
kartı olanlar farkı görebilirler) Ama OpenGL'in bu konuya nasıl yaklaştığını
incelemeden önce isterseniz 3 boyutlu genel koordinatlardan 2 boyutlu
pencere koordinatlarına geçişte kaç tane dönüşüm yapıldığına bakalım.
Öncelikle Modelview koordinat dönüşümü gelir.
Bu dönüşüm genel koordinatları gözlemcinin gözlerinin
( kamera ) yerine bağlı 3 boyutlu göz koordinatlarına
dönüştürür. Bu dönüşümün Modelview olarak adlandırılmasının
sebebi, bu dönüşümün benzer pekçok aralıklara uygulanabilmesidir.
Modelleme ve görüntüleme uygulamalarından ikincisi bir
photo-camera'nın bir stüdyoya yerlleştirilmesi ve tam
karşıdaki görüntünün fotograf olarak adlandırılması, diğeri ise
ilgilenilen objelerin kamera önüne yerleştirilmesi olarak
düşünülebilir.
Dönüşümün pipeline'ını takip edersek göz koordinatlarının
projeksiyon koordinat dönüşümlerine geçirildiğini görürüz.
Bu dönüşümün amacı şimdilik gizli kalacak.
Kameranın görüş yönünde ve objelerin görüş alanı içine
yerleştirilmesinden sonra, OpenGL görünümün ne kadarının
bilgisayar ekranına aktarılması gerektiğini bilmek
isteyecektir. Orneğin, kamera çok uzaktaki bir dağa
yönlendirilmiş olabilir. Bu durumda görüş alanı çok büyük bir
alanı belirtecektir. Bilgisayarlar sadece sonlu şeyleri ele
alabileceğinden görüntünün ne kadarını keseceğimizi
belirtmek zorundayız. Bu dönüşüm aynı zamanda
gizli yüzeylerin kaldırılmasıyla da ilgilenir. Sonda
ulaşılacak koordinatlar Clip koordinatlarıdır. Hatırlarsınız
ki, bu durum 3 boyutlu objelerin her zaman kamera önüne
düşmesi için yeterli değildir. Fakat, projeksiyon dönüşümünü
tanımlayan kesilmiş yüzeylerin içinde olmalıdır. Pekçok 3
boyutlu persfektif özellikleri ( konik, ortagonal, ... ) bu
aşamada uygulanabilir.
Şu an için ne persfektif böulünmenin ne de clip koordinatları ile normalleştirilmiş
araç koordinatları arasındaki farkı incelemeye girişmeyeceğiz. Şimdilik
bu gerekli değil.
Son önemli koordinat dönüşümü "Görüş noktası dönüşümü" . Burada,
bütün 3 boyutlu dönüşümlerden geçen 3 boyutlu koordinatlar son iş olarak
bilgisayar ekranının 2 boyutlu yüzeyine işlenir.
Koordinat dönüşümleri matrislerle ( iki boyutlu sayı dizileri ile )
ifade edilir. Yukarıdaki her bir dönüşüm için ayrı bir matris kullanılır.
Bu matrisler çerçeveyi render etmeden önce programın herhangi bir yerinde
tanımlanabilirler. OpenGL her sahnenin başında birtakım matris dönüşümlerini
uygular. Bu, çok akılcı ve yararlı bir tekniktir ve biz bunu daha ilerideki
yazılarda inceleyeceğiz. Şu an için, gelin kodu inceleyelim ve bu dönüşümlerin
nasıl ve nerede uygulandığını görelim. example2.cxx dosyasında tanıdık
olduğunuz yeniden şekillendirme fonksiyonları var.
void mainReshape(int w,int h){
//VIEWPORT TRANSFORMATION
glViewport(0,0,w,h);
//PROJECTION TRANSFORMATION
glMatrixMode(GL_PROJECTION);
glLoad Identity();
glFrustum(wnLEFT,wnRIGHT,wnBOT,wnTOP,wnNEAR,wnFAR);
//MODELWIEW TRANSFORMATION
glMatrixMode(GL_MODELVIEW);
...............
glViewport(x,y,w,h) direktifi,görüntü alanının dönüşümünü sağlar :
x ve y üçgeni gören pencerenin sol alt köşe
koordinatları ve 'w' ve 'h' de görüş alanının boyutlarıdır.
Bütün numaralar pixel (parantezler) içinde verilir.
Ardından glMatrix Mode() ,son matrisi seçmek için kullanılır , planlanan
dönüşümün belirlenmesine başlamak için
GL_PROJECTION parametresiyle çagırılır. Herhangi bir matris dönüşümü
belirlenmeden önce (köşe koordinatlarını
etkilemeyen) birleşim matrisi yüklemek tavsiye edilir.Bu işlem son
matrisi birleştirmeye başlayan glLoad Identity() ile
yapılır. Daha sonra sıra üç boyutlu perspektifin bildirimine gelir;
glFrustum(sol,sağ,alt,üst,yakiı,uzak) cumlesi sağ,sol,alt,üst,yakın
ve uzak pozisyonlarda kesişen yüzeyleri bildirir.Bu numaralar göz koordinatlarında
belirlenir ve sayıların büyüklükleri de görüntü
alanında (bilgisayar ekranı) yerleştirilen boşluk hacimlerinin şeklini
(böylelikle perspektifini ) belirler. Bu belki karışık
görunebilir;benim alışmam da bir sure aldı. Hakkında bir alışkanlık
kazanabilmek için yapılacak en iyi şey çesitli sayılar
denemektir; yalnız her zaman hatırlanması gereken şu ki; hangi sayıyı
seçerseniz seçin model-viewed (örnek alınan) nesne
kesişen yüzeylerin içinde kalmalıdır. Aksi takdirde ekranda hiçbir
şey görnmeyecektir. Planlanan dönüşümün belirlenmesi için
farklı yollar da var. Zaman içinde bu konulardan da bahsedeceğiz.
Final olarak geçerli matrisi (örnek alinan) modelview harf kalıbıyla
degiştiririz. Bu iş için tekrar GL_MODELVIEW
parametresini kullanarak glMatrixMode() özelliğinden yararlanırız.
mainReshape() özelliği diğer ilgisiz şeylerle devam eder ve
biter. Burada önemli olan şudur: Asıl pencere yeniden şekillendirilirken
bu özellik, görüntü alanını ve planlanan dönüşümü
belirlendi. Son olarak da (örnek alınan) modelview matrisi geçerli
duruma getirdi.
Bundan sonra, mainDisplay() özelliği modelview (örnek alınan) dönüşümün
belirtilmesine son verir ve polimeri scene() ile
tamamlar:
void mainDisplay(){
glutSetWindow(winIdMain);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
//Renkleri netleştirir ve (derinlik verir) depth buffers
//Bu ,tahtayi silmek gibidir
//MODELVIEW DONUSUMUNE DEVAM: Kamerayı kur ve yerleştir
glLoadIdentitiy(); //Birleşimi geçerli matrise yükle
glTranslatef(0.0,0.0,-4.0); //Kamerayı 4 adım geri götür
//kamerayı -z yönünü gösterir şekilde bırakırız. Aslında bu, arkadaki
görüntüyü -z yönünde
//4 adım hareket ettirerek gerçekleşir. Ekaranda yalnızca izlenen hacmin
içinde kalan
//görüntü elemanları gözükür. (Daha sonra planlanan dönüsüm kısmına
bakınız)
//Zinciri oluşturmak
glSaclef(0.5,0.5,0.5);
glRotatef(Aci,0,1,0);
scene();
glutSwapbuffers();
};
Umarım iki alt pencere kullanarak okuru çok şaşırtmamışımdır. Sonuçları
alt pencerelere baglı olarak açıklamıyorum; çünkü
bu bir önceki bölümde (windows management) zaten ele alınmıştı. Eğer
şüpheli olan bir kısım varsa lütfen geri dönüp hafızanızı
tazeleyiniz.
Bu özellik oldukça açık ve basittir. İlk önce glClear rengi siler ve
(derinlik verir) depth buffers.Depth buffer (derinlik
verilmesi) simdi üç boyutta önemlidir; çünkü her köşenin z koordinati,
saklı yüzeyleri ve yüzeylerin yer degiştirmesini saptamak
için test edilmelidir. Ardindan birleşim matrisi geçerli modelview
matris üzerine yükleriz ve 3 modelling dönüşümü çagırırız:
glTranslate(xt,yt,zt),,bu geçerli koordinat sistemini (xt,yt,zt) vektörleriyle
taşır. Bizim durumumuzda bu işlem kamerayı z'de
4 birim geri götürme etkisini yaratır ki bu da örnekten uzaktadır.
Eğer bunu yapmasaydık kamera orjinde, sahnenin ortasında
kalacaktı ve çok az şey görebilecektik.
glScalef(xs,ys,zs),adından da anlaşılacağı gibi koordinat sistemlerini
sırasıyla x,y,z eksenleri boyunca xs,ys,zs faktörleriyle
ölçülendirmeye yarar. Bu ölçülendirme koordinat sisteminin dünyada
kabul görmüş koordinat sistemine uyması için gereklidir.
glRotatef(aci,vx,vy,vz), geçerli koordinat sistemini (vx,vy,vz) normal
vektörü etrafında belli açılarla döndürür. Bu
bizim, kameranın döndügü izlenimini vermek için kullandıgımız bir hiledir.
Aslında dönen sahnedir. Kamerayı hareket ettirmenin
başka birçok yolu olmasına rağmen şu anda en kolayı budur.
Son bir uyarı: Modelling dönüsümlerin sırası çok onemlidir. Her koordinat
sistemi çagırışımızda Modelview matrise ne
olduğunu anlamamız gerekir. Her dönüşüm Ti matemaksel olarak bir Mi
matrisiyle temsil edilir. Bir dizi dönüşümün superposition
i ,Tn Tn-1....T1 (örneğin: yer degiştirme+ölçülendirme+döndürme) matematiksel
olarak bir tek matrisle temsil edilir.M=Mn
Mn-1.......M1
.Sıralama en can alıcı noktadır; çünkü karmaşık M dönüşümü bir köşeye
uygulandıgında dönüşümler ters sırada gerçekleşir.
M v = Mn Mn-1 .... M1 v
Ilk olarakM1, sonraM2, vb... ve en son olarak da Mn Kod örneğimizde
transformansyonları yandaki sıraya göre belirttim:
translasyon-> ölçeklendirme -> dönmüş. Bundan dolayı, dünya koordinatlarındakı
modelimizin her noktası grafik ekranına
yansıtılmadan önce dönmüş -> ölçeklendirilmiş -> çevirilmis olmalıdır.
Kod yazarken, bu transformasyonların ters sırasını her zaman aklınızda
bulundurun, aksi taktirde çok şaşırtıcı sonuçlar
bulabilirsiniz.
scene ?> fonksiyonu polimer obje uzerindeki 3D-çeviricisini çalıştırır.
3D modelinin sonradan nasıl inşa edildiğini anlamak için
Gd-OpenGL.cxx dosyasına gidip member fonksiyonu draw GdPolymer ? P
ye bir göz atmak gerekir. Polimer zincirindeki bütün
parçalardan geçen bir ana halka vardır ; öyle ki bu halka X, Y, Z koordinatlarını
tekrar bulup getirir ve o bölgede bir küre çizer
ve sonra birbirine halen baglı olan parçalardaki bağlar boyunca silindirler
çizer. Soru 1'i hatırlıyor musunuz? İşte size bir olası
çözüm ... Daha hızlı bir çözüm üretebilirseniz bizim de haberimiz olsun.
Okuyucunun Polimer çevirme rutinini tamamiyle anlayabilmesi için bir
şey daha var. glPushMatrix?> ve glPopMatrix?> ne işe
yarar ?
Polimer modelinde sadece 2 tane geometrik temel vardır ; biri merkezi
orijinde olan 0.40 yarıçaplı bir küre, diğeri ise yüksekliği
1.0 yarıçapı
0.40 olan bir dogru silindir. Polimer, kureleri ve silindirleri düzgün
bir pozisyonda yerleştirmek için bu iki temeli ve
transformasyonlar serisini kullanmak suretiyle yapılır. glCalList ?Parça>
ve glCalList?Silindir> komutlarından her biri her işleme
konulduğunda orijinde yeni bir küre ve silindir çevirilmiş olur. Küreleri
X, Y, Z koordinatlarına taşımak için bir translasyona
ihtiyacımız var ?bkz. glTranlatef?X, Y, Z>>; Bir silindiri bir ara
zincir gibi çevirmek ve yerleştirmek daha zor olur çünkü ilk
başta silindiri ara zincir kadar büyük yapmanız lazım ve daha sonra
onu doğru yöne çevirmeniz lazım. ?Benim hesap yöntemime
göre rotasyon transformasyon şeklinde bir derecelendirme kullanılır>
3D modelini inssa etmek için hangi metod kullanılırsa kullanılsın ona
hiç şüphe yok ki o ek transformasyonlara, tranzlasyonlara
ve rotasyonlara ihtiyaç olacak. scene ?> fonksiyonu çağırıldığında
OpenGL pozisyon makinasındaki mevcut matris
MODELVIEW matrisidir. Daha önce de belirttiğimiz gibi, bu, modelin
dünya koordinatlarinin clipping koordinatlarına
projeksiyonunu temsil eden matristir. Bu ciddi bir sorundur ; modelview
matrisi hala geçerli matris iken, 3D modelini
kurmak için uygulanan her ilave transformasyon geçerli matrise eklenecek
ve modelview tranformasyonunu tahrip etme
şeklindeki arzulanmaz bir sonuca yol açacaktır. Bununla yapılan iki
temel işlem vardır ; glPushMatrix?> ile uygulanan stack
push ve gl PopMatrix?> ile uygulanan pop. scene?> fonksiyonun kaynak
kodunu bir kere daha inceleyiniz ve her parçanın
küresini tercume etmeden önce MODELVIEW matrisini stack'in içine göndermek
için bir kereliğine push işlemini çağırdığımızda
dikkat ediniz. Halkanın sonunda da MODELVIEW matrisini geri getirmek
için pop işlemini çağırınız. Polimer sıralarını çeviren
içeride bulunan halkanın kendi push'u ve pop operasyonları vardır ;
öyle ki bunlar skalayi izole eder ve küre ile silindirleri
etkileyen tranzlasyonda transformasyonları çevirir.
3D tranformasyonlari matris stacklari hakkında söylenecek çok söz yok.
Bu makalede biz sadece her iki konuyu yuzeysel bir
şekilde anlattık. Şu an için bu kadarlık bir anlatım yeterli olur,
ilgilenen okuyucunun kaynak kodunu kendisi arastırması ve kendi
3D modellerini geliştirmesi gerekir. Kod Örneği 2'de daha önce arastırılmammış
bazı çehreleri de kullanır: materyaller ve
ışıklandırma. Edisyonların tartışmasını daha sonraki bir makaleye bırakıyoruz.
Bir dahaki sefere matris stacklerini ve 3D
tranformasyonlarını daha derin bir şekilde anlatacağız. Hareket eden
bir robotu OpenGL komutlarıyla nasıl yönlendireceğimizi
de göstereceğiz. O zamana kadara OpenGL ile iyi vakiti geçirmeye bakın.
|