| WIN32 API Kullanarak OpenGL Programlamaya Giriş 
 (1937 kelime)
 (706 okuma)
  
 
 
 
 WIN32 API Kullanarak OpenGL Programlamaya Giriş://Bu programda windowsta bir
opengl programı yaratmayı ve ekrana basit bir çizim yapmayı
 //işleyeceğiz :) Anlatımları mümkün olduğunca yeni başlayanlara
yönelik hazırlamaya çalıştım.
 //Yine de anlayabilmek için programlamadan orta derecede haberdar
olmanız gerek.
 
 #define WIN32_LEAN_AND_MEAN
 //Bunu yazarak windows headerlarındaki bize yaramayacak
fazlalıkları ayırıyoruz.Böylece
 //programımızın *.exe si daha az yer kaplıyor.
 
 #define SCREEN_WIDTH 640
 #define SCREEN_HEIGHT 480
 #define SCREEN_DEPTH 32
 //en boy ve renk derinligi için tanımlamalar
 
 #include <windows.h>
 //Windows fonksiyon ve tanımlarının olduğu dosyayı
ekliyoruz.
 
 #include <gl\gl.h>
 #include <gl\glu.h>
 //OpenGL tanımlamalarını ve fonksiyonları kullanmak
için bu dosyaları ekliyoruz.
 
 #pragma comment(lib,"opengl32.lib")
 #pragma comment(lib,"glu32.lib")
 //OpenGL kütüphanelerini ekliyoruz.Bunu Project\Settings
Link kısmına gidipte yapabilirdik.
 
 HDC g_hdc;
 //device context(programın sonunda açıklama
yapmaya çalıştım ;))
 
 HGLRC g_hrc;
 //rendering context yine çizim için gerekli
 
 float aci=0.0f;
 //ekranda bir üçgeni döndürmek
için kullanacağız
 
 
 LRESULT CALLBACK WndProc (HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam);
 //bu fonksiyon windows'un programa yolladığı mesajları
kontrol etmemizi sağlıyor.
 //bu mesajlar;boy değiştirme,kapanma,hareket etme gibi şeyler.
 //parametreler (hwnd - penceremizi belirtiyor,iMsg mesajı,wParam ve lParam
gelen mesaj hakkında
 //ek bilgi içeriyor.)
 
 
 int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR
 lpCmdLine,int nShowCmd)
 //dos uygulamalarındaki main() windows programlarında
WinMain'e dönüşüyor.Bu fonksiyon geriye
 //int döndürüyor.int'ten sonra gelen "WINAPI" bu fonksiyonun
programın başlangıç noktası olduğunu belirtiyor.
 //Şimdi parametrelere gelelim...
 //hInstance     - programın o anı için bilgiler
tutuyor.
 //hPrevInstance - programın bir önceki anı için bilgiler tutuyor(O_o).Bu
tüm win32 programlarında NULL
 //                 
yani sıfır işimize yaramayacak.
 //lpCmdLine     - program çalıştırılırken komut
satırında exe nin yanına yazılanlar bu değişkene
 //                 
gönderiliyor.Örnek : "ilkprog.exe deneme 1" yazarsak lpCmdLine
"deneme 1" olur.
 //nShowCmd      - program ilk çalıştırıldığında
