Geçen yazımızda SoNurbs sınıflarını incelemiştik ve
eldeki kısıtlı sayıdaki koordinatlardan nasıl eğriler elde edebileceğimizi ve bu eğrilerin
şeklini belirlemiştik. Şimdi ki yazımda ise SoFaceSet sınıflarını inceleyeceğiz. SoFaceSet
sınıfları eldeki texture, material, coordinat vb. özellikleri kullanarak bize görüntüyü
verir. Herhangi bir oyunda karşılaştığınız bir görüntü ekseriyetle SoFaceSet tarzı
sınıflarla oluşturulmuştur. Sınıflarımız kabaca tanımlamak gerekirse koordinatları
rastgele birleştirmekten ziyade bizim tanımladığımız kurala göre yüzey oluştururlar. İşin
en kafa karıştırıcı kısmıdır. Örneğimizde detaylı olarak üzerinden geçeceğiz.
Teorik Temelleri :
Elimizdeki koordinatlardan şekil çıkaracağımız bu koordinatların
nasıl birleşeceğini biz belirleriz ve bu belirlediğimiz yöntemlere göre aynı koordinatlardan
farklı şekiller çıkabilir. SoFaceSet sınıflarında belirli koordinatlar yüzey oluşturur
ve bu yüzeyler daha sonra birleşerek şeklimizi oluşturur. Dolayısı ile koordinatların
nasıl yüzey oluşturacağı çok önemlidir. Şimdi bir örnek verelim, bu örneğimizde elimizde
kübün köşe koordinatları olsun. Her yüzeyi üçgenlere bölerek gösterelim (en kolay gösterim
şekli olduğu için istersek beşgen,altıgen de yapabiliriz). Şimdi dikkat etmemiz gereken
en önemli iki kuralı size açıklayayım:
1nci Kural |
Vertexleri tararken saatin ters yönünde gitmemiz
gerekir ! |
2nci Kural |
Vertexleri tarama işimiz bittikten sonra tarayacağımız
diğer vertex bir önceki vertexin komşusu olması lazım. |
Şekle bakınca ne demek istediğimi anlayabilirsiniz. Bir
küp için bu çok kolaydır ama cismimiz karmaşıklaştıkça vertexleri tanımlamak için
çok ileri matematiksel hesaplamalar yapmak gerekmektedir.
SoFaceSet Nasıl Kullanılır ?
Şimdi geldik işin en zevkli kısmına! SoFaceSet sınıfı
yukarı da belirttiğimiz gibi koordinat ve yüzey oluşturulma kuralını ister bizden.Bunun
dışında renk gibi değişik özellikleri verebiliriz.Yukarıdaki örneğimiz için arrayleri
oluşturalım:
// ön yüzey için
float koordinatlar[8][3]={ { 0,0,0} ,// 1.nokta
{ 1,0,0} ,// 2.nokta
{ 0,1,0} ,// 3.nokta ...... devam ediyor
float vertices[12] ={ { 1,2,4,-1 } , /* 1.üçgen 1,2,4 noktalarını aldık -1 vertexin
tamamlandığını belirtir */
{ 1,4,3,-1} , /* 2.üçgen 1,4,3 noktalarını sırası ile tanıtıyoruz
................ diğer üçgenleri de oluşturuyoruz
|
Vertexleri Nesnemize Nasıl Tanıtırız?
/* diyelim ki elimizde Vertexler var (yukarıda ki vertices değerleri) bunu nesnemize
eklemek için SoIndexedFaceSet *faceSet = new SoIndexedFaceSet; şeklinde tanımlanmış
FaceSet nesnemize
faceSet->coordIndex.set1Value(1,2,4); şeklinde ekleyebiliriz , daha sonra
1,4,3 eklememiz gerekecektir */
/* SoIndexedFaceSet ve SoFaceSet nesneleri aynı görevi görürler tek farkları Indexed
sınıfları doğrudan koordinatları kullanırken NonIndexed sınıfları vertexProperty sınıflarını
kullanırlar.Bu sınıfı kullanmak istersek koordinatlar yerine vertexlerin (üçgenlik,beşgenlik
gibi )özelliklerini tanımlamamız gerekecektir.İlk yazımda ki help dosyasından nasıl
kullanılacağına bakabilirsiniz */
|
Uygulama :
Uygulamada elimizde belirli aralıkla yükseklik değerleri
var ve bu yükseklik değerlerini kullanarak bir harita yapacağız. Bu örnekte yükseklikler
bir text dosyasından okunuyor (her nokta arası x yönünde 40 metre y yönünde 30 metre) ve
daha sonra bu text dosyasındaki noktalardan koordinatlar ve vertexler oluşturuluyor.Yukarıda
verdiğim örnekteki kuralı takip ederek her 4 noktada bir 2 üçgen oluşturuluyor.
Uygulamada vector ve matrix fonksiyonları Numerical
Recipies kütüphanelerinin(*) fonksiyonlarıdır. Matris ve vektör tanımlamak için kullanırız. Malloc
veya new gibi komutlarla da bu işlem yapılabilir. Numerical Recipies kütüphanelerini
bir başka yazıda inceleyeceğiz.
yukarıda algoritmamızı görüyorsunuz sol yukardan saatin
ters yönünde üçgenler çizerek sağa doğru ilerliyoruz daha sonra bir alta geçip üçgenleri
sol taraftan oluşturmaya başlıyoruz.
#include <Inventor/Qt/SoQt.h>
#include <Inventor/Qt/SoQtRenderArea.h>
#include <Inventor/Qt/viewers/SoQtExaminerViewer.h>
#include <Inventor/SoDB.h>
#include <Inventor/nodes/SoCone.h>
#include <Inventor/nodes/SoCube.h>
#include <Inventor/nodes/SoDirectionalLight.h>
#include <Inventor/nodes/SoGroup.h>
#include <Inventor/nodes/SoMaterial.h>
#include <Inventor/nodes/SoPerspectiveCamera.h>
#include <Inventor/nodes/SoRotation.h>
#include <Inventor/nodes/SoSeparator.h>
#include <Inventor/nodes/SoIndexedFaceSet.h>
#include <Inventor/nodes/SoSphere.h>
#include <Inventor/nodes/SoTranslation.h>
#include <Inventor/nodes/SoCoordinate3.h>
#include <Inventor/sensors/SoTimerSensor.h>
#include <Inventor/actions/SoGLRenderAction.h>
#include <qapplication.h>
#include <qgroupbox.h>
#include <qlayout.h>
#include <stdio.h>
#include <stdlib.h>
#include "nr.h"
#include "nrutil.h"
void main(int argc, char **argv)
{
int i;
int j;
int x;
int y;
int count=0;
FILE *fpopen;
// Yükseklikeri okuyoruz
fpopen=fopen("heights.txt","r");
fscanf(fpopen,"%d\t%d",&x,&y);
float **values=matrix(0,x,0,y);
float *indice=vector(0,x*y*8);
for(j=0;j<y;j++)
{
for(i=0;i<x;i++)
{
fscanf(fpopen,"%f\t",&values[i][j]);
}
}
printf("flip the black thing on screen :) you see the mountains ");
QWidget *myWindow = SoQt::init(argv[0]);
if (myWindow == NULL) exit(1);
// Ana nesnelerimizi oluşturup SoFaceSet
nesnelerimizi de oluşturuyoruz.
SoSeparator *commonroot = new SoSeparator;
commonroot->ref();
SoSeparator *myObject = new SoSeparator;
SoCoordinate3 *myCoords = new SoCoordinate3;
SoIndexedFaceSet *faceSet = new SoIndexedFaceSet;
// Teker teker koordinatları myCoords
nesnesine ekliyoruz
for (j=0;j<y;j++){
for(i=0;i<x;i++){
myCoords->point.set1Value(i+x*j,(i%x)*40,(j%y)*30,values[i][j]);
}
}
myObject->addChild(myCoords);
// Vertexleri aşağıda oluşturuyoruz,Vertexler
oluşurken sol baştan başlayıp sağa kadar
// gittikten sonra bir aşağıya geçiyoruz
bu sefer sağ baştan başlayıp sola kadar gidiyoruz
// bu loopta soldan başlayıp sağa
kadar gidiyoruz
for(j=0;j<y-1;j++){
if ((j%2)==0){
for(i=0;i<x-1;i++){
// ilk üçgeni oluşturmak için bir aşağıya
sonra bir yana gidiyoruz
faceSet->coordIndex.set1Value(count++,i+j*x);
faceSet->coordIndex.set1Value(count++,i+x+j*x);
faceSet->coordIndex.set1Value(count++,i+1+j*x);
faceSet->coordIndex.set1Value(count++,-1);
// completed first
triangle
// aynı şekilde diğer üçgeni de oluşturmak için saatin
ters yönünde ilk üçgenle bitişik olacak şekilde yürüyoruz
faceSet->coordIndex.set1Value(count++,i+x+j*x);
faceSet->coordIndex.set1Value(count++,i+x+1+j*x);
faceSet->coordIndex.set1Value(count++,i+1+j*x);
faceSet->coordIndex.set1Value(count++,-1);
}
}
// bir alt sıraya geçiyoruz yine aynı yöntemle üçgenleri
oluşturuyoruz
if ((j%2)==1){
for(i=x-1;i>0;i--){
faceSet->coordIndex.set1Value(count++,i+j*x);
faceSet->coordIndex.set1Value(count++,i+x-1+j*x);
faceSet->coordIndex.set1Value(count++,i+x+j*x);
faceSet->coordIndex.set1Value(count++,-1);
// completed first
triangle
faceSet->coordIndex.set1Value(count++,i+j*x-1);
faceSet->coordIndex.set1Value(count++,i+x-1+j*x);
faceSet->coordIndex.set1Value(count++,i+j*x);
faceSet->coordIndex.set1Value(count++,-1);
}
}
}
// herşey bittikten sonra nesnelerimizi
üst nesnelere ekliyoruz
myObject->addChild(faceSet);
commonroot->addChild(myObject);
// Set up viewer:
SoQtExaminerViewer *myViewer = new SoQtExaminerViewer(myWindow);
//SoQtExaminerViewer *myViewer = new SoQtExaminerViewer(parent);
myViewer->setSceneGraph(commonroot);
myViewer->setTitle("Examiner Viewer");
myViewer->show();
// Map window and start event
loop.
//SoQt::show(parent);
SoQt::show(myWindow);
SoQt::mainLoop();
//return 0;
}
|
Kodumuzu çalıştırınca :
görüntüsünü elde ediyoruz. Görüntüler tanıdık geldi
değil mi? Herhangi bir oyunda yüzey taramasında gördüğünüz yüzey taramaları aşağı yukarı
bu yöntemlerle yapılıyor. Inventor gölge derinlik işlemlerini otomatik olarak ayarlıyor. Bize
ise bu görüntüyü daha da renklendirmek kalıyor. Bir daha ki yazıda görüşmek üzere!
[email protected]
(*) www.nr.com
Makale:
3D Grafik Programlama 3 : SoFaceSet Sınıfları,Vertexler C++ ve C++.NET dili Özkan Eren
|