Merhaba; openGL’de bu makalemizde openGL içinde basit(=primitive) tiplerden birisi olan TRIANGLE_FAN kullanarak bir kare çizmeyi ve yön tuşları ile hareket ettirmeyi en temel fonksiyonları kullanarak nasıl gerçekleştirebileceğimize bakacağız. Bunu yaparken hangi fonksiyonu ya da özelliği neden nerede kullandığımıza açıklamalı olarak bakıp hiçbir şeyi ezbere yapmadan openGL’yi temelden öğrenmeye çalışacağız. Ben bazı terimleri opengl içinde kalıplaşmış isimler olduğu için İngilizce olarak kullanmaya devam edeceğim; fakat ilk başta Türkçe karşılığını vermeye ve aynı şekilde bazı terimlerin de başka bir yerden aynı konuya çalışırken yabancılık hissetmemeniz için yanında orijinal İngilizce kullanımına da yer vereceğim. Burada biraz C++ ve temel grafik çizim terimlerini (koordinat sistemi,double buffer, TRIANGLE_FAN vb.) bildiğinizi varsayacağım.
Programlama Dili: C++
Kütüphane: OpenGL
IDE: Visual Studio

Öncelikle her zaman C/C++ kodlarımızda alışageldiğimiz gerekli dosyaları programımıza ekleyerek(=include) başlayacağız. Öncelikle GLTools.h dosyası her openGL programımızda vazgeçemeyeceğimiz bir dosya olacak. GLTools’un C++ sınıflarının her biri kendi başlık(=header) dosyaları bulunurken GLTools.h dosyası bağımsız C tabanlı fonksiyonlardan oluşmaktadır.

Daha sonra ekleyeceğimiz diğer başlık GLShaderManager.h; opengl’de bir shader olmadan ekranda bir şey çizmeniz pek mümkün değil. Shader’ın ne olduğuna ise diğer yazılarımdan bakabilirsiniz. Shader yöneticisi (=ShaderManager) size kendi shader’larınızı yaratmak ve yönetmek imkanı sağlamasının yanında sunduğu bir dizi diğer shader’lar ile en temel çizim(=render) işlemlerini de gerçekleştirmenizi sağlar. Sözü uzatmadan kodu verelim zira heyecan dorukta 🙂

#include "GLTools.h"                // OpenGL araçları
#include <GLShaderManager.h>  // Shader Manager Sınıfı

Burada Windows kullanıcısı olmayanlar için de kodumuza ufak bir kaç şey ekleyelim ki ayrım yapmadan herkes eğlensin değil mi ? Eğer kodu mac ortamında derleyecekseniz burada gerekli başlık dosyaları biraz fark edecektir çünkü GLUT platforma göre farklı davranışlar gösterir.Koda hangi ortamda çalıştığını göstermek için C++ kodumuzda kodun derleyiciye uğradığında ne ile karşılaştığını gösterecek bir değişkene bakmamız lazım bu da mac için __APPLE__ değişkenidir. Eğer platformumuz OSX değilse (yani Windows veya Linux ise) freeglutun statik kütüphane versiyonunu kullanırız. Bu yüzden FREEGLUT_STATIC önişleyicisini (=preprocessor) makromuzda tanımlamalıyız.Koda geçelim:

#ifdef __APPLE__
#include <glut/glut.h> //GLUT için OS X Versiyonu
#else
#define FREEGLUT_STATIC
#include <GL/glut.h>// Windows/Linux FreeGlut eşdeğeri
#endif

GLTools kütüphanesi GBatch adında basit bir konteynır içermektedir. Bu sınıf aşağıdaki tablodaki basit tiplerden herhangi birinin örneğini (=batch) içerebilir ve ayrıca GLShaderManager tarafından sağlanan herhangi bir stock shader kullanılarak çizimin nasıl render edilmesi gerektiğini bildiren bir sınıf nesnesidir. Kullanımı ise birazdan göreceğimiz gibi esrarına nispeten oldukça kolaydır; ama şimdilik sadece kodumuzun başında bu sınıftan nesnemizi yaratalım;

GLBatch squareBatch;

Şimdi bir şekil inşa etmek için diğer temel aracımız olan shaderManager sınıfından bir nesne yaratmamız gerekmekte:

GLShaderManager shaderManager;

