Bu yazımızda üç
boyutlu çizimler üzerinde döndürme, öteleme, yakınlaşma ve uzaklaşma işlemlerinin
nasıl yapılacağını ele alacağız. OpenGL ve C başlığı altında yürüttüğümüz yazı
dizisinde, bir OpenGL penceresinin nasıl oluşturulduğunu, iki boyutlu ve üç
boyutlu çizimlerin nasıl yapıldığını ele almıştık ve OpenGLin bu işlemleri
yapan fonksiyonlarını incelemiştik. OpenGL kütüphanesi x, y ve z eksenleri etrafında
döndürme, x, y ve z eksenleri boyunca öteleme işlemlerini yapan fonksiyonlar
da içermektedir. Daha önce yaptığımız uygulamalarda kısmen kullandığımız bu
fonksiyonları bu yazımızda ayrıntılarıyla ele alacağız.
void
glTranslated(GLdouble x, GLdouble y, GLdouble z);
void glTranslatef(GLfloat x, GLfloat y, GLfloat z);
|
Geçerli
görüntü matrisini, parametre olarak verilen öteleme matrisi ile çarparak
görüntü matrisini değiştirir. Başka bir deyişle görüntüyü x, y ve z eksenleri
boyunca öteler. Fonksiyonların x, y ve z parametreleri öteleme matrisinin
değerleridir. glBegin ve glEnd fonksiyonları arasında çağrılırsa GL_INVALID_OPERATION
hata kodunu üretir. |
void
glRotated(GLdouble angle, GLdouble x, GLdouble y, GLdouble z);
void glRotatef(GLfloat angle, GLfloat x, GLfloat y, GLfloat z); |
Geçerli
görüntü matrisini, parametre olarak verilen dönüşüm matrisi ile çarparak
görüntü matrisini değiştirir. Başka bir deyişle görüntüyü x, y ve z eksenleri
etrafında saat yönünde döndürür. Fonksiyonların x, y ve z parametreleri
dönüşüm matrisinin değerleridir, angle parametresi ise belirtilen eksen
etrafındaki döndürme açısının derece cinsinden değeridir. glBegin ve glEnd
fonksiyonları arasında çağrılırsa GL_INVALID_OPERATION hata kodunu üretir. |
Bu yazımızda, kullanıcının
klavyeden gireceği karakterlere göre çizimi belli bir eksen etrafında döndüren,
belli eksenler boyunca öteleyen bir uygulama oluşturacağız. Uygulama çalıştırıldığında
başlangıçta hiçbir işlem yapılmayacak. Kullanıcı klavyeden belli karakterleri
girdiğinde, uygulama girilen bu karakterlere göre işlem yapacaktır. Uygulamanın
içeriğini özetleyecek olursak :
    - x , y ve z eksenleri boyunca öteleme işlemleri
    - x , y ve z eksenleri etrafında döndürme işlemleri