penceremizin nasıl gösterilceğini belirten değişken.
 {
 //evet programımız başladı.şimdi
yapmamız gereken bir pencere yaratmak.
 //bunu yapmak için WNDCLASSEX adındaki yapıyı kullanacağız.Bu
yapının içini doldurup,
 //windows'a register edip bu yapıdaki özelliklere
sahip bir pencere yaratabiliriz.
 WNDCLASSEX winclass;
 ///windows class(sınıf)
 
 HWND hwnd;
 //hwnd - yaratacağımız pencereyi
kullanmamızı saglayacak değişken.(pencereyi yarattıktan sonra daha
 //iyi göreceksiniz.)
 
 MSG msg;
 //windows'un programımıza gönderebileceği
tonlarca mesaj var.Bu mesajları bu değişkenle kontrol
 //edeceğiz.
 
 bool bBitti;
 //ve son olarak programın sonlanıp
sonlanmadığını kontrol etmemizi sağlayacak değişken.
 
 //şimdi winclass'ın içini
dolduracağız.Bu biraz uzun gelebilir ve burdaki her şey anlaşılamayabilir.
 //ama burda yazılanlar her programda nerdeyse aynı yani
ilerde bunları bir fonksiyona koyup bir daha
 //yüzüne pek bakmadan kullanabilirsiniz.
 //Neyse niye uzattım ki bu kadar işte winclass... :)
 winclass.cbSize=sizeof(WNDCLASSEX);      
             //yapının boyutunu belirtiyoruz
 winclass.style=CS_HREDRAW|CS_VREDRAW;      
         //pencere
dikey veya yatayda değişikliğe uğrarsa
 //tekrar çizilmesini
istiyoruz
 winclass.lpfnWndProc=WndProc;      
               
 //windows mesajlarını halledicek fonksiyonumuz
 winclass.cbClsExtra=0;      
               
         //bu ikisi
hep sıfır
 winclass.cbWndExtra=0;      
               
         //^^^^^^^^^^
 winclass.hInstance=hInstance;      
                 //WinMain'deki hInstance'ı hatırladınız mı işte burda
 //kullanıyoruz.
 winclass.hIcon=LoadIcon(NULL,IDI_APPLICATION);  
     //program ikonunu yüklüyoruz.(alt+tab
yapınca çıkan)
 winclass.hCursor=LoadCursor(NULL,IDC_ARROW);        //Cursor  olarak ok yüklüyoruz.
 winclass.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);  
 //background'u beyaza boyuyoruz.
 winclass.lpszMenuName=NULL;      
               
     //menümüz yok.
 winclass.lpszClassName="patlican";      
           
 //class ismi.buna istediğiniz ismi verin :)
 winclass.hIconSm=LoadIcon(NULL,IDI_WINLOGO);  
     //küçük ikon
(pencerenin sol üst köşesinde çıkan)
 
 //evet winclass'ı doldurduk şimdi
bunu kayıt etmemiz lazım bunu aşağıdaki gibi yapıyoruz.
 //eğer register ederken bi sorun çıkarsa programı
bitiriyoruz.Ama genelde sorun çıkmaz :P
 if(RegisterClassEx(&winclass)==NULL)
 return 0;
 
 
 //kayıt işi bitti şimdi penceremizi
yaratabiliriz.Bunun için CreateWindowEx(); fonksiyonunu kullanıyoruz.
 //bu fonksiyonunda birçok parametresi var.bu fonksiyon
geriye,yarattığı pencereyi tanımlayan bir değer
 //gönderiyor bunu hwnd ye koyuyoruz böylece
yaratılan pencereyi kontrol edebiliriz.
 hwnd = CreateWindowEx(NULL,      
               
         //still
için extra bilgi yok
 "patlican",      
                 //class ismimiz yukardakiyle aynı olmalı
 "İlk OpenGL Programımız",      
     //programın tepesinde yazan
isim
 WS_OVERLAPPEDWINDOW,      
         //pencerenin
stili,ufaltma kapama buttonları var cervesi var vs.
 100,100,          
           
     //pencerenin sol üst köşesinin koordinatları
 SCREEN_WIDTH,SCREEN_HEIGHT,      
 //pencerenin genişliği ve boyu
 NULL,          
                     //pencerenin parent'ı
yok yani ona sahip bir üst pencere yok
 NULL,          
               
     //pencerenin menüsü
yok.
 hInstance,      
                 //hInstance'ı burda da yazıyoruz.
 NULL);          
                 //extra parametre yok
 //şimdi pencerenin yaratılmasında
hata oluşup oluşmadığını kontrol ediyoruz.Eğer hata oluşmuşsa hwnd
 //NULL olur.Bir hata varsa prorgramı bitiriyoruz.
 if(hwnd==NULL)
 return 0;
 
 //penceremizi yarattıktan sonra
artık opengl ile alakalı işlere başlayabiliriz.ilk olarak
 //pixel format oluşturacagız.
 PIXELFORMATDESCRIPTOR pfd;
 //pixel format yapısı
 
 //şimdi pixel format yapısını dolduracağız
 pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR);  
     //yapının boyutu
 pfd.nVersion = 1;          
               
     //bunun her zaman 1 olması
lazım
 pfd.dwFlags = PFD_DRAW_TO_WINDOW      
         //pencereye
cizimi,
 | PFD_SUPPORT_OPENGL                //opengl'i
 | PFD_SUPPORT_GDI              
     //gdi cizimlerini
 | PFD_DOUBLEBUFFER;                //ve double bufferı destekleyen bir pixel format
 pfd.dwLayerMask = PFD_MAIN_PLANE;      
         //bu dikkate