Şimdi ShaderManager olayını biraz daha derinlemesine incelemek istiyorum.Merak etmeyin konuyu dağıtmıyoruz sadece taşları yerine oturtmaya çalışıyoruz; fakat bu taşlar şimdilik biraz uzak diyarlardan geliyor olsa da daha sonra kodumuzu konumuzu pılımızı pırtımızı toparlayacağız

OpenGL çekirdek profili(=core profile) içinde hali hazırda ön tanımlı(=built in) bir render hattı(=rendering pipeline) bulundurmaz bu yüzden bir şekli çizime sunmadan önce bir shader belirlemiz gerekmekte. GLShaderManager kullanılmadan önce başlatılması gerekmektedir. (shader’ın opengl içindeki durumu nedeni ile böyle bir tabir kullandık). Yani her opengl programının kaynak kodunun başlarında yukarıdaki gibi bir örnekleme ve başlangıç fonksiyonun içinde shader manager’ın derlenmesi(=compile) ve kendi shader’larına bağlanması(=link) için az sonra göstereceğimiz gibi shaderManager.InitializeStockShaders() kodunu göreceğiz. Şaşırmayalım diye alıştıra alıştıra söyleyelim dedik

Şimdi bu basit programımız için gerekli fonksiyonlara bakalım. Önce en basit fonksiyondan başlamak istiyorum yani programın açıldığı pencerenin boyutları değiştiğinde program nasıl bir tepki gösterecek. Bu fonksiyon ayrıca program ilk çalışmaya başladığında da kullanılacaktır bu yüzden olmazsa olmaz diyebiliriz. Pencere boyutu değiştiğinde görüş alanını(=viewport) yeniden belirlememiz lazım ki şeklimiz ekrandan dışarı çıkmasın (ama merak etmeyin çıksa da bir yerimize batmayacaktır ). İleriki örneklerde göreceğiz ki ayrıca projeksiyon matriksini de yeniden belirlememizi sağlayacak fonksiyondur. Parametre olarak pencere için yeni (sadece değiştirildiğinde değil ilk yaratıldığında da kullanıldığını unutmayın!) genişlik ve yükseklik değerlerini alır ve kullandığımız koordinat sisteminden ekran koordinat sistemine dönüşümü sağlar (merak etmeyin bunu opengl deki hazır bir fonksiyonu kullanarak gerçekleştireceğiz.). Daha fazla heyecanlandırmadan kodu sunayım efendim:

    void ChangeSize(int w, int h){
        glViewport(0, 0, w, h);
    }

Biraz şu içerideki fonksiyondan bahsedeyim. İlk iki parametre pencerenin sol alt x ve y koordinatlarıdır. Genelde 0’dır ama pencerenin farklı yerlerine farklı çizimler gerçekleştirmek için bu viewport 0 olmayan parametreler ile kullanılabiliyor. Viewport gerçek ekran koordinatlarını kullanarak opengl’nin çizim yapabileceği bir alan tanımlanmasını sağlar. Eğer pencere boyutlarından daha küçük bir viewport belirlerseniz haliyle çizim küçülecektir. Bu yazdığımız fonksiyonu ise main içerisinde glutReshapeFunc(ChangeSize); fonksiyonu ile opengl sistemine kaydını yapmamız gerekiyor fakat daha sonra zaten bunu ayrıntılı olarak göreceğiz ve açıklayacağız.

Karemizin köşe koordinatlarını içeren dizimizi ve kenar büyüklüğünü program dosyası içinde global olarak tanımlıyoruz ki başta tuşları kontrol edeceğimiz fonksiyon olmak üzere tüm fonksiyonlardan karemize istediğimiz gibi müdahele edebilelim.(eğer diğer fonksiyonlarda kullanmayacak olsaydık aşağıdaki init() fonksiyonumuz içinde de karemizi oluşturabilirdik.) Şimdi şeklimizin kenar boyutunu daha sonra rahatça değiştirebilmek için bu değeri kenar adlı bir değişkende saklayıp koordinatları bu değer ile ilişkilendireceğiz ki daha sonra yapılacak bir değişiklikte herbir köşeyi yeniden değiştirmek zorunda kalmayalım.

GLfloat kenar = 0.1f;
GLfloat vKoseler[]={ -kenar, -kenar, 0.0f,
                    kenar, -kenar, 0.0f,
                    kenar,  kenar, 0.0f,
                    -kenar,  kenar, 0.0f};

