Doğru
Parçalarını Çizimi
Daha önce OpenGL altında basit bazı poligonların çizimini görmüştük.
OpenGL sadece birkaç basit geometrik şeklin çizimini destekliyor: nokta,
doğru, poligon ve küçük dörtgensel veya üçgensel yaylarla tanımlanmış
yüzeyler gibi.
OpenGL'in basitliğinin ardındaki ana fikir bu basit nesnelerden,
karışık nesnelere doğru geliştirimin geliştiriciye bırakılmış
olmasıdır. OpenGL noktaların, doğruların ve poligonların
detaylarını kontrol etmek için komutlar içerir.
Örnek olarak noktanın büyüklüğü piksel olarak glPointSize
ile aşağıdaki kullanım şekliyle belirlenir:
void glPointSize(GLfloat size)
Noktanın benimsenmiş değer olarak büyüklüğü 1 dir ve size
daima 0'dan büyük olmalıdır. Özet olarak noktanın büyüklüğü ondalıklı
sayılarla ifade edilir. Nokta ve doğru büyüklüklerinde oransal (fractional)
değerlere izin verilir. OpenGL oransal piksel büyüklükleri görüntüleştirim
içeriğine (rendering context) göre yorumlar. Eğer basamak görüntü giderici
(anti-aliasing) mod etkinse, OpenGL komşu pikselleri ilgilenilen doğruya
uyacak şekilde düzeltir ve oransal genişlik görünümünü vermeye çalışır.
Basamak görüntü giderimi (anti-aliasing), doğruların düşük ekran
çözünürlüğünde gösterdiği çirkin yıldızları ortadan kaldırmak için
geliştirilen bir tekniktir. Eğer bu yöntem uygulanamıyorsa
glPointSize size'ı en yakın tamsayıya yuvarlatacaktır.
Fiziksel olarak bir pikselin büyüklüğü aygıta bağlı bir şeydir. Örnek
olarak düşük ekran çözünürlüğünde piksel daha geniş olarak çözülür. Benzer
biçimde, çizici gibi çok yüksek çözünürlükte de, benimsenmiş olan 1
piksellik çizgi hemen hemen görünmez hale gelir. Çizgilerinizin gerçek
genişliğini kestirmek istiyorsanız pikselin çıktı aygıtınızdaki gerçek
fiziksel ölçü değerini bilmeniz gerekmektedir.
Çizgilerin genişliği glLineWidth fonksiyonu ile belirlenir,
bu komutun glBegin() - glEnd() çifti arasında daha önceden
çağrılmış olması gerekir. Komutun tam sözdizimi aşağıdadır.
void glLineWidth(GLfloat width)
OpenGL'de basamak görüntü giderimsiz (nonantialiased) çizgilerin genişliği
basamak görüntü giderimli (antialiased) çizgilerin genişliğinin en yakın
tamsayıya yuvarlanmış en büyük değeriyle sınırlanmıştır. Şunu aklınızda tutun:
çizgi genişlikleri çizgiye dik olarak ölçülmezler; eğimin mutlak değerinin
1 den, küçük olması durumunda y doğrultusunda, büyük olması durumunda x
doğrultusunda ölçülürler.
Bu ay diğer bir basit fakat yararlı 2D animasyonları hazırlamış
bulunuyoruz. Bunlar size çeşitli tür çizgi kalınlıklarının OpenGL'de
nasıl kullanılacağını gösterecektir.(../../common/March1998/example2.c,
../../common/March1998/Makefile). Kuvantum fiziğinden bir örnek, bir kuyu
potansiyel içinde tutulu kalmış bir kuvantum parçacık seçtim. Niye? Hımmm,
gerçekte unuttum. Her neyse, bunun fizik ve mühendislik öğrencilerine
zamana bağımlı Schroedinger denkleminin nasıl çözülebileceğini görmekte
yardımcı olacağını düşündüm. Diğer bireyler kuvantum mekaniğinin önceden
kestirilemez doğasını gözleyerek eğlenebilirler. Kuvantum Mekaniği'nde
bir parçacık, pozisyonu ve hızı ile değil bir "dalga" ile, mutlak değer
karesi, parçacığın verilen bir konumda (parçalı beyaz doğru) bulunma
olasılığını veren dalga ya da dalga fonksiyonu (bizim animasyonumuzda mor
kalın çizgi olan) ile temsil edilir:
Figure 1. Quantum Simulation Snapshot
Sıradan diferansiyel denklemler hakkında bazı kurs çalışmalarında
bulunanlara söylediğim gibi dalga denklemi FFT (Hızlı Fourier Dönüşümü)
ayrık operatör (split operator) yöntemi ile integre edilir. Bu metod
diğer sonlu farklar metodlarından daha hızlı ve doğrudur. Bu yöntem
doğrusal olmayan dalga yayılımında da kullanılabilir. Zamansal evrim
operatörü ikinci ya da daha yüksek mertebeden olan ve sadece ya konuma
ya da momentuma bağlı operatörlere ayrıştırılır. Daha sonra dalga
fonksiyonunun evrimi, bu operatörlerin ardışık olarak uygulanmaları ve
bunun için de konum ve momentum uzayları arasında gidiş gelişlerle
sağlanır.
Kaynak kodu diğer birçok uygulama için kullanılabilir. Benim kuvantum
simulasyonumu kendi zamana bağımlı fonksiyonunuzla değiştokuş edebilir
ve sisteminizin güzel animasyonlarını elde edebilirsiniz. Kendiniz de,
fonksiyon ve veri dosyalarının grafiklerinin çizimi için basitleştirilmiş
bir OpenGL tabanlı gnuplot yazmayı deneyebilirsiniz.
Eğer okuyucu daha önceki makaleleri takip ettiyse Glut ve OpenGL
üzerindeki kaynak kodlarını çok basit görüp kolay anlayacaktır.
(Tabii ki kuvantum mekaniği bir kenarda tutularak). Burada olağanüstü
giden birşey bulunmamaktadır. main()'de çift emicibellekli
(buffer) bir modda pencere açtık. Daha sonra, sırasıyla, dalga
fonksiyonunun grafiğinin çizimini ve denkleminin çözümünden elde
edilmesini sağlayan display() ve idle() geriçağırım
(callback) fonksiyonlarına geçtik. Her ne kadar bu yazının içeriğini
kavramak için çok güzel bir aldatmaca olan idle() fonksiyonunda ne olduğunu
bilmek ille de gerekmiyorsa da siz yine de önemseyin.
void
display (void)
{
static char label[100];
float xtmp;
/* Clean drawing board */
glClear (GL_COLOR_BUFFER_BIT);
/* Write Footnote */
glColor3f (0.0F, 1.0F, 1.0F);
sprintf (label, "(c)Miguel Angel Sepulveda 1998");
glRasterPos2f (-1.1, -1.1);
drawString (label);
/* Draw fine grid */
glLineWidth (0.5);
glColor3f (0.5F, 0.5F, 0.5F);
glBegin (GL_LINES);
for (xtmp = -1.0F; xtmp < 1.0F; xtmp += 0.05)
{
glVertex2f (xtmp, -1.0);
glVertex2f (xtmp, 1.0);
glVertex2f (-1.0, xtmp);
glVertex2f (1.0, xtmp);
};
glEnd ();
/* Draw Outsite box */
glColor3f (0.1F, 0.80F, 0.1F);
glLineWidth (3);
glBegin (GL_LINE_LOOP);
glVertex2f (-1.0F, -1.0F);
glVertex2f (1.0F, -1.0F);
glVertex2f (1.0F, 1.0F);
glVertex2f (-1.0F, 1.0F);
glEnd ();
/* Draw Grid */
glLineWidth (1);
glColor3f (1.0F, 1.0F, 1.0F);
glBegin (GL_LINES);
for (xtmp = -0.5; xtmp < 1.0; xtmp += 0.50)
{
glVertex2f (xtmp, -1.0);
glVertex2f (xtmp, 1.0);
glVertex2f (-1.0, xtmp);
glVertex2f (1.0, xtmp);
};
glEnd ();
/* Draw Coordinate Axis */
glLineWidth (2);
glBegin (GL_LINES);
glVertex2f (-1.0, 0.0);
glVertex2f (1.0, 0.0);
glVertex2f (0.0, -1.0);
glVertex2f (0.0, 1.0);
glEnd ();
/* Axis Labels */
glColor3f (1.0F, 1.0F, 1.0F);
sprintf (label, "Position");
glRasterPos2f (0.80F, 0.025F);
drawString (label);
glColor3f (1.0F, 0.0F, 1.0F);
sprintf (label, " Quantum Probability ");
glRasterPos2f (0.025F, 0.90F);
drawString (label);
glColor3f (1.0F, 1.0F, 1.0F);
sprintf (label, " Real(Psi) ");
glRasterPos2f (0.025F, 0.85F);
drawString (label);
/* Draw Wavefunction */
psiDraw (NR_POINTS, psi, x);
/* Draw potential Function */
potentialDraw (NR_POINTS, potential, x);
glutSwapBuffers ();
};
İlk olarak renk emicibellek biti (color buffer bit) temizleniyor.
Bu bize temiz (siyah) bir çizim düzlemi oluşturuyor. glRasterPos ve
glutBitmapCharacter (çizimkatarı renk bakıpseçme [clut=color look
up table] ) olanağı için bir sarmalayıcıdan [wrapper] başka birşey değil)
kullanılarak dipnot ekleniyor. Gelecek derslerde glRasterPos
dokuların görüntüleştiriminde yine yardımcı fonksiyon olarak gözükecektir.
Ne OpenGL ne de GLUT grafik penceresi üzerinde metin görüntüleştiriminde
kolay ve güçlü bir yol sağlayabilmektedir. glutBitmapCharacter, temelde,
bir font ikilitabangösterilimini (bitmap) renk emicibelleği üzerine
düşürür.
Dipnotu bir takım ãizgiler izler: dış kutu, arka plan çizgisi,
koordinat eksenleri ve tabii ki psiDraw ve potentialDraw
ile oluşturulan eğriler. Her çizgi görüntüleştirilmeden önce
glLineWidth komutu doğrunun kalınlığını piksel sayıları ile
belirtir. 1. şekil bir Xwindow sistemindeki (Linux Alpha) çıktıyı
göstermektedir. Bazı bilinmeyen nedenlerden dolayı bana göre windows95
çıktısı anlamsıy bir yapıda gibi gözüküyor, sanki antialiasing özelliği
SGI OpenGL sürücüsü tarafından desteklenmiyor görünümü veriyor; farklı
genişlikteki doğruları ayırdedilebilir biçimde görüntülemek mümkün olamıyor,
arka plan kare örgü (grid) çok fazla düzgün gözüküyor. Bu bozukluklar
düşük çözünürlüklerde olma yerine yüksek çözünürlüklerde oluşuyor.
Linux X pencere sisteminin büyük win95/NT sistemini bir kez daha yenilgiye
uğratmasından dolayı mutluyum.
display() fonksiyonunda iki tür doğru görüntüleştirimi
bulunmaktadır. Düğüm noktalarını sürekli bir açık doğru ile birleştiren
GL_LINES modu ve sonunda çevrimi kapatan GL_LINE_LOOP modu.
Doğru
Parçalarında Basamak Görüntü Giderimi
Burada reshape() geriçağırım fonksiyonunda doğrular
için basamak görüntü giderimini etkinleştirmiş bulunmaktayım.
void
reshape (int w, int h)
{
glMatrixMode (GL_MODELVIEW);
glLoadIdentity ();
glViewport (0, 0, w, h);
glMatrixMode (GL_PROJECTION);
glLoadIdentity ();
gluOrtho2D (-1.2, 1.2, -1.2, 1.2);
glEnable (GL_LINE_SMOOTH); /* Enable Antialiased lines */
glEnable (GL_LINE_STIPPLE);
};
GL_LINE_STIPPLE ne için kullanılıyor? OpenGL bize sadece çizgi
genişliğini değil aynı zamanda örüntüyü de (pattern) kontrol etmeyi sağlıyor.
GL_LINE_STIPPLE etkinleştirilmesi bize tirelerle veya diğer örüntülerle
doğrular çizmemizi sağlar. Animasyondaki tek kesikli çizgi (stippled line)
psiDraw() fonksiyonunun çiziminde gözükmektedir.
glLineWidth (1);
glPushAttrib (GL_LINE_BIT);
glLineStipple (3, 0xAAAA);
glBegin (GL_LINE_STRIP);
for (i = 0; i < nx; i++)
{
xs = ratio1 * (x[i] - XMIN) - 1.0;
ys = ratio2 * (psi[2 * i] - YMIN) - 1.0;
glVertex2d (xs, ys);
};
glEnd ();
glPopAttrib ();
Çizgi
Kesiklileştirimi
glLineStipple kesikli çizgi çiziminde kullanılacak
örüntüyü belirler. Bizim örneğimizde kullanılan örüntü 0xAAAA dır. 2'li
düzende bu mod 0000100010001000 şeklindedir. OpenGL bu çizimi 3 bit boş,
1 bit dolu, 3 bit boş, 1 bit dolu, 3 bit boş, 1 bit dolu ve son olarak
4 bit boş biçiminde yorumlar. Evet, örüntü geriye doğru okunur, çünkü
ilk önce düşük mertebeden bit'ler okunur. Şimdi glLineStipple
iki parametre çağıracaktır, onaltılık düzende bir sayı olması gereken
kesikli örüntü ve bu örüntüyü ölçeklemek için bir tamsayı çarpan.
Dolayısıyla, bir 3 çarpanıyla, bizim kesikli doğrumuz 9 bit boş,
3 bit dolu, 9 bit boş, 3 bit dolu, 9 bit boş, 3 bit dolu ve son
olarak da 12 bit boş biÇiminde bir görüntü verecektir. Bu çarpanla ve
ikilitaban örüntülerle oynayarak karmaşık yapıdaki olası tüm doğruları
çizmek mümkündür.
Bir ayrıntı daha: Burada kesikli çizgi görüntüleştirimini push ve pop
deyimleri arasına aldım. Birinci yazımızda OpenGL'in bir durum makinası
olduğunu belirttiğimizi anımsayın. Gelecek yazılarımızda bu push ve pop
işlemleri üzerinde daha ayrıntılı duracağız, ama kısaca yaptığımız şeyin
ilk olarak glPushAttrib (GL_LINE_BIT) komutuyla GL_LINE_BIT
durum değişkeninin (bu değişken kesiklileme örüntüsünü tanımlar ya da
seçer) o andaki değerini bir yığıt (stack) içine yerleştirmek, daha sonra da
GL_LINE_BIT değerini glLineStipple komutuyla değiştirmek ve
işimizi bitirdiğimizde GL_LINE_BIT'in ilk değerini geri getiren
glPopAttrib fonksiyonunu çağırmak olduğunu söyleyebiliriz.
Bu düzenek (mechanism) OpenGL durum değişkenlerinin değerlerinin
yerel olarak değiştirilmesinin etkin bir yoludur. Eğer bunu yapmamış
olsaydık, glLineStipple komutundan sonra çizilen tüm
öizgiler aynı kesiklileme örüntüsüne sahip olacak ve kendi uygulamamızdaki
her görüntüleştirimi yapılan çizgi için bir glLineStipple komutu
kullanmak zorunda kalacaktık. Push ve Pop bizi bu can sıkıcı duruma
düşmekten korur.
İlerki Zamanda....
OpenGL, 3 Boyutlu API arayüzü nedeniyle çok ünlüdür. Buradaki
kadarıyla, biz OpenGL ile 2 Boyutlu bazı temel görüntüleştirim olanaklarını
incelemiş bulunuyoruz. Gelecek kez, 3 Boyutlu OpenGL görünümünü, bir
perspektifin nasıl yaratılacağını, koordinat sistemlerini, kesmeyle alma
düzlemlerini (clipping plane) ve izdüşüm matrislerini inceleyeceğiz.
O güne dek OGL......ile iyi eğlenceler.
|