alınmıyor.
 pfd.iPixelType = PFD_TYPE_RGBA;      
           
 //renk olarak kırmızı yesil mavi ve
 //alpha(şeffalık için) istiyoruz
 pfd.cColorBits = SCREEN_DEPTH;      
           
 //renk derinligi
 pfd.cDepthBits = 16;          
                 //bu z bufferın derinligi
 pfd.cAccumBits = 0;          
           
         //bunlar şu an gerekli diil
 pfd.cStencilBits = 0;          
                 //stencil buffer da istemiyoruz
 
 //yapıyı doldurduk. :D
 
 g_hdc=GetDC(hwnd);
 //penceremizin "device context"ini
alıyoruz.bu çizim yapmamız için gerekli.
 
 int nPixelFormat;
 if ((nPixelFormat=ChoosePixelFormat(g_hdc,&pfd))==FALSE
)
 //ChoosePixelFormat
fonksiyonu yukarda belirtigimiz özelliklere en iyi uyan pixel formatı
seçer.
 //ve geri bu pixel formatı belirten bir sayi döndürür.
 {
 //hata olusursa
bir mesaj kutusu ile haberdar ediyoruz kullanıcıyı.ve programı bitiriyoruz.
 MessageBox(NULL, "Pixel Format seçme
başarısız!", "Hata", MB_OK);
 PostQuitMessage(0);
 }
 
 if (SetPixelFormat(g_hdc, nPixelFormat, &pfd) == FALSE)
 //seçilen pixel formatı kullanmak
için bu fonksiyonu kullanıyoruz.
 {
 //yine bir
uyarı mesajı ve programı bitiriyoruz.
 MessageBox(NULL, "SetPixelFormat başarısız!",
"Hata", MB_OK);
 PostQuitMessage(0);
 
 }
 
 
 g_hrc=wglCreateContext(g_hdc);
 //son olarak "rendering context"i
yaratıyoruz
 wglMakeCurrent(g_hdc,g_hrc);
 //ve g_hdc ye seçiyoruz.böylece
artık g_hdc'yi kullanarak çizim yapabiliriz."rendering context"i
 //oluşturmadan önce pixel formatı yaratmalıyız.ayrıca
bir kaç tane "rendering context"
 //yaratıp ayrı pencerelere render yapabiliriz.(harita
editörlerinde olduğu gibi)
 //Ama bu konumuzun dışında :)
 
 ///Penceremizi gösterip update
ediyoruz.hwnd işte burda ve pencere ile ilgili fonksiyonlarda kullanılıyor.
 ShowWindow(hwnd,nShowCmd);
 UpdateWindow(hwnd);
 
 bBitti=false;
 //programın bitmediğini belirten
değişken
 
 while(bBitti==false)
 //burda döngümüze
başlıyoruz.Programı bitirmek istedigimizde bBitti değişkenini true yapmamız
yeterli.
 {
 //bu fonksiyon
eğer bir mesaj varsa doğru olur.
 //Ve o mesajı 'PM_REMOVE' olduğu için
mesaj kuyruğundan kaldırır.
 if(PeekMessage(&msg, NULL, NULL,
NULL, PM_REMOVE))
 {
 //eger gönderilen mesaj çıkma mesajı ise yani kullanıcı
pencereyi kapatmak istemişse programı
 //bitiriyoruz.
 if (msg.message
== WM_QUIT)
 bBitti
= true;
 //virtual key kodlarını character kodlarına çeviriyor gerekli
:)
 TranslateMessage(&msg);
 //mesajı windos'a geri yolluyor.
 DispatchMessage(&msg);
 }
 //evet programızın
en önemli kısmı burası.Buranın dışında yaptığımız herşey her programımızda
neredeyse
 //aynı kalıcak.Burda ekrana bir üçgen
cizip döndüreceğiz.En azında OpenGL ile birşey çizmiş olucaz
bu
 //kadar koddan sonra :)
 
 //****rendering
 //önce ekranı ve depth bufferı
temizliyoruz.
 
 glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
 //sonra modelview
matrisi birim matrise eşitliyoruz.yani her frame(kare)de herşey ilk haline
dönüyor.
 glLoadIdentity();
 //kamera her
frame basında 0,0,0 da olduğu için 5 birim geri hareket ediyoruz
 //böylece ne çizdiğimizi
göreceğiz.Bunu yapmak yerine çizdigimiz objenin z degerini -5
yapabilirdik
 //ama ben geri hareket etmeyi tercih
