Professional Documents
Culture Documents
OpenGL'de seçim biri direkt biride endirekt olarak iki şekilde ele alınabilir. İlk metot
OpenGL'nin kendisinin bu iş için doğrudan atfettiği apilerle yapılırken ikinci metotta yine
OpenGL'deki bazı apilerle ama direkt bu iş için atfetmediği apilerle yapılır(renk kodlama diye
adlandırabileceğimiz bu metotda, nesnelere renk atayarak sahneleme yapmadan arka
tampondan piksellerin okunması).
Giriş:
OpenGL , 3D sahnede nesnelerin seçimi için bir mekanizma sağlamıştır. Bu ders, farenin
altındaki veya OpenGL penceresinin bir dörtgensel bölgesinde bulunan nesnenin nasıl
algılanıp tespit edileceğini gösterecek. Adımlar, farenin tıklandığı yerdeki nesneleri tespit
etmek içindir:
Nesneleri tespit etmek için, sahnenizdeki bütün konuyla ilgili nesneleri isimlendirmelisiniz.
OpenGL , primitiflere veya primitif setlerine (Nesneler) isimler vermenize imkan verir. Seçim
modunda olunduğunda, OpenGL tarafından aslında hiçbir nesnenin framebuffer'da
sahnelenmediği özel bir sahneleme modu sağlanır. Nesnelerin isimleri (artı derinlik bilgisi)
bir dizide toplanır. İsimsiz nesneler için, sadece derinlik bilgisi toplanır.
OpenGL terminolojisini kullanarak, Her ne zaman bir primitif seçim modunda sahnelenirse
bir vuruş(hit) meydana gelir . Vuruş kayıtları(Hit records), seçim tamponunda(selection
buffer) depolanır . Seçim modundan çıkılması üzerine, OpenGL, vuruş kayıtlarının seti ile
seçim tamponunu döndürür. OpenGL, her vuruş için derinlik bilgisini sağladığından dolayı,
uygulama, hangi nesnenin kullanıcıya daha yakın olduğunu o zaman kolayca bulabilir.
İsim Yığını:
Başlıktanda anlaşılacağı üzere, nesnelere verdiğiniz isimler bir yığında(stack) depolanır .
Aslında nesnelere bir metin string'indeki gibi isimler vermezsiniz. Onun yerine, nesneleri
numaralarsın. Bununla birlikte, OpenGL isim terimini kullanıldığından dolayı, bu derste aynı
şekilde numara yerine isim terimi kullanılacak.
Bir nesne sahnelendiği zaman, eğer o yeni görüş hacmini keserse bir vuruş kaydı yaratılır .
Vuruş kaydı yığındaki mevcut isimleri artı nesne için minimum ve maksimum derinliği içerir
. Vuruş kaydı, isim yığını boş bile olsa yaratıldığına dikkat çünkü derinlik bilgisi var. Eğer
başka nesneler, uygulama, seçim modundan ayrılmadan önce veya isim yığını değiştirilmeden
önce sahnelenirse, O zaman vuruş kaydında depolanan derinlik değerleri, buna bağlı olarak
değiştirilir .
Vuruş kaydı, sadece, uygulama, seçim modundan ayrıldığı zaman veya isim yığınının mevcut
içeriği değiştirildiğinde seçim tamponunda depolanır. Bu yüzden seçim modu için sahneleme
fonksiyonu , primitiflerin sahnelenmesine ek olarak isim yığınının içeriğinden de sorumludur.
________________________________________
void glInitNames(void);
________________________________________
Bu fonksiyon boş bir isim yığını yaratır. Sen, isimleri yığına koymadan(push) önce yığını
ilklemek için bu fonksiyonu çağırman gerekir.
________________________________________
void glPushName(GLuint name);
________________________________________
Yığının en üstüne ismi ekler. Yığının maksimum boyutu uyarlamaya bağlıdır, OpenGL el
kitabında , uygulamaların büyük çoğunluğu için gereği kadar olacak en az 64 ismi içerdiği
yazılmakta. Tam sayıyı öğrenmek için GL_NAME_STACK_DEPTH parametresiyle
glGetIntegerv fonksiyonunu kullanabilirsin. Kapasitesi ötesinde yığına değerlere push etmek,
bir taşma hatası olan GL_STACK_OVERFLOW 'e sebebiyet verir .
________________________________________
void glPopName();
________________________________________
Yığının en üstündeki ismi çıkarır. Boş bir yığından bir değeri almak(pop etmek), bir aşağı
taşma hatası olan
GL_STACK_UNDERFLOW 'e sebebiyet verir.
________________________________________
void glLoadName(GLunit name);
________________________________________
glPopName();
glPushName(name);
________________________________________
glLoadName fonksiyonu, temelde yukarıdaki kod parçası için bir kestirme yoldur. Boş bir
yığına bir ismi yüklemek, GL_INVALID_OPERATION hatasına sebebiyet verir .
Not: glBegin glEnd arasında bu fonksiyonları koyamazsın, Seçim modu için yeni bir
sahneleme fonksiyonunu gerektirecek can sıkıcı bir durum ortaya çıkar. Örneğin eğer glBegin
glEnd arasında noktalar setine sahipsen ve Onları ayırt edebileceğin bir şekilde onları
adlandırmayı istiyorsan, O zaman, her isim için bir glBegin glEnd bloğu yaratmalısın .
#define BODY 1
#define HEAD 2
...
void renderInSelectionMode() {
1 glInitNames();
2 glPushName(BODY);
3 drawBody();
4 glPopName();
5 glPushName(HEAD);
6 drawHead();
7 drawEyes();
8 glPopName();
9 drawGround();
}
________________________________________
2. glPushName(BODY); - Bir isim yığına konur(push). Yığın şimdi tek bir isim içeriyor .
3. drawBody(); - Bir şeyler çizmek için OpenGL primitiflerini çağıran bir fonksiyon . Eğer
burada çağrılan primitiflerin herhangi biri görüş hacmini keserse bir vuruş kaydı yaratılır.
Vuruş kaydının içeriği, isim yığınında mevcut isim olacak(BODY), artı görüş hacmini kesen
o primitifler için minimum ve maksimum derinlik değerleri.
4. glPopName(); - Yığının en üstündeki ismi çıkarır . Yığında tek bir item olduğundan dolayı,
o şimdi boş olacak. İsim yığını değiştirildi bu yüzden Eğer vuruş kaydı 2'de yaratılsaydı, o,
seçim tamponunda saklanacaktı.
5. glPushName(HEAD); - Bir isim yığına konur(push). Yığın şimdi, yeniden tek bir isim
içeriyor . Yığın değiştirildi , Ama vuruş kaydı yok bu yüzden Hiçbir şey seçim tamponuna
gitmez.
7. drawEyes(); - OpenGL primitiflerini sahneleyen yine başka bir fonksiyon. Eğer burada
primitiflerin herhangi biri görüş hacmini keser ve 6'dan bir vuruş kaydı varsa, vuruş kaydı
buna bağlı olarak güncellenir. Vuruş kaydındaki mevcut isimler tutulur , Ama eğer
drawEyes()'deki primitiflerin herhangi biri daha minimumluk veya daha maksimumluk
derinliğe sahipse, O zaman bu yeni değerler vuruş kaydında depolanır . Eğer drawEyes()'deki
primitifler görüş hacmini kesiyor ama drawHead()'den hiçbir vuruş kaydı yoksa O zaman
yenisi yaratılır .
8. glPopName(); - Yığının en üstündeki ismiçıkarır. Yığın, tek bir isme sahip olduğundan
dolayı yığın şimdi boştur. Yeniden yığın değiştirildi bu yüzden Eğer bir vuruş kaydı
yaratılsaydı, o, seçim tamponunda depolanacaktı.
9. drawGround(); - Eğer burada çağrılan primitiflerin herhangi biri görüş hacmini keserse bir
vuruş kaydı yaratılır. Yığın boştur, bu yüzden Hiçbir isim vuruş kaydında depolanmayacak ,
sadece derinlik bilgisi. Eğer isim yığınında bu noktadan sonra hiçbir değişiklik meydana
gelmezse , O zaman yaratılan vuruş kaydı sadece, uygulama, seçim modundan ayrıldığı
zaman seçim tamponunda depolanacak . Bu, derste sonra anlatılacak.
En başta sallama bir ismi (kullanılmamış bir değer) push edip, Sonra İsimleri push ve pop
etmek yerine sadece glLoadName'i kullanabileceğine dikat. Yine de bir isme sahip olmayan
nesneleri es geçme ismin sallama isim mi diye kontrol etmekden daha hızlıdır . Diğer taraftan
, push - pop etmeye kıyasla glLoadName()'i çağırmak daha hızlıdır.
Devamdaki iki fonksiyon iki farklı yaklaşımı gösteriyor . ilki her kardan adam için tek bir
isim kullanıyor.
________________________________________
Devamdaki fonksiyon her kardan adama iki isim verir. Bu ikinci fonksiyonda eğer bir vuruş
meydana gelirse vuruş kaydı iki isme sahip olacak.
________________________________________
Hiyerarşik isimlendirme:
Bu, çoklu isimlendirmede doğal bir aşamadır. Bir nesnenin özel bir yerinin tıkladığını bulmak
için bu ilginç olabilir.Örneğin sadece hangi kardan adamın tıkladığında bilmek yetmez aynı
zamanda kafasının mı yoksa vücudunun mu tıklandığınıda bilmek isteyebilirsin. Devamdaki
fonksiyon bu bilgiyi sağlar .
________________________________________
Bu durumda, bir kardan adamın vücut veya kafasından herhangisine tıkladığında üç isme
sahip olacaksın: satır, sütun ve BODY veya HEAD değeri.
Bu , bu durum için seçim moduna özel bir sahneleme fonksiyonu yazmayı dikkate almayı
ifade eder. Tabi bunu yaparken çok dikkatli olmalısın. Örneğin uygulamanızda birkaç odaya
sahip olduğunuzu ve her odada çeşitli seçilebilir nesneler olduğunu varsayalım. Nesneler
diğer odalarda ve görünmüyor, duvarlarla kullanıcının nesneleri seçmesini engellemelisiniz.
Eğer aynı sahneleme fonksiyonunu kullanırsanız O zaman duvarlar bir vuruşa sebebiyet
verecek , Ve dolayısıyla duvar arkasında olan nesneleri es geçmek için derinlik bilgisini
kullanabilirsin. Bununla birlikte eğer sadece seçilebilir nesnelerli bir fonksiyon inşa etmeye
karar verirsen O zaman, bir nesnenin mevcut odada mı veya duvarın ardında mı olduğunu
söylemek için bir yola sahip olamazsın.
Bu yüzden, seçim modu için özel sahneleme fonksiyonu inşa ettiğinde, Sen, sadece seçilebilir
nesneleri değil aynı zamanda mesela duvarlar gibi blokaja sebebiyet verebilen bütün nesneleri
dahil etmelisin. Yüksek etkileşimli gerçek zamanlı uygulamalar için iyi bir çıkış yoludur bu.
Duvarlar yerine, kutu, masada olabilir.
Parametreler:
buffer : İşaretsiz tam sayı tipinde bir dizi. Bu dizi, OpenGL'nin, vuruş kayıtlarını depolayacağı
yerdir.
size : Dizinin boyutu.
________________________________________
Seçim moduna girmeden önce bu fonksiyonu çağırman gereklidir. Sonra, devamdaki çağrıyla
seçim moduna girersin:
________________________________________
void glRenderMode(GLenum mode);
Parametreler:
mode - Sahneleme moduna girmek için GL_SELECT kullanılır normal sahnelemeye dönmek
için GL_RENDER. Bu sonraki değer, varsayılan değerdir.
________________________________________
Şimdi geldik zor kısma. Uygulama, görüş hacmini tekrar tanımlamalı ki Sadece farenin
tıklandığı yer etrafında küçük bir alan sağlansın. Bunu yapmak için GL_PROJECTION'e
matris modunu set etmek gereklidir. Sonra, uygulama, normal sahneleme modu
düzenlemelerini saklamak üzere güncel matrisi push( yığına koymalı) etmelidir. Sonra matris
ilklenir. Devamdaki kod parçasında bunların nasıl yapıldığı gösterilmektedir:
________________________________________
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
________________________________________
Tamam böylece şimdi temiz bir projeksiyon matrisine sahipsin. Şimdi gereken tek şey, görüş
hacmini tanımlamaktır ki sahneleme sadece fare imlecinin etrafında küçük bir alanda yapılsın.
Bu, devamdaki fonksiyonu kullanarak yapılabilir:
________________________________________
void gluPickMatrix(GLdouble x, GLdouble y, GLdouble witdth, GLdouble height, GLint
viewport[4]);
Parametreler:
x,y : Bu iki değer, fare imlecinin yerini temsil eder. Onlar, seçim alanının merkezini tanımlar.
Bu değerler, pencere koordinatlarında belirtilir. Tabi OpenGL’de pencere koordinatlarının
viewport’un sol alt köşesi orjinli iken Windows işletim sisteminin sol üst köşe orjinli
olduğuna dikkat.
width, height : Seçim bölgesinin boyutu, Fazla küçük , küçük nesneleri seçiminde zorlanmanı,
Fazla büyükde, çok fazla vuruşla uğraşmak zorunda kalabilirsin.
viewport : Geçerli viewport
________________________________________
Yukarıdaki fonksiyonu çağırmadan önce, güncel viewportu elde etmek zorundasın. Bunu,
GL_VIEWPORT parametresiyle(glGetIntegerv fonksiyonunda) OpenGL durum
sorgulamasıyla yapabilirsin. Sonra, gluPickMatrix()'i çağırıp normal sahneleme için yaptığın
gibi projeksiyonunu(perspektif veya ortogonal ) set edersin. Sonunda modelview matris
moduna dönülür, Ve sahnelemeye başlamak için isim yığını ilklenir. Devamdaki kodda, bir
perspektif projeksiyonunu kullanmada yapılacak fonksiyon çağrıları sırası gösteriliyor.
________________________________________
glGetIntegerv(GL_VIEWPORT,viewport);
gluPickMatrix(cursorX,viewport[3]-cursorY,5,5,viewport);
gluPerspective(45,ratio,0.1,1000);
glMatrixMode(GL_MODELVIEW);
glInitNames();
________________________________________
...
GLint viewport[4];
glSelectBuffer(BUFSIZE,selectBuf);
glRenderMode(GL_SELECT);
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
glGetIntegerv(GL_VIEWPORT,viewport);
gluPickMatrix(cursorX,viewport[3]-cursorY,5,5,viewport);
gluPerspective(45,ratio,0.1,1000);
glMatrixMode(GL_MODELVIEW);
glInitNames();
}
________________________________________
Vuruş kayıtlarını işleme tabi tutmak için uygulamanız ilkin normal sahneleme moduna
dönmelidir. Bu, prametresi GL_RENDER olan glRenderMode() çağrısıyla yapılır. Bu
fonksiyon, seçim modunda sahneleme esnasında yaratılan vuruş kayıtlarının
sayısını döndürür. Bu adımdan sonra uygulamanız seçim tamponunu işleme tabi tutabilir.
GL_RENDER 'li glRender çağrısından önce vuruş kayıtlarının seçim tamponuna
kaydedildiğinin hiçbir garantisi yoktur buna dikkat.
Ayrıca, projeksiyon matrisini orjinal eski haline getirmek gereklidir . Bu matris, glPushMatrix
ile seçim moduna
girildiğinde kaydedildiğinden dolayı, Gerekli olan tek şey matrisi pop etmektir(stacktan
almak). Devamdaki kod parçasında gerekli adımlar gösteriliyor:
____________________________________
void stopPicking() {
int hits;
Son değineceğimiz, seçim tamponu yapısı ile ilgilidir. Seçim tamponu, vuruş kayıtlarını
meydana geldiği sırayla depolar , yani primitiflerin çizim sırasıyla. Z-culling hala vuruş
kayıtları ürettiği için primitiflerin çizilmeyeceğine dikkat.
İkinci ve üçüncü alanlar, vuruşun minimum ve maksimum derinliğini temsil eder. Sadece
görüş hacmini kesen primitiflerin noktalarının hesaba alındığına dikkat. OpenGL kırpılan
çokgenler için noktaları tekrar inşa eder. Bundan dolayı temelde, görüş hacmini kesen
primitiflerin sahnelenen parçasının minimum ve maksimum derinliklerini elde edersin, bütün
primitifin minimum ve maksimum derinlikliğini değil. Derinlik, Z buffer'dan alınır ([0,1]
ermininde uzandığı yer), 2^32 -1 hesaplamasını yapıp en yakın tam sayıya yuvarlanır. z
buffer'ın doğrusal olmayan yapısı nedeniyle Elde edeceğiniz derinlik, bakış açısına uzaklığına
doğrusal orantılı değildir buna dikkat .
Sonraki alanlar vuruş için isimlerin dizisi kaydedilir. Bu isimler, vuruş kaydedildiği zaman
isim yığınının içerikleridir. isimlerin numarası sıfır olabildiğinden dolayı bu dizi boş dizi
olabilir buna dikkat.
Viewport'a en yakın nesneyi bulmak için derinlik bilgisini kullanırsınız. Örneğin kullanıcının
tıkladığını en küçük minimum derinliğe sahip nesneyi seçmek isteyebilirsiniz. Yukarıdaki
örnekte ilgili vuruş üçüncü vuruştur. Devamdaki fonksiyon, Red Book'dan alınmıştır, ekrana
en yakın nesnenin ismi yazdırılmakta.
_______________________________________
ptr += names+2;
}
printf ("The closest hit names are ");
ptr = ptrNames;
for (j = 0; j < numberOfNames; j++,ptr++) {
printf ("%d ", *ptr);
}
printf ("\n");
}
#include <math.h>
#include <gl\glut.h>
#include <gl\gl.h>
#include <gl\glu.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "render.h"
#define RENDER 1
#define SELECT 2
#define BUFSIZE 1024
GLuint selectBuf[BUFSIZE];
GLint hits;
int mode = RENDER;
int cursorX,cursorY;
float ratio;
h = h1;
w = w1;
if(h == 0)
h = 1;
ratio = 1.0f * w / h;
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glViewport(0, 0, w, h);
gluPerspective(45,ratio,0.1,1000);
glMatrixMode(GL_MODELVIEW);
}
void startPicking() {
GLint viewport[4];
float ratio;
glSelectBuffer(BUFSIZE,selectBuf);
glGetIntegerv(GL_VIEWPORT,viewport);
glRenderMode(GL_SELECT);
glInitNames();
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
gluPickMatrix(cursorX,viewport[3]-cursorY,5,5,viewport);
ratio = (viewport[2]+0.0) / viewport[3];
gluPerspective(45,ratio,0.1,1000);
glMatrixMode(GL_MODELVIEW);
}
ptr += names+2;
}
if (numberOfNames > 0) {
printf ("tıkladığınız kardan adam ");
ptr = ptrNames;
for (j = 0; j < numberOfNames; j++,ptr++) {
printf ("%d ", *ptr);
}
}
else
printf("bir kardan adam tıklamadınız!");
printf ("\n");
void stopPicking() {
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
glFlush();
hits = glRenderMode(GL_RENDER);
if (hits != 0){
processHits2(hits,selectBuf,0);
}
mode = RENDER;
}
void renderScene() {
glutSetWindow(mainWindow);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
if (mode == SELECT) {
startPicking();
}
glLoadIdentity();
gluLookAt(cam.pos[0],
cam.pos[1],
cam.pos[2],
cam.lookAt[0],
cam.lookAt[1],
cam.lookAt[2],
cam.lookUp[0],
cam.lookUp[1],
cam.lookUp[2]);
draw();
if (mode == SELECT)
stopPicking();
else
glutSwapBuffers();
}
if (key == 27) {
quit();
exit(0);
}
else
processKeyboard(key, x, y);
}
cursorX = x;
cursorY = y;
mode = SELECT;
}
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA);
glutInitWindowPosition(100,100);
glutInitWindowSize(w,h);
mainWindow = glutCreateWindow("Direkt metotla seçim");
glutKeyboardFunc(processNormalKeys);
glutReshapeFunc(changeSize);
glutDisplayFunc(renderScene);
glutMouseFunc(mouseStuff);
glutIdleFunc(renderScene);
initScene(argc,argv);
init(&cam);
glutMainLoop();
return(0);
}
#include "render.h"
cam->pos[0] = 1.5;
cam->pos[1] = 3.75;
cam->pos[2] = 3;
cam->lookAt[0] = 1.5;
cam->lookAt[1] = 1.75;
cam->lookAt[2] = 0;
cam->lookUp[0] = 0;
cam->lookUp[1] = 1;
cam->lookUp[2] = 0;
void drawSnowMan() {
glPushMatrix();
glColor3f(0.0f,0.0f,0.0f);
glTranslatef(0.05f, 0.10f, 0.18f);
glutSolidSphere(0.05f,10,10);
glTranslatef(-0.1f, 0.0f, 0.0f);
glutSolidSphere(0.05f,10,10);
glPopMatrix();
snowManDL = glGenLists(1);
glNewList(snowManDL,GL_COMPILE);
drawSnowMan();
glEndList();
return(snowManDL);
}
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
snowman_display_list = createDL();
}
void draw() {
Red Book, seçim konusuna ilişkin renk kodlama bazlı ilginç bir yaklaşım sunmuştur. Bu
yaklaşım, önceki bölümde anlatılana kıyasla kullanımı çok daha basit.
Renk kodlamada, perspektif değişikliği gerekmiyor ve bu yüzden teoride daha basittir.
Sadece, konuyla ilgili nesnelerin (seçilebilir ) her biri için farklı bir renk tayin edildiği bir
sahneleme fonksiyonu tanımlanır. Kullanıcı, sahne üzerinde fareyi tıkladığı zaman arka
tamponda sahne sahnelenir, arka tampondan seçilen piksel okunur ve rengi kontrol edilir.
Tamponlar değiştirilmediği için renk kodlamanın yapıldığı sahneleme asla görülmez.
Örneğin, 4 kardan adamlı devamdaki sahneyi düşünün. Gördükleriniz, normal sahneleme
fonksiyonunun ürünüdür.
Sonraki figür, renk kodlama sahneleme fonksiyonu tarafından arka tamponda üretilmiş
sonuçtur. Gördüğünüz üzere her kardan adam farklı bir renge sahip.
void draw() {
// zemini çiz
glColor3f(0.9f, 0.9f, 0.9f);
glBegin(GL_QUADS);
glVertex3f(-100.0f, 0.0f, -100.0f);
glVertex3f(-100.0f, 0.0f, 100.0f);
glVertex3f( 100.0f, 0.0f, 100.0f);
glVertex3f( 100.0f, 0.0f, -100.0f);
glEnd();
Takip eden fonksiyon renk kodlama sahneleme fonksiyonu. görebileceğiniz üzere, zemin,
seçilebilir bir nesne olmadığıdan burada tekrar çizimine gerek yok. Tabi kardan adamlar için
kodlar uygun şekilde değiştirildi
void drawPickingMode() {
glDisable(GL_DITHER);
for(int i = 0; i < 2; i++)
for(int j = 0; j < 2; j++) {
glPushMatrix();
// her kardan adam için farklı renk
switch (i*2+j) {
case 0: glColor3ub(255,0,0);break;
case 1: glColor3ub(0,255,0);break;
case 2: glColor3ub(0,0,255);break;
case 3: glColor3ub(250,0,250);break;
}
glTranslatef(i*3.0,0,-j * 3.0);
glCallList(snowman_display_list);
glPopMatrix();
}
glEnable(GL_DITHER);
}
Farenin tıklandığı pixelin rengini kontrol etme, arka tampondan aynı pixeli okumayı kapsar.
Bu, devamdaki fonksiyonu kullanarak başarılabilir:
Parametreler:
x,y : sol alt köşe
width, length: okunacak alanın boyutu
format: okunacak veri tipi. Burada, GL_RGB.
type: piksel öğelerinin veri tipi. Burada , GL_UNSIGNED_BYTE kullanacağız.
pixels: piksellerin depolanacağı dizi. Bu, fonksiyonun sonucu.
format ve type ikilisi için muhtemel diğer seçenekleri görmek için, Red veya Blue book’a
bak.
bu yaklaşımı kullanımının getirdiği Viewport bilgisini okumak üzere y koordinatını ters
çevirmek gerekli. Viewport bilgisini aldıktan sonra, aşağıdaki kodda gösterildiği gibi
glReadPixels çağrılır. x ve y parametreleri kursör konumunu temsil eder. sadece tek pixel
okunduğundan dolayı genişlik ve yükseklik ikilisi 1 set edilir. sonra, pixel değerleri kontrol
edilir.
void processPick ()
{
GLint viewport[4];
GLubyte pixel[3];
glGetIntegerv(GL_VIEWPORT,viewport);
glReadPixels(cursorX,viewport[3]-cursorY,1,1,
GL_RGB,GL_UNSIGNED_BYTE,(void *)pixel);
printf("%d %d %d\n",pixel[0],pixel[1],pixel[2]);
if (pixel[0] == 255)
printf ("1.sıradaki 1. kardan adamı tıkladınız");
else if (pixel[1] == 255)
printf ("2.sıradaki 1. kardan adamı tıkladınız");
else if (pixel[2] == 255)
printf ("1.sıradaki 2. kardan adamı tıkladınız");
else if (pixel[0] == 250)
printf ("2.sıradaki 2. kardan adamı tıkladınız");
else
printf("Bir kardan adam tıklamadınız!");
printf ("\n");
Bu yaklaşımı problemsiz kullanmak için, birkaç şey hakkında daha bilgi vereyim. İlki, float
yerine unsigned byte olarak renkleri set et. RGB modda, her bileşen için sadece 256 değer
mümkündür, bu yüzde float olarak renk set edersen, OpenGL, mümkün en yakın rengi
seçecek. İkincisi, monitörünüzün modu , True Color(gerçek renk) olarak set olduğuna emin
olun. Aksi halde ekranda sunulan renk, icap edene yakını seçilir ve dolayısıyla arka
tampondan okuduğumuz pikseller set ettiğimiz değerlerle eşleşmez. Ve son olarak, renk
kodlaması sahneleme fonksiyonunda, aydınlatma ,dokulama, dithering(renk bileşenleri veya
indeksler , renk tamponuna yazılmadan önce mixlenme-harmanla işlemine sokulur) pasif
edildiğine emin olmalısın, çünkü bu işlemler orijinal rengi değiştirebilir.
Eğer renk set etmek için floatlarla çalışmayı istiyor veya monitörünüzün TrueColor'a
ayarlanık olduğuna garanti veremiyorsanıx o zaman belirtilen renklerin sahnelendiği zaman
nasıl görüneceğini kontrol etmek için bir test yapabilirsin.
Bu yaklaşım şunu gerektirir: uygulamanın başlatmadan önce istenen renk bloklarıyla
sahneleme yapılıp gerçekte sahnelenen renkleri glReadPixels ile okunup Bir dizide bu renkler
depolanıp ve sahneleme zamanı belirttiğin renkler yerine onlar kullanılır.
Gerçi renk seçiminde dikkatli olunmalı. Eğer her bileşen için değerler yeterince farklı set
edilmezse yine sorun yaşayabilirsin
. Örneğin monitör gerçek renk yerine yüksek renk (high color) olarak set edilikse unsigned
byte ile 250 ‘e kırmızı bileşeni ayarladığında, aslında 255'lik kırmızı bileşenini elde edersin.
32 veya 24 yerine bir rengi belirtmek için sadece 16 bit kullanıldığı zaman her bileşen için
daha az mümkün değer olur. 16 bitlik bileşen için bitlerin mümkün kombinasyonu hakkında
detay için red bookta pixel packing values(paketli piksel değerler) kısmına bak.
#include <math.h>
#include <gl\glut.h>
#include <gl\gl.h>
#include <gl\glu.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "render.h"
#define RENDER 1
#define SELECT 2
#define BUFSIZE 1024
GLuint selectBuf[BUFSIZE];
GLint hits;
int mode = RENDER;
int cursorX,cursorY;
float ratio;
h = h1;
w = w1;
if(h == 0)
h = 1;
ratio = 1.0f * w / h;
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glViewport(0, 0, w, h);
gluPerspective(45,ratio,0.1,1000);
glMatrixMode(GL_MODELVIEW);
}
void processPick ()
{
GLint viewport[4];
GLubyte pixel[3];
glGetIntegerv(GL_VIEWPORT,viewport);
glReadPixels(cursorX,viewport[3]-
cursorY,1,1,GL_RGB,GL_UNSIGNED_BYTE,(void *)pixel);
printf("%d %d %d\n",pixel[0],pixel[1],pixel[2]);
if (pixel[0] == 255)
printf ("1.sıradaki 1.kardan adamı tıkladınız");
else if (pixel[1] == 255)
printf ("2.sıradaki 1.kardan adamı tıkladınız ");
else if (pixel[2] == 255)
printf ("1.sıradaki 2.kardan adamı tıkladınız ");
else if (pixel[0] == 250)
printf ("2.sıradaki 2.kardan adamı tıkladınız ");
else
printf("Bir kardan adam tıklamadınız!");
printf ("\n");
void renderScene() {
glutSetWindow(mainWindow);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
gluLookAt(cam.pos[0],
cam.pos[1],
cam.pos[2],
cam.lookAt[0],
cam.lookAt[1],
cam.lookAt[2],
cam.lookUp[0],
cam.lookUp[1],
cam.lookUp[2]);
if (mode == SELECT)
drawPickingMode();
else
drawPickingMode();
if (mode == SELECT) {
processPick();
mode = RENDER;
}
else
glutSwapBuffers();
}
if (key == 27) {
quit();
exit(0);
}
else
processKeyboard(key, x, y);
}
cursorX = x;
cursorY = y;
mode = SELECT;
}
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA);
glutInitWindowPosition(100,100);
glutInitWindowSize(w,h);
mainWindow = glutCreateWindow("Renk kodlamayla seçim");
glutKeyboardFunc(processNormalKeys);
glutReshapeFunc(changeSize);
glutDisplayFunc(renderScene);
glutMouseFunc(mouseStuff);
glutIdleFunc(renderScene);
initScene(argc,argv);
init(&cam);
glutMainLoop();
return(0);
}
#include <math.h>
#include <gl\glut.h>
#include <gl\gl.h>
#include <gl\glu.h>
#include <stdio.h>
#include <stdlib.h>
#include "render.h"
void quit() {}
void processKeyboard(unsigned char key, int x, int y) {
printf("tuş: %d\n",key);
}
cam->pos[0] = 1.5;
cam->pos[1] = 3.75;
cam->pos[2] = 3;
cam->lookAt[0] = 1.5;
cam->lookAt[1] = 1.75;
cam->lookAt[2] = 0;
cam->lookUp[0] = 0;
cam->lookUp[1] = 1;
cam->lookUp[2] = 0;
void drawSnowMan() {
glPushMatrix();
glColor3f(0.0f,0.0f,0.0f);
glTranslatef(0.05f, 0.10f, 0.18f);
glutSolidSphere(0.05f,10,10);
glTranslatef(-0.1f, 0.0f, 0.0f);
glutSolidSphere(0.05f,10,10);
glPopMatrix();
GLuint createDL() {
GLuint snowManDL;
snowManDL = glGenLists(1);
glNewList(snowManDL,GL_COMPILE);
drawSnowMan();
glEndList();
return(snowManDL);
}
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
snowman_display_list = createDL();
}
void draw() {
void drawPickingMode() {
glDisable(GL_DITHER);
for(int i = 0; i < 2; i++)
for(int j = 0; j < 2; j++) {
glPushMatrix();
switch (i*2+j) {
case 0: glColor3ub(255,0,0);break;
case 1: glColor3ub(0,255,0);break;
case 2: glColor3ub(0,0,255);break;
case 3: glColor3ub(130,0,130);break;
}
glTranslatef(i*3.0,0,-j * 3.0);
glCallList(snowman_display_list);
glPopMatrix();
}
glEnable(GL_DITHER);
}