Sıradaki fonksiyonumuz OpenGL’nin görevlerini yapabilmesi için bazı şeyleri programdan önce gösterip tanıtacağımız fonksiyon olacak. Diğer bir deyişle programa ait tek seferlik ön kurulumları gerçekleştireceğiz. Fonksiyona şöyle bir göz atalım isterseniz:

void init(){
    glClearColor(0.2f, 0.2f, 0.2f, 1.0f );
    shaderManager.InitializeStockShaders();

    squareBatch.Begin(GL_TRIANGLE_FAN, 4);
    squareBatch.CopyVertexData3f(vKoseler);
    squareBatch.End();
}

glClearColor(0.2f, 0.2f, 0.2f, 1.0f ); fonksiyonu ile arkaplan rengini ayarlıyoruz. (burada açık gri bir renk siz dilediğiniz gibi değiştirebilirsiniz). Böylece renk tamponunun(=color buffer) pencereyi daha sonra ekranı temizlerken hangi renge boyayacağını belirlemiş oluyoruz. Fonksiyonun ilk üç parametresi sırası ile kırmızı,yeşil, mavi renk oranlarıdır. Oran dediğimiz için bu değerlerin 0-1 arasında olmasına özen gösteriniz yoksa bir hata almazsınız fakat tüm arkaplana 1den büyük belirlediğiniz renk hakim olacağı için diğer değerlerinizin bir anlamı kalmayacaktır.Gelelim son prametremize yani alfa değerine. Alfa değeri transparan bir efekt oluşturmak veya arkada farklı renkler varsa bunların karışımını(=blending) oluşturmak için kullanılır. Yani kırmızı bir camdan mavi bir cisme bakarken mor rengi elde etmek ve nesnelere yarısaydam özellik katmak gibi işlemlerden bahsediyoruz.

Daha önce belirttiğimiz gibi shaderManager nesnemizin kullanacağı shader yığınına bağlanmasını sağlıyoruz ki daha sonra çizimin gerçekleşeceği fonksiyonda çizimimize katacağımız özelliklere opengl yabancı kalmasın.

Şeklimizin koordinatlarını belirlerken iki boyutlu bir dizi oluşturmadan sıradan bir dizi tanımlamak ne kadar güzel değil mi? Opengl dizimizin elemanlarını sırası ile alır ve uygun koordinatlara gelecek şekilde yerleştirir. Yani her 3 elemandan bir köşe yapmaktadır. Z koordinatları 0 olduğu için şeklimiz iki boyutlu olacaktır fakat sadece bunları değiştirerek 3 boyutlu bir görünüm elde edeceğinizi de düşünmeyin bunun için opengl de bazı özellikleri de aktif hale getirmemiz ve shader’ı mızı bu yönde ayarlamamız gerekecektir ama bu da başka bir makaleye kalsın.

squareBatch.Begin(GL_TRIANGLE_FAN, 4); fonksiyonun prototipi void GLBatch::Begin(GLenum primitive, GLuint nVerts, GLuint nTextureUnits = 0); şeklindedir. Gördüğünüz gibi öncelikle şeklin tipini(aşağıdaki tablodakilerden birini), köşe sayısını belirleyebiliriz. Son parametreyi şimdilik kafaya takmayalım zaten yazmak zorunda olmadığımız bir parametre.

squareBatch.CopyVertexData3f(vKoseler); bu fonksiyon ile batch’imizin koordinatlarını köşe(=vertex) bilgilerini kopyalayarak kolayca güncelleyebiliyoruz. Burayla ilgili son olarak dilerseniz bu alanda yüzey normallerini, renkleri ve texture(=Desen) bilgilerini de aşağıdaki prototipi verilen fonksiyonlar ile batch’inize yükleyebilirsiniz.

void GLBatch::CopyNormalDataf(GLfloat *vNorms); 
void GLBatch::CopyColorData4f(GLfloat *vColors); 
void GLBatch::CopyTexCoordData2f(GLfloat *vTexCoords, GLuint uiTextureLayer);

