Home Index Search Links About Us
[LinuxFocus Image]
[Navegation Bar]
  Duyumlar   Belgelikler   Kuruluşlar  Öğütler  

OpenGL Programlama: Doğru Parçaları Üzerine Ekbilgiler

Yazar: Miguel Miguel Angel Sepúlveda

Çeviri:Filiz Topal


../../common/March1998/example2.c

../../common/March1998/Makefile

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:

[Click here to see the image]
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.


Daha Çok Bilgi İçin:
© 1998 Miguel Angel Sepulveda
Bu sanal yörenin bakımı Miguel A Sepulveda tarafından yapılmaktadır..