ettim :)
 //X      //Y  //Z
 
 glTranslatef(0.0f,0.0f,-5.0f);
 //glRotatef belli
bir açı kadar bir eksen etrafında dönme sağlıyor.
 glRotatef(aci,0.0f,0.0f,1.0f);
 //açıyı
artırıyoruz böylece dönme sürekli oluyor
 aci+=0.5f;
 //eğer tam bir
tur atmışsak baştan başlıyoruz
 if(aci>=360.0f) aci=0.0f;
 //evet OpenGL
de birşey çizmeden önce glBegin(); ile ne çizeceğimizi
belirtiyoruz.
 //biz burda GL_TRIANGLES yazdık şimdi
opengl yazdığım üç noktayı birleştirip bir üçgen
 //yapacağını biliyor.
 glBegin(GL_TRIANGLES);
 //birinci nokta tam merkezde
 glVertex3f(0.0f,0.0f,0.0f);
 //ikincisi onun sağında
 glVertex3f(1.0f,0.0f,0.0f);
 //üçüncü nokta merkezdekinin üstünde
 glVertex3f(0.0f,1.0f,0.0f);
 //çizim
bitti
 glEnd();
 //çizim
yaparken noktaların koordinatlarını saat yönünün tersinde
yazmak iyi bir alışkanlık.
 //ama mecbur diilsiniz tabi :)
 
 
 SwapBuffers(g_hdc);
 //SwapBuffers();
nedir yahu diyenler olabilir.
 //Hani pixel format yaratırken şöyle
bişey yazmıştık PFD_DOUBLEBUFFER;
 //şimdi bizim çizim için
iki tane alanımız var bunlardan biri ön yüzey digeri arka yüzey
 //kullanıcı sadece ön yüzü
görüyor biz ise çizimlerimizi arka yüzeye yapıyoruz.
 //çizimleri bitirince arka yüzü
bu komutla öne getiriyoruz ve kullanıcı ne çizdiğimizi görüyor.
 //bunun faydası görüntünün
titremesini önlemsi.inanmayan arkadaşlar pixelformattan
 //PFD_DOUBLEBUFFER 'ı kaldırıp farkı
görebilirler :]
 
 
 }
 //bu noktaya gelindiğinde program
bitmek üzere.Ama programı bitirmeden önce yapmamız
 //gereken bir kaç sey daha var.Öncelikle yarattığımız
pencereyi aşağıdaki basit fonksiyonla
 //yok ediyoruz hwnd'nin faydaları yine görülüyor.
 DestroyWindow(hwnd);
 //pencereyi yok ettikten sonra sıra
winclass'a geliyor.Onu da verdigimiz adı ve hInstance'ı
 //kullanarak unregister ediyoruz
 UnregisterClass("patlican",hInstance);
 //son olarak msg ın wParam kısmını
döndürüyoruz ve programımız bitiyor.
 return msg.wParam;
 }
 //bu bizim mesaj fonksiyonumuz windowstan gelen farklı