squareBatch.End(); fonksiyonu ile opengl’e verileri kopyalamak ile işinizin bittiğini belirtmeniz gerekir.Böylece şeklimizin belirli özelliklerini gösterecek işaretçileri ayarlamış oluruz ve sınıfımız şeklin hangi özellikler(=attributes) ile yüklü olduğunu bilebilir. Unutmamamız gereken bir şey de bu fonksiyonun kullanımından sonra şeklimiz için yeni özellikler belirleyemeyiz. Şimdi sıra geldi programımıza biraz daha heyecan katacak bir fonksiyona, basılan tuşların programımıza anlamlı gelmesi ve kendini ona göre tekrar çizmesini sağlayacak fonksiyonumuz yani. Burada opengl bizim için tuşların isimlerini sağlamaktan daha fazla bir şey sunmayacak maalesef. Yani hangi tuşa basıldığını tesbit edip nasıl bir akış izleyeneciğini belirlemek tamamen geliştiricinin yani bizim elimizde. Lafı uzatmadan fonksiyonumuzu tanımlayalım:

void SpecialKeys(int key, int x, int y)

Diğer fonksiyonlarımızda olduğu gibi bu fonksiyonda da ismi pek önemli değil, önemli olan hangi fonksiyonu opengl’nin hangi kayıt fonksiyonuna parametre olarak gönderdiğimizdir.(Mesela bu fonksiyonu glutSpecialFunc(SpecialKeys) ifadesi ile main fonksiyonumuzda kayıt edeceğiz.) İlk parametre basılan tuşun kodu x ve y parametreleri ise fare işlecinin koordinatlarıdır. Fare işleci ile ilgili şimdilik bir işimiz olmayacak ama daha sonraları bunu da kullanmak isteyeceğiz. Burada bu fonksiyon ile ilgili dikkat etmemiz gereken diğer bir ayrıntı da bu adından da anlaşılacağı gibi özel tuşlar için çalışacak bir fonksiyondur;yani ok tuşları page down/up, home tuşları gibi. ASCII tuşları (karakter ve sayılar mesela) için farklı bir fonksiyon oluşturup glutKeyboardFunc(); ile kayıt etmeniz gerekmektedir. Şimdi isterseniz fonksiyonumuzun tamamını verelim açıklamaya daha sonra devam edelim.

void SpecialKeys(int key, int x, int y)
    {
    GLfloat adimBuyuklugu = 0.025f;

    // Sol üst köşe koordinatları
    GLfloat blockX = vKoseler [9]; 
    GLfloat blockY = vKoseler [10]; 

    if(key == GLUT_KEY_UP)
        blockY += adimBuyuklugu;

    if(key == GLUT_KEY_DOWN)
        blockY -= adimBuyuklugu;

    if(key == GLUT_KEY_LEFT)
        blockX -= adimBuyuklugu;

    if(key == GLUT_KEY_RIGHT)
        blockX += adimBuyuklugu;

    // Çarpışma tespiti (=Collision Dedection)
    if(blockX < -1.0f) blockX = -1.0f; //sol kenara dayandık
//sağ kenara dayandık
    if(blockX > (1.0f - kenar * 2)) blockX = 1.0f - kenar * 2;
//alt kenara dayandık
    if(blockY < -1.0f + kenar * 2)  blockY = -1.0f + kenar * 2;
//üst kenara dayandık
    if(blockY > 1.0f) blockY = 1.0f;

    // Köşe koordinatlarını yeniden oluştur.(Sadece x ve y koordinatları)
    vKoseler [0] = blockX;
    vKoseler [1] = blockY - kenar *2;

    vKoseler [3] = blockX + kenar *2;
    vKoseler [4] = blockY - kenar *2;

    vKoseler [6] = blockX + kenar *2;
    vKoseler [7] = blockY;

    vKoseler [9] = blockX;
    vKoseler [10] = blockY;

    squareBatch.CopyVertexData3f(vKoseler);

    glutPostRedisplay();
    }

Öncelikle yön tuşlarından birine basıldığında şeklimizin ne kadar hareket edeceğini ismine nazır adımBuyuklugu değişkenimiz ile belirliyoruz. Bundan sonra şeklimize göre bir referans noktası belirliyoruz ve şeklimizin hareketlerini bu noktamıza göre şekillendireceğiz ben burada en son çizilen noktanın koordinatlarını aldım ve gerek çarpışma tespitini gerek yeni koordinatlarını buna göre oluşturdum. Bundan sonrası biraz mantığınızı çalıştırmak ve geometri bilgisini kullanmak sanırım. Dikkat etmemiz diğer bir nokta da çarpışma tespitidir. Yani şeklimizin ekran dışına taşmadan hareket etmesini sağlamaktır. Muhtemelen yapacağınız her oyun vb. program da çarpışma tesbiti biraz başınızı ağrıtacaktır; opengl nesnelerinizin somut bir nesne olduğunu düşünemeyebilir. Burada dilerseniz alıştırma olarak ikinci bir sabit kare oluşturup bu iki kare için çarpışma tespiti gerçekleştirmeye çalışabilirsiniz. Biraz yardımcı olması için aşağıdaki şekli hazırladım(biraz acemice tamam) burada kenar değişkenin aslında tam kenara değil yarısına tekabül ettiğine dikkat edelim

