OpenGL
Nedir ?
OpenGL, en basit
anlamıyla grafik donanımına arayüz olan bir yazılımdır. Bu arayüz yüz elli ayrı
fonksiyona sahiptir. Bu fonksiyonları kullanarak etkileşimli üç boyutlu (3B,
3D) uygulamalar üretme ihtiyacını giderebilir, nesne tanımlama ve benzeri işlemleri
gerçekleştirebiliriz.
OpenGL donanımdan
bağımsız birçok farklı donanım düzleminde uygulanabilecek şekilde tasarımlanmıştır.
Bu niteliği kazanabilmek için, pencere işlemleri ve kullanıcı girişi işlemlerini
yapan komutlar OpenGLde mevcut değildir. Bunun yerine, pencere işlemleri kullanılan
donanımın kontrol edildiği an içerisinde çalıştırılmalıdır. Benzer olarak, OpenGL
kütüphanesi üç boyutlu nesneleri modellemede yüksek seviyede fonksiyonlara sahip
değildir. Bu tip fonksiyonlarla göreceli olarak karmaşık şekiller, örneğin otomobiller,
vücudun bir bölgesi, uçaklar veya moleküller tanımlanabilir. Fakat istenilen
model, küçük bir takım geometrik şekillerden hareketle oluşturulabilir-noktalar,
çizgiler ve poligonlar gibi-.
OpenGL
Komut Dizisi
OpenGL komutları,
C#da, gl öneki ve baş harfleri büyük olarak yazılan komutu oluşturan diğer
kelimelerin birleşiminden oluşmaktadır. Benzer olarak OpenGLde sabitler GL
ön eki ile başlar ve bütün harfler büyük olacak şekilde kullanılır. Ayrıca ayrı
sözcüklerde alt çizgi ile birleştirilir. Aşağıda OpenGL komut örnekleri ve veri
tipleri verilmiştir :
Önek
|
Veri
Tipi |
C
Dilinde Karşılığı |
OpenGL
Tanımı
|
B |
8-bit
integer |
Signed
char |
GLbyte |
S |
8-bit
integer |
Short |
GLshort |
İ |
32-bit
integer |
Long |
GLint,
GLsize |
F |
32-bit
floating-point |
Float |
GLfloat,
GLclampf |
D |
64-bit
dfloating point |
Double |
GLdouble,
GLclampf |
Ub |
8-bit
unsigned integer |
Unsigned
char |
GLubyte,GL
boolean |
Us |
16-bit
unsigned integer |
Unsigned
short |
GLushort |
Ui |
32-bitunsigned
integer |
Unsigned
long |
GLuint,
GLenum |
OpenGLde
Çizim Yapmak
OpenGLde çizim
yapmak için öncelikle çizim öncesi işlemler gerçekleştirilmelidir. İlk olarak
ekran temizlenmeli, ardından çizimin rengi belirlemneli, daha sonra ise geometrik
şeklin ne olacağı tanımlanmalıdır.
1. Ekranın Temizlenmesi
GLClear fonksiyonunun
parametresi, arka planın hangi işleme göre temizleneceğini belirler. Tipik olarak
öncelikle temizleme rengi seçilir, daha sonra temizlenmesi gerekli olan tamponlar
temizlenir. glclearDepth fonksiyonu ise ayarlanacak derinlik tamponundaki
her bir pikselin değerini belirler.
Tampon
|
İsmi
|
Renk
Tamponu |
GL_COLOR_BUFFER_BIT |
Derinlik
Tamponu |
GL_DEPTH_BUFFER_BIT |
Birikim
(Accumulator) Tamponu |
GL_ACCUM_BUFFER_BIT |
Kalıp
(Stencil) Tamponu |
GL_STENCIL_BUFFER_BIT |
2.
Renk Belirleme
Renk belirlemek
için glColor3f isimli fonksiyon kullanılır. Bu komut üç parametre alır, bu
parametrelerin hepsi kayan noktalı sayılardır ve 0.0 ve 1.0 aralığındadır. Parametreler,
sırasıyla kırmızı, yeşil ve mavi (RGB) renk bileşenlerinden oluşur. Bu üç değeri
renkleri karıştırma olarak düşünebiliriz. Örneğin 0.0 değeri o renk bileşeninden
hiç kullanılmayacağı, 1.0 değeri ise o renk bileşeninin tamamen baskın kılılacağı
anlamına gelmektedir. Örneğin :
glColor3f(1.0f,0.0f,0.0f);
ifadesi en parlak
kırmızı renk sistemini çizim rengi olarak atar. Bu ifadede hiç yeşil veya mavi
bileşen yoktur. Tüm bileşenleri sıfır atamak siyah renk, 1.0 atamak beyaz renk,
0.5 atamak ise gri renk anlamına gelir. Aşağıda sekiz renk atama ifadesi görülmektedir
:
glColor3f(0.0,
0.0, 0.0); //siyah
glColor3f(1.0, 0.0, 0.0); //kırmızı
glColor3f(0.0, 1.0, 0.0); //yeşil
glColor3f(1.0, 1.0, 0.0); //sarı
glColor3f(0.0, 0.0, 1.0); //mavi
glColor3f(1.0, 0.0, 1.0); //magenta
glColor3f(0.0, 1.0, 1.0); //cyan
glColor3f(1.0, 1.0, 1.0); //beyaz
Aşağıda, renk tayfı
kartezyen koordinat sisteminde gösterilmiştir :
3.
OpenGLde Geometrik Şeklin Çizimi
Daha önceden de
belirttiğimiz gibi OpenGLde modelleme, basit geometrik şekillerin çizimine
dayanır. Şimdi bunun nasıl yapıldığını inceleyelim. Her bir farklı geometrik
şekil ya da nesne glBegin() ve glEnd() fonksiyonları arasına yerleştirilir.
-
Nokta, Çizgi ve Poligon belirleme :
OpenGL, çizim yapılırken
birden çok nokta, çizgi parçası ve çokgen çizdirilebilmesi için kuvvetli ve
kullanılması çok basit çizim araçlarına sahiptir. Matemetiksel olarak nokta,
çizgi ve poligonun ne anlama geldiğini biliriz, fakat bu tanımlar OpenGLde
tam olarak böyle değildir. Bilgisayar tabanlı hesaplamaların sonlu duyarlı olmaları
sebebiyle bir takım yuvarlama hataları oluşur. Çizgiler, noktalar ve poligonların
koordinatlarında bu yuvarlama hatalarından kaynaklanan sapmalar olabilir.
-
Nokta Belirleme :
Cisimlerin, yüzeylerin
ve kenarların belirtebilmesi için köşe noktalarına ihtiyaç duyulur. OpenGL ve
bilgisayar grafiklerinde noktanın genişliğinden söz edilebilir ve bunun için
glPointSize() fonksiyonu kullanılarak farklı büyüklükte noktalar tanımlanabilir.
-
Çizgiler :
OpenGLde çizgi
terimi, matematikte her iki doğrultuda sonsuz uzunlukta doğru olarak ifade edilen
terimin aksine, yalnızca sonsuz uzunluktaki çizgi parçası anlamına gelmektedir.
Birleşmiş, seri halindeki çizgi parçalarını belirtmenin kolay yolları mevcuttur.
-
Poligonlar :
Poligonlar tipik
olarak iç kısımlarındaki pikseller ile çizilir. Fakat noktalar kümesi olarak
da çizilmeleri mümkündür. OpenGL de poligonların kenarları birbirleri ile kesişmezler.
Buna matematikte basit poligon adı verilir. Ayrıca OpenGL poligonları konveks
yani dışbükey olmalıdır.
-
Dörtgenler :
Dörtgenler grafik
uygulamalarında yaygın olarak kullanılır. OpenGL kütüphanesinde içi dolu dörtgen
çizmek için kullanılan glRect isimli bir fonksiyon bulunmaktadır. Dörtgen bir
poligon gibi de çizilebilir.
-
Eğriler :
Herhangi bir eğri,
çizgi veya yüzey kısa çizgi parçalarından meydana gelen küçük poligonsal alanlar
ile oluşturulabilir. Kısa çizgilerin birleşmesi ile elde edilen şekil eğri gibi
görünür.
-
Köşe Nktalarının Belirlenmesi :
OpenGLde tüm geometrik
nesneler, köşe noktalarından oluşan bir dizi koordinat ile tanımlanır. Bir köşe
noktasının belirlenmesi için glVertex isimli fonksiyon kullanılır. Bu fonksiyon
glBegin ile glEnd fonksiyon çağrıları arasında kalan bloğa yerleştirilir.
//fonksiyonun
kullanımı :
Void glVertex{boyut}{s,i,f,d}[v](koordinatlar);
    //boyut 2,3,4 olabilir
    //s : short, i : int, f : float, d : double
    //v : vektör
