OpenGL ES

Merhaba sevgili e-bergi okurları! İlk yazımda sizlerle OpenGL ES üzerine konuşacağım. OpenGL ES amacı PDA'lar, cep telefonları, konsollar, aviyonik ve türevlerinde kullanılmak olan, ileri 3D uygulama programlama arayüzüdür. Cross-platform olması ve geniş bir alanda kullanılması nedeniyle OpenGL'i temel almıştır. Böylece OpenGL geçmişi olan programcılar daha kolay adapte olabililiyorlar. Ayrıca OpenGL ES'in gömülü sistemlerdeki kısıtlamaları ortadan kaldırması ve ihtiyaçları karşılaması hedeflenmektedir. Günümüzde pek çok alanda en çok gelişen 3D API'lerden biridir.

Verimlilik

OpenGL oldukça geniş bir API'ye sahip, halbuki gömülü sistemlerde çalışmak için kaynakları daha az kullanmak gerekiyor. OpenGL ES'te, OpenGL'deki aynı işlemi farklı yollardan yapan teknikler kaldırılmış. Örneğin immediate node, görüntü listesi(display list) ve vertex dizisi(vertex array) uygulamalarından sadece vertex dizisi OpenGL ES'te kullanılır. Bütün bu küçültmelere rağmen OpenGL'e benzer bir dil elde edilmiş.

Bunların yanı sıra yine verimlilik kaygısıyla integer friendly bir tasarım var ve shader diline precision qualifiers tanıtılmış.

Versiyonlar

Birinci versiyonda daha çok "Donanımı nasıl hızlandırırım?" sorusu üzerinde durulmuş. 1.1 ve serinin ilerleyen versiyonlarında ise kullanılan hafıza ve gücün biraz daha azaltılması amaçlanmış. İkinci versiyonda ise 3D grafiklerin programlanması önem kazanıyor. Fragment ve vertex shader yazabiliyoruz. Böylece oyunlarımızda daha zengin grafikler elde ediyoruz. ( mesela per-fragment lighting kullanımı) Programlanabilir pipeline tercih edildiği için 1.x ile uyumlu değil. Böyle tercih edilmesinin sebebi boyut kaygısı. OpenGL ES 1.x.'te hazır fonksiyonları kullandığımız için doğal olarak geliştirme sırasında masaüstü deneyimi yaşayamıyoruz. Bu açıdan ikinci versiyon önemli görünüyor.

Merhaba “Üçgen”

Konuya giriş anlamında örnek bir kod paylaşmadan önce programımızın “esUtil.h” kütüphanesi yardımıyla çalıştığımı belirtmem gerekiyor. Es önekinin anlami da bu. Program objesini kontrol edebilmek için yapı kullanıyoruz. Objeler işimizi oldukça kolaylaştırıyor.

typedef struct
{
GLuint programObject;
} UserData;

Sonra bir gölgelendirici(shader) objesi yaratiyoruz. OpenGL ES’te iki çeşit gölgelendirici var, fragment ve vertex olmak üzere. Biraz üstünde durmak gerekirse genel işlem şu şekilde işliyor: Önce gölgelendirici objesine kaynak kodu veriyoruz. Böylece gölgelendirici objesi bir object formuna derleniyor. Derlenme sonrası da program objesiyle birleştiriliyor. Sonra program objesinin uygulanabilir bir hale gelmesi için bağlantı gerçekleştiriliyor. Eğer herhangi bir hata yoksa son olarak elde ettiğiniz obje emrinizde.

GLuint LoadShader(const char *shaderSrc, GLenum type)
{
GLuint shader;
GLint compiled;
shader = glCreateShader(type);

if(shader == 0)
return 0;
// Load the shader source
glShaderSource(shader, 1, &shaderSrc, NULL);
glCompileShader(shader);
glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
if(!compiled)
{
GLint infoLen = 0;
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);
if(infoLen > 1)
{
char* infoLog = malloc(sizeof(char) * infoLen);
glGetShaderInfoLog(shader, infoLen, NULL, infoLog);
esLogMessage("Error compiling shader:\n%s\n", infoLog);
free(infoLog);
}
glDeleteShader(shader);
return 0;
}
return shader;
}