Köşe koordinatlarını yeniden oluştururken referans noktamıza olan uzaklıkları yeniden hesaplayarak dizimize aktardık ve bu dizimizi de daha önce kullandığımız squareBatch.CopyVertexData3f(vKoseler); ifadesi ile batch’imize yükledik.

glutPostRedisplay(); fonksiyonu ile Opengl’e “hey opengl, bazı şeyler değişti şimdi ekranı yeniden işlemen çizimleri yeniden gerçekleştirmen gerekiyor bir bak istersen!” demektir. Yani şeklimizi yeni koordinatlarla yükledik ve opengl’e hadi çiz dedik basitçe.
Şimdi main fonksiyonundan önce yazacağımız son fonksiyonumuza bakalım. OpenGL’e şekli çiz dedik tamam ama o da ha deyince yapamıyor maalesef bu işi. Bu yüzden kullanılacak shader başta olmak üzere renk ve ekranımıza katacağı yeni özellikleri belirlemek üzere bir fonksiyon yazmalıyız. Neyse özetle GLUT default olarak penceremizi ilk açılışında, boyutu değiştiğinde, küçültülüp-büyütüldüğünde, güncel verilerle yeniden çizim yapılması gerektiğinde (glutPostRedisplay() fonksiyonunu hatırlayın) bu fonksiyonu kullanacaktır. Yani ekranın yeniden render’dan geçmesini sağlamalıyız. Fonksiyona şöyle bir göz atalım isterseniz.

void RenderScene(void)
    {
    // ekranı şu özelliklerle temizle ve oluştur.
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);

    GLfloat kareRengi[] = { 0.98f, 0.525f, 0.12f, 1.0f};
    shaderManager.UseStockShader(GLT_SHADER_IDENTITY, kareRengi);
    squareBatch.Draw();
    // tadaam! Yeni çizim komutlarını ekrana göster
    glutSwapBuffers();
    glutPostRedisplay();
}

Gördüğünüz gibi batch’imizin çizim komutunu(=Draw()) buradan veriyoruz. init() fonksiyonunda da vermek hataya neden olmaz fakat ekranda kalmasını istiyorsak şeklimizin ki çizmemizin amacı budur zannediyorumki ozaman burada bu komutu kullanmak daha uygundur. glutSwapBuffers(); metodu GLUT’a main fonksiyonunda yüklenirken çift tamponlu çalışmak istediğimizi belirttiğimiz için gerekildir. Ayrıca şeklimizin rengini burada belirledik çünkü şeklin çizilmesi için önce shader’ımızın başlatılması uygun özellikte bir shader seçilmesi lazım gelmektedir. İkinci dediğimizin yeri bu fonksiyon olduğu ve seçtiğimiz rengi şeklimizin içine döşeyeme işlemini shader gerçekleştireceği için burada şeklimizin rengini belirlemiş olduk. Şimdilik büyük harflerle yazılı özelliklere takılmayalım isterseniz, bunları daha sonraya bırakalım ve main fonksiyonumuza başlayalım.
Aslında main fonksiyonuna gelmeden programımızı bitirmiş sayıldık yani işin çoğunu bitirdik son rötuşları main fonksiyonunda gerçekleştireceğiz. Mesela pencere adının ve boyutlarının belirlenmesi, yazdığımız fonksiyonların opengl içine kaydının gerçekleştirilmesi ve temel döngünün(açıklayacağım merak etmeyin) oluşturulması.

// GLUT tabanlı programlar için ana giriş
int main(int argc, char* argv[])
    {
    gltSetWorkingDirectory(argv[0]);

    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH);
    glutInitWindowSize(400, 400);
    glutCreateWindow("Ok tuşları ile hareket");

    GLenum err = glewInit();
    if (GLEW_OK != err)
        {
        // Problem: glewInit başarısız bir yerde ciddi sorun var.
        fprintf(stderr, "Error: %s\n", glewGetErrorString(err));
        return 1;
        }

    glutReshapeFunc(ChangeSize);
    glutDisplayFunc(RenderScene);
    glutSpecialFunc(SpecialKeys);

    init();

    glutMainLoop();
    return 0; // Aslında program buraya hiç ulaşamaz 
    }