glVertex2s(2,3);
             //2 boyutta kullanımı
glVertex(0.0,0.0,3.1415)   //3 boyutlu kullanımı
glVertex3dv(dvect); glVertex3d(doublex,doubley,doublez)
glVertex3dv(double[] )
glVertex3f(floatx,floaty,floatz)
glVertex3fv(float[]v)
glVertex3i(intx,inty,intz)
glVertex3iv(int[]v)
glVertex3s(shortx,shorty,shortz)
glVertex3sv(short[] v)
|
glBegin fonksiyonun
çağrılmasından sonra gelen ifadeler, ne tip geometrik şeklin çizileceğini belirler.
Aşağıdaki
tabloda glBegin fonksiyonunda kullanılan on adet veri tipi gösterilmiştir
:
Veri
Tipi
|
Anlamı
|
GL_POINTS |
Bağımsız noktalar |
GL_LINES |
Bağımsız
çizgi parçaları |
GL_POLYGON |
Dışbükey
poligon |
GL_TRIANGLES |
Üçgen |
GL_QUADS |
Dört-yüzlü
poligonlar |
GL_LINE_STRIP |
Çizgi
parçaları serisi |
GL_LINE_LOOP |
İlk
ve son köşeleri ile birbirine bağlı kapalı çizgi parçaları serisi |
GL_TRIANGLE_STRIP |
Üçgen
serisi |
GL_TRIANGLE_FAN |
Üçgen
yelpazeleri |
GL_QUAD_STRIP |
Dörtgen
serisi |
Uygulama
: OpenGLde Basit Bir Ev Modeli
Şimdi uygulamaya
geçelim. İlk olarak C#ta oluşturulmuş bir Windows uygulamasının içinde bir
form tanımlıyoruz. Görüntümüzü bu form üzerinde göstereceğiz.
OpenGL fonksiyonlarını
kullanabilmek için OpenGL kütüphanesini referanslara eklememiz gerekmektedir.
csgl.dll dosyasını referans olarak seçmek için önce Visual Studio .NET penceremizdeki
proje penceresindeki referans ekle kısmından csgl.dll dosyasını bilgisayarımızdaki
sabit diskte kayıtlı olduğu yerden ekliyoruz ve using CsGL.OpenGL ifadesini
kaynak kod dosyamızın başıne ekleyerek bu kütüphaneyi kullanacağımızı önceden
derleyiciye bildiriyoruz. Ayrıca csgl.native.dll dosyasını C:\WINDOWS\system32
dosyasına kopyalıyoruz. csgl.dll ve csgl.native.dll dosyalarını http://csgl.sourceforge.net/
sayfasından indirebilirsiniz.
Önce bir küp sınıfı
tanımlıyoruz. Tüm OpenGL komutlarını, OpenGLControl sınıfından türettiğimiz
Ourview isimli sınıfın içinde yapıyoruz ve her OpenGL uygulamamızda olması gereken
InitGLContext() fonksiyonumuzu, bu türetilmiş sınıfta ezerek (override ederek)
içindeki üye fonksiyonların parametrelerini değiştirerek istenen başlangıç koşullandırmalarını
yerine getiriyoruz. Çizim işlemleri ise glDraw() fonksiyonu içerisinde tanımlanır.
Burada yine glDraw() fonksiyonumuzu, türetilmiş Ourview isimli sınıfımızda ezerek
fonksiyona yeni özellikler kazandırıyoruz. Çizime başlamadan önce ekran tamponunu
temizliyoruz ve şeklimizi koordinat sisteminde, bize görünebilir olması için
glTranslatef() fonksiyonumuz ile z ekseninde -8 birim derine öteliyoruz. Bu
fonksiyonun özelliği, bir çizim başlangıç noktası oluşturmasıdır. glRotatef()
ile de şeklimizi kendini sürekli yineleyen fonksiyonumuzda (ki biz bu yineleme
rutinini form içindeki Main() in içinde belirteceğiz - buna daha sonra değineceğim
- ) döndürerek şeklin her bir yinelemede farklı bir tarafını görme şansına sahip
oluyoruz. Bu fonksiyonun tam olarak ne işe yaradığını öğrenmek için farklı parametre
değerleri girerek deneyip görmek en güzel yöntemdir.
Bildiğimiz gibi,
şeklimizi glBegin() ve glEnd() fonksiyonları arasında tanımlıyoruz. Eğer bir
dörtgen dizisi belirleyecek isek, bunu bu fonksiyonlar arasında yapmamız gereklidir.
Biz uygulamamızda parametre olarak GL_QUADS girerek dörtgen çizeceğimizi belirtiyoruz.
Çizeceğimiz geometrik şeklin koordinatlarını belirtmeden önce hangi renkte olması
gerektiğini bildirmemiz gereklidir. Renk belirtiminin nasıl olduğuna daha önce
değinmiştik.
Modelimiz ev olduğu
için, bir küp oluşturmalıyız. Evin çatısı için 4 farklı üçgen belirtmeliyiz.
Her bir üçgenin sadece tek bir ortak Vertexi vardır. Diğer köşe noktalarını
evin duvarlarının üst köşelerine konumlandırıyoruz.
Öncelikle Küp
isim uzayımızın kodlarını inceleyelim. Daha sonra form sınıfının kodlarına değineceğiz.
Gerekli açıklamaları kodların arasında bulabilirsiniz. Daha detaylı bilgi için,
OpenGL kırmızı ve mavi kitaba başvurmanızı öneririm.
using System;
using System.Drawing;
using CsGL.OpenGL;
using System.Windows.Forms;
namespace
Küp
{
       public class Ourview : OpenGLControl
       {
              public bool
finished = false;
              public Ourview()
: base() //Kurucu fonksiyonumuz
              {
                    
this.KeyDown += new KeyEventHandler(Ourview_OnKeyDown);
              }
      
       protected void Ourview_OnKeyDown(object Sender,
KeyEventArgs kea)
              {
                    
//eğer escape basılırsa uygulama sona erer
                    
if(kea.KeyCode==Keys.Q&&kea.Modifiers == Keys.Shift)
                    
{
                    
       Application.Exit();
                    
}
              }
      
       //Köşe noktalarını bir değişkenle ifade
ettik çünkü buradan değişitrerek şeklin ne kadar büyük olacağını belirleyebiliriz.
              private
float fEksi = -1.0f;
              private
float fArti = +1.0f;
              private
float fDondur = 0.0f;
      
       public override void glDraw()
              {
                    
GL.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);//Ekranı ve
derinlik tamponunu siler
                    
GL.glLoadIdentity(); //O anki model matrisini sıfırlar
                    
GL.glTranslatef( 0.0f,0.0f,-8.0f );// viewport = 0 0 0 ve böylece derinlik
8 dır.
                    
GL.glRotatef( fDondur, 1.0f, 1.0f, 1.0f );//modelimizi fDondur açısıyla
yerleştirdiğimiz koordinat etrafında döndürürür
                    
GL.glBegin(GL.GL_QUADS); // Geometrik şeklimizin ne olacağı. Burada dörtgen
belirttik.
                    
//üst yüz
                    
GL.glColor3f(1.0f,1.0f,0.0f);//sarı renk
                    
GL.glVertex3f( fArti, fArti, fArti);
                    
GL.glVertex3f( fArti, fArti, fEksi);
                    
GL.glVertex3f( fEksi, fArti, fEksi);
                    
GL.glVertex3f( fEksi, fArti, fArti);
                    
//alt yüz 
                    
GL.glColor3f(0.0f,1.0f,1.0f);//cyan renk
                    
GL.glVertex3f( fArti, fEksi, fArti);
                    
GL.glVertex3f( fArti, fEksi, fEksi);
                    
GL.glVertex3f( fEksi, fEksi, fEksi);
                    
GL.glVertex3f( fEksi, fEksi, fArti);
      
              //arka yüz
                    
GL.glColor3f(1.0f,0.0f,0.0f);//kırmızı renk
                    
GL.glVertex3f(fArti,fArti,fEksi);
                    
GL.glVertex3f(fArti,fEksi,fEksi);
                    
GL.glVertex3f(fEksi,fEksi,fEksi);
                    
GL.glVertex3f(fEksi,fArti,fEksi);
      
              //ön yüz
                    
GL.glColor3f(0.0f,0.0f,1.0f);//mavi renk
                    
GL.glVertex3f(fArti,fArti,fArti);
                    
GL.glVertex3f(fArti,fEksi,fArti);
                    
GL.glVertex3f(fEksi,fEksi,fArti);
                    
GL.glVertex3f(fEksi,fArti,fArti);
      
              //Sağ yüz
                    
GL.glColor3f(0.0f,1.0f,0.0f);//yeşil renk
                    
GL.glVertex3f(fArti,fArti,fArti);
                    
GL.glVertex3f(fArti,fEksi,fArti);
                    
GL.glVertex3f(fArti,fEksi,fEksi);
                    
GL.glVertex3f(fArti,fArti,fEksi);
      
              //sol yüz
                    
GL.glColor3f(0.5f,1.0f,0.5f);
                    
GL.glVertex3f(fEksi,fEksi,fEksi);
                    
GL.glVertex3f(fEksi,fArti,fEksi);
                    
GL.glVertex3f(fEksi,fArti,fArti);
                    
GL.glVertex3f(fEksi,fEksi,fArti);
      
              GL.glEnd();
      
              GL.glBegin(GL.GL_TRIANGLES);
                    
//ön yüz için
                    
GL.glColor3f(1.0f,0.5f,0.2f);
                    
GL.glVertex3f(fArti,fArti,fArti);
                    
GL.glVertex3f(fEksi,fArti,fArti);
                    
GL.glVertex3f(0.0f,2*fArti,0.0f);
                    
//sol yüz için
                    
GL.glColor3f(1.0f,0.2f,0.5f);
                    
GL.glVertex3f(fEksi,fArti,fArti);
                    
GL.glVertex3f(fEksi,fArti,fEksi);
                    
GL.glVertex3f(0.0f,2*fArti,0.0f);
                    
//sağ yüz için üçgen
                    
GL.glColor3f(1.0f,0.25f,0.75f);
                    
GL.glVertex3f(fArti,fArti,fEksi);
                    
GL.glVertex3f(fArti,fArti,fArti);
                    
GL.glVertex3f(0.0f,2*fArti,0.0f);
                    
//arka yüz için üçgen
                    
GL.glColor3f(1.0f,0.75f,0.75f);
                    
GL.glVertex3f(fEksi,fArti,fEksi);
                    
GL.glVertex3f(fArti,fArti,fEksi);
                    
GL.glVertex3f(0.0f,2*fArti,0.0f);
      
              GL.glEnd();
                    
fDondur += 1.25f; // Döndürme açımız
              }
      
       protected override void InitGLContext()
              {
                    
GL.glShadeModel(GL.GL_SMOOTH); // Yumuşak bir gölgelendirme sağlar
                    
GL.glClearColor(1.0f, 1.0f, 1.0f, 0.5f); // arka plan rengi
                    
GL.glClearDepth(1.0f); // Derinlik Tamponu kurmak için
                    
GL.glEnable(GL.GL_DEPTH_TEST); // Derinlik testini etkin kılar
                    
GL.glDepthFunc(GL.GL_LEQUAL); // Yapılcak derinlik testinin tipi
                    
GL.glHint(GL.GL_PERSPECTIVE_CORRECTION_HINT, GL.GL_NICEST); // Çok iyi perspektif
Hesaplamaları için
              }
              //Form boyutu
değiştiğinde neler yapmamız gerektiği burada verilmiştir.
              protected
override void OnSizeChanged(EventArgs e)
              {
                    
base.OnSizeChanged(e);
                    
Size s = Size;
      
              GL.glMatrixMode(GL.GL_PROJECTION);//izdüşüm
matrisini seçer
                    
GL.glLoadIdentity();//model matrisini sıfırlar
                    
GL.gluPerspective(45.0f, (double)s.Width /(double) s.Height, 0.1f, 100.0f);//Çerçevenin
en boy oranı hesaplanarak
                    
GL.glMatrixMode(GL.GL_MODELVIEW);//model matrisini seçer
                    
GL.glLoadIdentity();//ve daha sonra model matrisi tekrar sıfırlanır
              }
       }
}
|
Windows uygulaması
oluşturduğumuz için, çizimimizi ana formumuz içerisinde göstermemiz gerekmektedir.
O halde form için bazı koşullamaları gerçeklememiz gerekmektedir. Oluşturduğumuz
küp isim uzayının Ourview sınıfını form sınıfımızın private değişkeni olarak
tanımlamalıyız. Formumuzun kurucu fonksiyonunda da bazı belirtimler yapmalıyız.
Örneğin formun tamamında çizimimizi görmek için. Main fonksiyonununda ise yeni
bir referans form tanımlıyoruz ve bu formun finished ve IsDisposed değişkenleri
false oldukça devam etmesini sağlıyoruz. Asıl çizim olayını glDraw() fonksiyonunu
çağırarak gerçekleştiriyoruz. Ve formun kendisini sürekli tazelemesini istiyoruz.
Böylece süreç içinde değişen olayları her yeni tazelemede görebilmekteyiz.
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
        /*Bu çalışma Kocaeli Üniversitesi
Elektronik ve Haberleşme Mühendisliğinde İşaret ve Görüntü İşleme Laboratuvarında
(KULİS)
         *OpenGLde 3-B nesne tanımlama
projesi kapsamında Yılmaz Ürgün tarafından yapılmıştır. */
namespace Küp
{
        public class Form1 : System.Windows.Forms.Form
        {
                private
Küp.Ourview view;
      