mesajları burada kontrol ediceğiz.Bu
 //programda sadece bir kaç mesaja bakıcaz.
 LRESULT CALLBACK WndProc (HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM
 lParam)
 {
 //pencerinin boyutları değiştiğinde
kullanacağız.
 int  height,width;
 
 switch(iMsg)
 {
 //bu mesaj sadece bir kere pencere
yaratıldığında gelir.Eğer sadece bir kez yapılmasını istediğiniz
 //şeyler varsa mesela pixel format yaratmak o kodu burayada
yazabilirdik.
 case WM_CREATE:
 
 break;
 //bu mesaj pencerenin boyutları
değiştigi zaman gelir.Burada ufak bir OpenGL ayarı yapıyoruz.
 //bunun nedeni pencere boyutları değiştiginde perspektifi
tekrar hesaplamak.
 case WM_SIZE:
 width = LOWORD(lParam);
 height = HIWORD(lParam);
 //pencerenin
yeni genişliği ve boyu lParam içinde.
 
 if (height==0)
 //aşagıda genişliği
boya böleceğiz bu yüzden boyun sıfır olması bir sayının sıfıra
 //bölünmesi ihtimalini doğuruyor
ki buna asla programlarımızda izin vermemeliyiz
 {
 height=1;
 //boyu 1 yapıyoruz böylece hiçbir zaman sıfır olmuyor.
 }
 
 glViewport(0,0,width,height);
 //viewport'u
bütün pencere yapıyoruz istersek daha küçük bir
alanda yapabiliriz.
 //bu alan çizim yapacağımız
bölgeyi belirtiyor.
 
 glMatrixMode(GL_PROJECTION);
 // projeksiyon
matrisini seciyoruz
 glLoadIdentity();
 // ve birim matrise
eşitliyoruz
 
 gluPerspective(45.0f,(GLfloat)width/(GLfloat)height,
1 ,150.0f);
 //perspective
ayarı yapıyoruz.parametreler sırasıyla
 //FOV   - field of view,görüş
alanı
 //ratio - width/height oranı objelerin
nasıl göründüğümü etkiliyor.
 //near clipping plane - objelerin kameradan
ne kadar yakında çizilmeyecegini belirtiyor.
 //          
             kameraya 1
birimden yakın objeler çizilmeyecek
 //far clipping plane - objelerin kameradan
ne kadar uzakta çizilmeyeceğini belirtiyor.
 //          
             kameradan
150 birim ve daha uzaktaki objeler çizilmeyecek.
 
 glMatrixMode(GL_MODELVIEW);
 //tekrar model
matrisine dönüyoruz
 glLoadIdentity();
 //ve birim matrise
eşitliyoruz
 break;
 //bu iki mesaj
pencere kapatılmaya ya da yok edilmeye çalışıldığında gelir.
 //PostQuitMessage(0); WM_QUIT mesajı
gonderir ki bunu da yukardaki döngüde kontrol ediyoruz.
 //(PeekMessage'ın orda)
 //böylece program bitiyor.
 case WM_DESTROY:
 case WM_CLOSE:
 //Program bitmeden önce yarattığımız "rendering context"i seçip
siliyoruz.
 wglMakeCurrent(NULL,NULL);
 wglDeleteContext(g_hrc);
 PostQuitMessage(0);
 break;
 
 }
 //bu fonksiyon butun mesajlar için
normal yapılanları yapıyor.
 return (DefWindowProc(hwnd, iMsg, wParam, lParam));
 }
 
 ////Notlar:
 // Bu program Microsoft Visual C++ 6.0 ile yazılmıştır.
 // Programın çalışması için opengl kütüphanelerinin
bilgisayarınızda bulunması gerekli
 // ayrıca yeni bir proje yaratırken win32 application seçmelisiniz.
 //
 //
 // Bu baya uzun bir program gibi gözüküyor ama yorumlar olmadığı
zaman o kadar da uzun diil.
 // Kısaca şunları yaptık.
 // 1.Pencere classı yaratıp doldurduk.Bunu kaydettik sonra bir pencere yaratık.
 // 2.Ardından pencerenin "device context"ini alıp bunu pixel format yaratmak
için kullandık.
 // 3.Daha sonra wglCreateContext ile "rendering context"i  yaratıyoruz
ve bunu "device context" e seçiyoruz.
 //   Artık çizim yapabiliriz.
 // 4.Çizimlerimizi yapıyoruz ve programı bitiriyoruz.
 //
 // Mesajlar - Windows tarafından programa gönderilen mesajlardır.Siz
mouse ile programı kapatmaya çalışınca
 // ya da pencerenin boyunu değiştirdeğinizde farklı mesajlar oluşur.Bu mesajlar
oluştuğunda programınızın neler
 // yapacağına karar verebilirsiniz.Bunu WndProc fonksiyonu ile sağlıyoruz.
 //
 // HDC - handle to device context.İlk duyduğumda ve gördügümde
ben de ne olduğunu tam anlamamıştım.
 // hdc çıktı yapmanızı sağlayan bi yapı bizim durumuzda bu çıktı
ekrana oluyor ama bu printer ile
 // ya da başka çıktı araçlarıyla da olabilir.
 //
 // HGLRC - "rendering context" HDC ile çizim yapmak için rendering
context oluşturmamız gerekiyor.
 // rendering context OpenGL ile çizim yapmayı sağlıyor.
 //
 // Evet ilk OpenGL programımız bitti.Bu programda yazılan herşeyi hemen anlamanız
pek mümkün diil.
 // Benim tavsiyem programla oynayıp orasını burası değiştirmeniz ve neler
olduğunu görmeniz.Böylece
 // çok daha kolay öğrenebilirsiniz.
 //
 //
 
 Barış S. Uşaklı :: www.oyunyapimi.org :: 2003 :: |