Şimdi ne yaptığımızı sıra ile inceleyelim: gltSetWorkingDirectory(argv[0]): Bu komut ile o an ki çalışma alanımızı belirliyoruz yani program dosyalarının nereden bakılacağını.Aslında bu windows için gerekli değil çünkü windowsda varsayılan olarak çalışma alanı bu çalıştırılabilir dosyanın bulunduğu dizin ile aynı olsa da MAC OS X platformunda çalışma alanının uygulama paketi içindeki /Resources klasörü olmasını sağlamaktadır. GLUT bu işlemleri otomatik olarak yapsa da bu yöntem tamamen güvenli ve bu tercihler başka bir program tarafından değiştirilmiş olsa da herzaman çalışır. Ayrıca bu yöntem ileride texture ve model verileri yüklerken kullanışlı olacaktır.
glutInit(&argc, argv): eğer var ise komut satırı parametlerini kullanarak GLUT tabanlı kurulumu gerçekleştirmeyi ve kütüphaneyi programa iliştirmek için kullandığımız komut parçası.
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH):daha sonra GLUT a hangi özellikleri kullanarak pencereyi yaratması gerektiğini söylüyoruz. Buradaki özellikler tek başlarına bir makale konusu olabilecekken şöyle belirteyim sırasıyla; çift tampon(=double buffer) kullanmak istediğimizi, RGB renk modunda çalışacağımızı ve derinlik özelliklerinin(kullanmamış olsak da görelim) açık tutulmasını istediğimizi belirtmiş olduk.

Pencere boyutu ve başlığını da ayarladıktan sonra GLEW kütüphanesinin başlatılmasını sağlamalıyız böylece openGL sürücüsünün tüm kayıp girdi noktaları taranarak openGL API’sinin sistemde tam yüklü olduğundan emin olup çizim işlemlerimizin bir hata ile son bulmasını önlüyoruz. Burada asıl önemli olan glewInit() fonksiyonunun çağırılmasıdır. Bunun geriye döndürdüğü değeri hata kontrolü için kullanabilmemiz ise openGL nimetlerinden diyebiliriz.
Yazdığımız tüm fonksiyonların kayıtlarını GLUT’un bize sunduğu glutReshapeFunc(), glutDisplayFunc()ve glutSpecialFunc() fonksiyonları ile gerçekleştiriyoruz. Bunların yerli yerinde kullanılması yani geçilen parametrelerin uygun olarak yazdığımız fonksiyonlar olması çok önemlidir.
İnit() fonksiyonumuzun kullanımını burada görüyoruz. Aslında init fonksiyonumuz GLUT ile ilgili bir işlem yapmıyor fakat openGL’in kendisinin bize gerekli araçları sağlamasını ve önyüklemeleri yapmasını sağlıyor. Son olarak ana döngümüzü oluşturuyoruz ve programımız çalışmaya hazır. Bu fonksiyondan da kısaca şöyle bahsedelim: ana pencere kapanana kadar bu fonksiyon asla geri dönmez ve her programda bir kere çağrılabilir. Bu fonksiyon işletim sistemine bağlı mesajların, basılan tuşların vb. hayati olayların işleme konulmasını sağlar. Ayrıca az önce kaydını gerçekleştirdiğimiz fonksiyonlarımızın yerli yerinde kullanılıp uygun bir şekilde çağırıldığını garanti eder. Return ifadesi ise c++ derleyicisinin hata vermemesini sağlamak için gereklidir buraya ulaşmadan zaten programımız güzel bir şekilde çalışıyordur. Şimdi tüm kodu yayınlayıp her şeyi toparlamaya çalışalım.

#include "GLTools.h"            // OpenGL araçları
#include <GLShaderManager.h>   // Shader Manager Sınıfı

#ifdef __APPLE__
#include <glut/glut.h> //GLUT için OS X Versiyonu
#else
#define FREEGLUT_STATIC
#include <GL/glut.h>// Windows/Linux FreeGlut eşdeğeri
#endif