         private System.ComponentModel.Container
components = null;
      
         public Form1()
                {
                      
 InitializeComponent();
      
               
 this.view = new Küp.Ourview(); // view sınıfı
                      
 this.view.Parent = this;
                      
 this.view.Dock = DockStyle.Fill; // Tüm formu doldurmasını sağlar
                 
      this.Show();
                }
                protected
override void Dispose( bool disposing )
                {
                      
 if( disposing )
                      
 {
                      
         if (components != null)
                      
         {
                      
               
 components.Dispose();
                      
         }
                      
 }
                      
 base.Dispose( disposing );       
         }
      
         #region Windows Form Designer
generated code
                private
void InitializeComponent()
                {
                      
         this.AutoScaleBaseSize = new
System.Drawing.Size(5, 13);
                      
         this.ClientSize = new System.Drawing.Size(552,
454);
                      
         this.Name = "Form1";
                      
         this.Text = "OpenGL - KOÜ
KULİS 2006";
      
         }
                #endregion
                [STAThread]
                static
void Main()
                {
                      
         Form1 form = new Form1();// yeni
bir form oluşturuyoruz.
      
               
         while((!form.view.finished) &&
(!form.IsDisposed)) // sonlandırma false ve Dispose false ise devam et.
                      
         {
                      
               
         form.view.glDraw();//Çizimi yapan
fonksiyon
      
               
               
         form.Refresh();//Formu tazele
                      
               
         Application.DoEvents();
                      
         }
                      
         form.Dispose();
                }
       }
}
|
Projemizi çalıştırdıktan
sonra aşağıdaki ekran çıktısını elde ettik.
Kaynaklar
1) OpenGL kırmızı
ve mavi kitapları.
2) http://www.c-sharpcorner.com/Code/2002/Oct/OpenGLBasics.asp.
3) http://www.c-sharpcorner.com/Code/2002/Oct/SimpleOpenGL.asp
4) Hatice Günaçtı - 3 Boyutlu Model Tanımlama - KOÜ 2005, Bitirme Tezi
Makale:
C#'ta OpenGL Kullanımı C#, Visual C# ve .NET Yılmaz Ürgün
|