Daha önce yaptığımız
uygulamalarda bir OpenGL çizim penceresi oluşturmuştuk. Bu pencere üzerinde
iki ve üç boyutlu çizimler yapmıştık. Bu uygulamamızda yine aynı pencereyi kullanacağız
ve üç boyutlu çizim yapacağız. Döndürme ve öteleme işlemlerinde kullanmak üzere
global alanda aşağıdaki değişkenleri tanımlayalım :
BOOL filled
= FALSE;
BOOL rotate = FALSE;
GLfloat rotateAngle = 0.0f;
GLfloat xRotate = 0.0f, yRotate = 0.0f, zRotate = 0.0f;
GLfloat xTranslate = 0.0f, yTranslate = 0.0f, zTranslate = -7.0f; |
Bu değişkenleri
ne amaçla kullanacağımıza kısaca değinelim. Uygulamamızda bir küp oluşturacağız.
OpenGL kütüphanesi bu kübü, sadece kenar çizgileri ile gösterebilmemize ya da
yüzeylerini boyayarak gösterebilmemize izin verir. filled isimli değişkeni de
bu durumu belirlemek için kullanacağız. Uygulamanın çalışma zamanında, kullanıcı
klavyeden f (fill) tuşuna basarak bu seçeneği değiştirebilecek. Kullanıcı
klavyeden t (turn) tuşuna bastığında şekil döndürülecek, tekrar t tuşuna
bastığında ise şekil o anki açısıyla sabit biçimde gösterilecek. rotateAngle
isimli değişken, şeklin döndürülme açısını belirleyecek. xRotate, yRotate ve
zRotate değişkenleri ise şeklin hangi eksenler etrafında döndürüleceğini belirleyecek.
Döndürme işlemini başlatmak veya durdurmak için kullanıcı klavyeden t (turn)
tuşuna basacak. Döndürme eksenlerini ayarlamak için ise eksen isimlerini ifade
eden x, y ve z tuşlarına basacak. xTranslate, yTranslate ve zTranslate
değişkenleri, x, y ve z eksenleri boyunca yapılacak öteleme miktarını belirleyecek.
Kullanıcı klavyeden l (left), r (right), u (up) ve d (down) tuşlarına
bastıkça bu değişkenlerin değerlerini değiştireceğiz. Dolayısıyla öteleme matrisinin
değerlerini değiştirmiş olacağız.
Uygulamamızda çizim
işlemlerini yaptığımız fonksiyon DrawGLScene isimli fonksiyon idi. Uygulamanın
çalışma zamanında herhangi bir mesaj alınmadığı sürece bu fonksiyon çağrılır
ve ekrana çizim işlemi yapılır. DrawGLScreen fonksiyonunun hemen ardından SwapBuffers
isimli fonksiyon çağılır ve görüntü tamponundaki değişiklikler ekrana yansıtılır.
Bu uygulamada DrawGLScreen isimli fonksiyonun içeriğini değiştireceğiz. Ekrana
önce yapılacak değişikliklerin daha iyi anlaşılması için x, y ve z eksenlerini
belirten bir koordinat sistemi çizdireceğiz, ardından bir küp çizdireceğiz.
Şimi DrawGLScreen fonksiyonumuzu inceleyelim :
int DrawGLScene(GLvoid)
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glLoadIdentity();
    DrawCoordiateSystem();
    glLineWidth(1.0f);
    glTranslatef(xTranslate,yTranslate,zTranslate);
    glRotatef(rotateAngle,xRotate,yRotate,zRotate);
    if (filled)
       glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
    else
       glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);
    glBegin(GL_QUADS);
    glColor3f(0.0f,0.5f,0.1f);
    glVertex3f(1.0f,1.0f,-1.0f);
    glVertex3f(-1.0f,1.0f,-1.0f);
    glVertex3f(-1.0f,1.0f,1.0f);
    glVertex3f(1.0f,1.0f,1.0f);
    glColor3f(0.0f,0.4f,0.0f);
    glVertex3f(1.0f,-1.0f,1.0f);
    glVertex3f(-1.0f,-1.0f,1.0f);
    glVertex3f(-1.0f,-1.0f,-1.0f);
    glVertex3f(1.0f,-1.0f,-1.0f);
    glColor3f(0.0f,0.2f,0.7f);
    glVertex3f(1.0f,1.0f,1.0f);
    glVertex3f(-1.0f,1.0f,1.0f);
    glVertex3f(-1.0f,-1.0f,1.0f);
    glVertex3f(1.0f,-1.0f,1.0f);
    glColor3f(0.0f,0.0f,0.5f);
    glVertex3f(1.0f,-1.0f,-1.0f);
    glVertex3f(-1.0f,-1.0f,-1.0f);
    glVertex3f(-1.0f,1.0f,-1.0f);
    glVertex3f(1.0f,1.0f,-1.0f);
    glColor3f(0.8f,0.1f,0.2f);
    glVertex3f(-1.0f,1.0f,1.0f);
    glVertex3f(-1.0f,1.0f,-1.0f);
    glVertex3f(-1.0f,-1.0f,-1.0f);
    glVertex3f(-1.0f,-1.0f,1.0f);
    glColor3f(0.0f,0.8f,0.0f);
    glVertex3f(1.0f,1.0f,-1.0f);
    glVertex3f(1.0f,1.0f,1.0f);
    glVertex3f(1.0f,-1.0f,1.0f);
    glVertex3f(1.0f,-1.0f,-1.0f);
    glEnd();
    if (rotate
== TRUE)
       rotateAngle += 0.25f;
    return TRUE;
}
|
Fonksiyonda glClear
fonksiyonu ile görüntü tamponunu temizledikten sonra, koordinat sistemini çizen
DrawCoordiateSystem isimli fonksiyonu çağırıyoruz. Bu fonksiyonun içeriği de
şöyledir :
void DrawCoordiateSystem()
{
    glLineWidth(3.0f);
    //x ekseni
    glBegin(GL_LINES);
    glColor3f(1.0f,1.0f,1.0f);
    glVertex3f(-4.0f,-3.0f,-9.0f);
    glVertex3f(1.0f,-3.0f,-9.0f);
    glEnd();
    //y ekseni
    glBegin(GL_LINES);
    glColor3f(1.0f,1.0f,1.0f);
    glVertex3f(-4.0f,-3.0f,-9.0f);
    glVertex3f(-4.0f,2.0f,-9.0f);
    glEnd();
    //z ekseni
    glBegin(GL_LINES);
    glColor3f(1.0f,1.0f,1.0f);
    glVertex3f(-4.0f,-3.0f,-9.0f);
    glVertex3f(-4.0f,-3.0f,-14.0f);
    glEnd();
}
|
DrawCoordiateSystem
fonksiyonundan incelemeye başlayalım. Bu fonksiyonda x, y ve z eksenlerini ifade
edecek üç çizgi çizdireceğiz. Eksenlerin diğer çizimlerden ayırd edilebilmesi
için öncelikle çizgi kalınlığını değiştiriyoruz. OpenGL kütüphanesinin glLineWidth
isimli API fonksiyonu çizgi kalınlığını değiştirir. Bu fonksiyonun prototip
bildirimine bakalım :
void
glLineWidth (
    GLfloat width //Çizgi kalınlığı
); |
glLineWidth API
fonksiyonu float türünde bir parametre alır. Bu parametre çizgi kalınlığını
belirtir. Varsayılan çizgi kalınlığı 1.0dır. Koordinat sisteminin eksenlerini
çizmek için glLineWidth fonksiyonunu 3.0f parametresini geçerek çağırıyoruz
ve çizgi kalınlığının 3.0 olmasını sağlıyoruz. Eksen çizgilerini çizmek için,
çizimi GL_LINES sembolik sabiti ile başlatıp çizim rengini beyaz yapıyoruz.
Ardından eksen çizgilerini glVertex3f fonksiyonunu kullanarak çiziyoruz.
Eksen çizgilerinin
çizimini tamamladıktan sonra kübün çizimine geçiyoruz. Çizgi kalınlığını tekrar
1.0 yapmak için glLineWidth fonksiyonunu 1.0f parametresini geçerek çağırıyoruz.
Şimdi döndürme ve öteleme işlemlerini yapacak olan fonksiyonları çağırabiliriz.
Koordinat eksenlerinin bu işlemlerden etkilenmesini önlemek için eksen çizimlerini
bu fonksiyonları çağırmadan önce yapıyoruz. Öteleme işlemi için glTranslatef,
döndürme işlemi için ise glRotatef fonksiyonunu uygun parametre değişkenlerini
geçerek çağırıyoruz. Bu fonksiyonlara geçilen parametre değişkenleri, global
alanda tanımlamış olduğumuz değişkenlerdir. Bu değişkenlerin değerlerini uygulamanın
çalışma zamanında kullanıcının klavyeden gireceği bilgilere göre güncelleyeceğiz.
Böylece her çizim öncesi parametre değişkenlerinin değerleri, dolayısıyla döndürme
ve öteleme matrislerinin değerleri değişmiş olacak.
Döndürme ve öteleme
işlemlerinin ardından kübün çizimine geçmeden önce çizilecek poligonun çizim
modunu belirlemek üzere glPolygonMode OpenGL API fonksiyonunu çağırıyoruz. Bu
fonksiyonun prototip bildirimini hatırlayalım :
void
glPolygonMode (
    GLenum face, //İşlemden etkilenecek olan yüzeyler
    GLenum mode //Çizim biçimi
); |
glPolygonMode OpenGL
API fonksiyonu, çizilecek kapalı alanın pikselleştirilme biçimini ayarlar. Fonksiyonun
iki parametre değişkeni bulunmaktadır. İlk parametre değişkeni verilen özellikten
etkilenecek olan yüzeyi veya yüzeyleri ifade eder. Bu parametre değişkeni yerine
G_FRONT sembolik sabiti geçilirse sadece ön yüzeyler, GL_BACK sembolik sabiti
geçilirse sadece arka yüzeyler, GL_FRONT_AND_BACK sembolik sabiti geçilirse
hem ön hem de arka yüzeyler atanacak özellikten etkilenecektir. Fonksiyonun
ikinci parametre değişkeni atanacak olan özelliği ifade eder. Bu parametre değişkeni
yerine GL_POINT sembolik sabiti geçilirse çizilecek olan kapalı alan noktalarla
gösterilir, GL_LINE sembolik sabiti geçilirse çizilecek olan kapalı alan alanın
kenarlarından geçen çizgilerle gösterilir, GL_FILL sembolik sabiti geçilirse
çizilecek olan kapalı alan yüzeyleri boyanarak gösterilir. DrawGLScene fonksiyonunda,
eğer filled değişkeninin değeri TRUE ise çizim GL_FILL modunda, filled değişkeninin
değeri FALSE ise çizim GL_LINE modunda yapılıyor. Kullanıcının klavyeden gireceği
bilgilere göre filled değişkeninin değeri değiştirilerek uygulamanın çalışma
zamanında çizim modunu değiştirmek mümkün olmaktadır.
Çizim modunu da
ayarladıktan sonra küp yüzeylerinin çizimine geçiyoruz. Çizimi GL_QUADS modunda
başlatarak küp yüzeylerini dört köşe koordinatlarının değerlerini geçerek çiziyoruz.
Her yüzey için glColor3f API fonksiyonu ile başka renk atıyoruz. Bu sayede yaptığımız
işlemi uygulamanın çalışma zamanında görebilmemiz kolaylaşacaktır. Küp yüzeylerinin
çizimini tamamladıktan sonra eğer çizim döndürülmekte ise, dönme açısının değerini
0.25f kadar artırıyoruz. Her çizim sonrası bu değer artırılarak sürekli bir
dönme işleminin yapılması sağlanmaktadır.
Çizim fonksiyonumuzdan
sonra şimdi pencere fonksiyonumuzu inceleyelim. Pencere fonksiyonunda, gelen
mesajları dinleyerek kullanıcının klavyeden gireceği bilgiler doğrultusunda,
uygulamada kullandığımız global değişkenlerin değerlerini değiştireceğiz. Önce
fonksiyonumuzu inceleyelim :
LRESULT CALLBACK
WndProc (HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam)
{
    static HWND hWndChild;
   
switch (uMsg) {
       case WM_CLOSE :
          PostQuitMessage(0);
          return 0;
       case WM_SIZE
:
          ReSizeGLScene(LOWORD(lParam),HIWORD(lParam));
          return 0;
       case WM_COMMAND
:
          sbreak;
       case WM_CHAR
:
          if (wParam
== t)
             rotate = rotate == TRUE ? FALSE : TRUE;
         
else if (wParam == x)
             xRotate =
abs(xRotate - 1.0f);
         
else if (wParam == y)
             yRotate =
abs(yRotate - 1.0f);
         
else if (wParam == z)
             zRotate =
abs(zRotate - 1.0f);
         
else if (wParam == f)
             filled =
filled == TRUE ? FALSE : TRUE;
         
else if (wParam == r)
             xTranslate
+= 0.25f;
         
else if (wParam == l)
             xTranslate
-= 0.25f;
         
else if (wParam == u)
             yTranslate
+= 0.25f;
         
else if (wParam == d)
             yTranslate
-= 0.25f;
         
else if (wParam == i)
             zTranslate
+= 0.25f;
         
else if (wParam == o)
             zTranslate
-= 0.25f;
         
break;
     }
     return
DefWindowProc(hWnd,uMsg,wParam,lParam);
}
|
Kullanıcının klavyeden
girdiği bilgiler, pencere fonksiyonuna WM_CHAR mesajı aracılığı ile iletilirler.
Mesaj döngüsünün işlendiği switch-case ifadesinde, WM_CHAR mesajının alınması
durumunda önceden belirlediğimiz bazı karakterlerin girildiğinin anlaşılması
durumunda belli işlemlerin yapılmasını sağlıyoruz. Klavyeden t tuşuna basıldığında
çizimin döndürülme durumunu ayarlıyoruz. Kullanıcı bir kere t tuşuna bastığında
rotate isimli değişkenin değeri TRUE olur ve çizim döndürülür. Klavyeden tekrar
t tuşuna basıldığında rotate isimli değişkenin değeri FALSE olur ve döndürme
işlemi durdurulur. Klavyeden x, y veya z tuşlarına basılması durumunda xRotate
, yRotate ve zRotate isimli değişkenlerin değerlerini değiştiriyoruz. Bu tuşlara
ilk kez basılması durumunda değişkenlere 1.0 değeri atanır ve değeri 1.0 olan
eksenler etrafında döndürme işlemi yapılır. Klavyeden tekrar x, y veya z tuşlarına
basılması durumunda bu değişkenlerin değerleri 0.0 olur ve o eksen etrafındaki
döndürme işlemi sonlandırılır.
Klavyeden f tuşuna
basılması çizimin biçimini belirlemektedir. f tuşuna ilk kez basılması durumunda
filled isimli değişkenin değeri TRUE olur ve çizilen kübün yüzeyleri boyanır.
Klavyeden tekrar f tuşuna basıldığında ise filled değişkeninin değeri FALSE
olur ve küp sadece kenarlarını sınırlayan çizgilerle gösterilir.
Klavyeden r ve
l tuşlarına basılması çizimin x ekseni doğrultusundaki öteleme miktarını değiştirir.
Bu tuşlarla çizim sola ya da sağa doğru kaydırılabilir. Klavyeden u veya d
tuşlarına basılması çizimin y ekseni doğrultusundaki öteleme miktarını değiştirir.
Bu tuşlarla çizim yukarıya veya aşağıya doğru kaydırılabilir. Klavyeden o
veya i tuşlarına basılması çizimin z ekseni doğrultusundaki öteleme miktarını
değiştirir. Bu tuşlarla çizime yaklaşılması veya çizimden uzaklaşılması sağlanır.
Uygulamanın çalışma
zamanındaki görünümü şu şekillerde olacaktır :
   
  
Bu yazımızda OpenGL
ile eksenler boyunca öteleme ve eksenler etrafında döndürme işlemlerinin nasıl
yapıldığını ele aldık. Yapmış olduğumuz uygulamayı buradan
indirebilirsiniz. Bir sonraki yazımızda görüşmek üzere, mutlu günler dilerim.
* Kaynaklar
:
   - OpenGL Platform SDK Help Book
Makale:
C ve OpenGL : Döndürme ve Öteleme İşlemleri C ve Sistem Programlama Çiğdem Çavdaroğlu
|