GLBatch squareBatch;
GLShaderManager shaderManager;

GLfloat kenar = 0.1f;
GLfloat vKoseler[]={ -kenar, -kenar, 0.0f,
                    kenar, -kenar, 0.0f,
                kenar,  kenar, 0.0f,
               -kenar,  kenar, 0.0f};
void init(){
    glClearColor(0.2f, 0.2f, 0.2f, 1.0f );
    shaderManager.InitializeStockShaders();

    squareBatch.Begin(GL_TRIANGLE_FAN, 4);
    squareBatch.CopyVertexData3f(vKoseler);
    squareBatch.End();
    }

void SpecialKeys(int key, int x, int y)
    {
    GLfloat adimBuyuklugu = 0.025f;

    // Sol üst köşe koordinatları
    GLfloat blockX = vKoseler [9]; 
    GLfloat blockY = vKoseler [10]; 

    if(key == GLUT_KEY_UP)
        blockY += adimBuyuklugu;

    if(key == GLUT_KEY_DOWN)
        blockY -= adimBuyuklugu;

    if(key == GLUT_KEY_LEFT)
        blockX -= adimBuyuklugu;

    if(key == GLUT_KEY_RIGHT)
        blockX += adimBuyuklugu;

    // Çarpışma tespiti (=Collision Dedection)
    if(blockX < -1.0f) blockX = -1.0f; //sol kenara dayandık
//sağ kenara dayandık
    if(blockX > (1.0f - kenar * 2)) blockX = 1.0f - kenar * 2;
//alt kenara dayandık
    if(blockY > -1.0f + kenar * 2)  blockY = -1.0f + kenar * 2;
//üst kenara dayandık
    if(blockY > 1.0f) blockY = 1.0f;

    // Köşe koordinatlarını yeniden oluştur.(Sadece x ve y koordinatları)
    vKoseler [0] = blockX;
    vKoseler [1] = blockY - kenar *2;

    vKoseler [3] = blockX + kenar *2;
    vKoseler [4] = blockY - kenar *2;

    vKoseler [6] = blockX + kenar *2;
    vKoseler [7] = blockY;

    vKoseler [9] = blockX;
    vKoseler [10] = blockY;

    squareBatch.CopyVertexData3f(vKoseler);

    glutPostRedisplay();
    }
void RenderScene(void)
    {
    // ekranı şu özelliklerle temizle ve oluştur.
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);

    GLfloat arkaPlanRengi[] = { 0.98f, 0.525f, 0.12f, 1.0f};
    shaderManager.UseStockShader(GLT_SHADER_IDENTITY, arkaPlanRengi);
    squareBatch.Draw();
    // tadaam! Yeni çizim komutlarını ekrana göster
    glutSwapBuffers();
    glutPostRedisplay();
    }

void ChangeSize(int w, int h)
    {
    glViewport(0, 0, w, h);
    }

// GLUT tabanlı programlar için ana giriş
int main(int argc, char* argv[])
    {
    gltSetWorkingDirectory(argv[0]);

    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH);
    glutInitWindowSize(400, 400);
    glutCreateWindow("Ok tuşları ile hareket");

    GLenum err = glewInit();
    if (GLEW_OK != err)
        {
        // Problem: glewInit başarısız bir yerde ciddi sorun var.
        fprintf(stderr, "Error: %s\n", glewGetErrorString(err));
        return 1;
        }

    glutReshapeFunc(ChangeSize);
    glutDisplayFunc(RenderScene);
    glutSpecialFunc(SpecialKeys);

    init();

    glutMainLoop();
    return 0; // Aslında program buraya hiç ulaşamaz ;)
    }

Programımızın ekran çıktısı:

Reklamlar

Bir Cevap Yazın

Aşağıya bilgilerinizi girin veya oturum açmak için bir simgeye tıklayın:

WordPress.com Logosu

WordPress.com hesabınızı kullanarak yorum yapıyorsunuz. Çıkış  Yap / Değiştir )

Twitter resmi

Twitter hesabınızı kullanarak yorum yapıyorsunuz. Çıkış  Yap / Değiştir )

Facebook fotoğrafı

Facebook hesabınızı kullanarak yorum yapıyorsunuz. Çıkış  Yap / Değiştir )

Google+ fotoğrafı

Google+ hesabınızı kullanarak yorum yapıyorsunuz. Çıkış  Yap / Değiştir )

Connecting to %s