Burada bir gölgelendirici ve program objesi oluşturmamız lazım. LoadShader kullanarak vertex ve fragment shaderlarını yüklüyoruz. GlCreateProgram sayesinde program objesini yaratıyoruz. Devamında ise binding ve link işlemleri var.

int Init(ESContext *esContext)
{
UserData *userData = esContext->userData;
GLbyte vShaderStr[] =
"attribute vec4 vPosition;   \n"
"void main()            \n"
"{               \n"
" gl_Position = vPosition;  \n"
"}              \n";
GLbyte fShaderStr[] =
"precision mediump float;       \n"
"void main()                \n"
"{                   \n"
" gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); \n"
"}                   \n";
GLuint vertexShader;
GLuint fragmentShader;
GLuint programObject;
GLint linked;
vertexShader = LoadShader(GL_VERTEX_SHADER, vShaderStr);
fragmentShader = LoadShader(GL_FRAGMENT_SHADER, fShaderStr);
programObject = glCreateProgram();
if(programObject == 0)
return 0;
glAttachShader(programObject, vertexShader);
glAttachShader(programObject, fragmentShader);
glBindAttribLocation(programObject, 0, "vPosition");
glLinkProgram(programObject);
glGetProgramiv(programObject, GL_LINK_STATUS, &linked);
if(!linked)
{
GLint infoLen = 0;
glGetProgramiv(programObject, GL_INFO_LOG_LENGTH, &infoLen);
if(infoLen > 1)
{
char* infoLog = malloc(sizeof(char) * infoLen);
glGetProgramInfoLog(programObject, infoLen, NULL, infoLog);
esLogMessage("Error linking program:\n%s\n", infoLog);
free(infoLog);
}
glDeleteProgram(programObject);
return FALSE;
}
// Store the program object
userData->programObject = programObject;
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
return TRUE;
}

Init fonksiyonu içinde yaratilan shaderları kullanarak üçgen çizmi işini aşağıda yapıyoruz. Kısaca burada frame yönlendiriliyor. Öncesinde glClear yardımıyla arabellek(buffer) temizleniyor. OpenGL ES’te birkaç çeşit arabellek var, bunlar color, depth ve stencil. Daha da önemlisi üçgenin geometrisini belirtmemiz gerekiyor. (x,y,z) koordinatlarını kullanarak bunları belirtiyoruz. GlDrawArrays kullanarak ilkel(primitive) şekilleri çizebiliyoruz. Bunlar: üçgen, doğru, şerit(strip).

void Draw(ESContext *esContext)
{
UserData *userData = esContext->userData;
GLfloat vVertices[] = { 0.0f, 0.5f, 0.0f,
-0.5f, -0.5f, 0.0f,
0.5f, -0.5f, 0.0f};
// Set the viewport
glViewport(0, 0, esContext->width, esContext->height);
// Clear the color buffer
glClear(GL_COLOR_BUFFER_BIT);
// Use the program object
glUseProgram(userData->programObject);
// Load the vertex data
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, vVertices);
glEnableVertexAttribArray(0);
glDrawArrays(GL_TRIANGLES, 0, 3);
eglSwapBuffers(esContext->eglDisplay, esContext->eglSurface);
}

Son olarak SwapBuffer kullanıyoruz. Burada double buffering dediğimiz bir sistemden bahsetmek gerekiyor. Bütün rendering "back buffer" denen kısımda oluyor, ama biz bunu görmüyoruz. Rendering bitince "front buffer" denilen kısma geçiyor. Burası görebildiğimiz kısım. Gelecek framede front buffer, back buffer oluyor ve sistem böyle devam ediyor. Çok temel bir amacı var. Ekranımız belirli aralıklarla yenileniyor bildiğiniz gibi. Eğer ekrandaki görüntüyü doğrudan değiştirmeye çalışırsak bazı yapaylıklar ortaya çıkacak ve kısmen yenilenecektir. Bu soruna böyle bir çözüm geliştirilmiş.

Kaynaklar