Java 3D
Son yıllarda çoklu ortamda ilerleme kaydeden Java, Java3D ile yüksek performans isteyen üç boyutlu grafiklerde de 'Ben varım!' diyor.
OpenGL (Open Graphics Library) üç boyutlu (3D) uygulamalara yazmak için dilden ve platformadan bağımsız standart bir spesifikasyondur. Üç boyutlu, hatta iki boyutlu karmaşık şekiller çizmeye yaramaktadır. Oyunların çoğu OpenGL tabanlıdır. DirectX, OpenGL'e alternatif olarak Microsoft tarafından geliştirilmiş üç boyutlu grafik ve çoklu ortam kütüphanesidir. Java3D ise OpenGL veya DirectX altyapısını kullanarak üç boyutlu nesnelerle program yapmaya yarayan bir Java küyüphanesidir. OpenGL ve DirectX düşük düzeyli yani makineye daha yakın temel fonksiyonları içerirken, Java3D nesneye yönelik programlamaya uygun bir yazılım altyapısıdır. Java 3D Kütüphaneleri Java 3D kütüphanesi java.media.j3d paketindir. Üç boyutlu nesnelerde çok miktarda vektor ve matris işlemleri olduğu için javax.vecmath kütüphanesi de Java3D kütüphanelerine dahildir. Java 3D kütüphanesi çok temel sınıflar içerir, basit bir örnek için bir çok miktarda kod yazmak gerekir. O yüzden Sun firması com.sun.j3d ile başlayan çok miktarda yardımcı ve faydalı nesne geliştirmiştir. Örneğin Java 3D'de küp yapmaya yarayan bir nesne yokdur, bunun için com.sun.j3d.utils.geometry.ColorCube adında, her yüzeyinde değişik bir renk olan bir küp bulunmaktadır. Elbette progragmcı bunu kendi de yapabilir ama Sun'ın yardımcı sınıflar basit bir üç boyutlu nesne yapmak için en başta bir miktar kod yazmak zorunda olmaktan kurtarmaktadır. Canvas3D Üç boyutlu nesnelerin çizilmesi için bir tuval yani 'canvas' gereklidir. Java 3D kütüphanesi Java'nın standart arayüz kütüphanesi AWT üzerinde çalışır. Canvas3D sınıfı java.awt.Canvas sınıfını genişletir. Örneğin public class ColorCubeTest extends Frame { şeklindeki kod, sistemin grafik konfigurasyonunu kullanan bir tuval oluşturmakta ve bir pencerenin ortasına yerleştirmektedir. Universe (Evren) Java3D'de çizilen bütün nesneler bir evren'de ('universe'de) yer alır. Öncelikle tuval (canvas) üzerinde çalışan evren oluşturulur. Evren aslında VirtualUniverse sınıfıyla temsil edilir ancak Sun'ın faydalı sınıfları arasında bu sınıfı genişleten SimpleUniverse sınıfı bulunmaktadır. Örneğin public class ColorCubeTest extends Frame { biçiminde kod bir evren oluşturmakta ve tuvale atamaktadır. Point (Nokta) & Vector (Vektör) Behaviour (Davranış) Normal şartlarda canvas ve evren durağandır. İzleyici gördüğü üç boyutlu nesneler karşısında hareket edemez. Bir kamera hareketi gibi bir işlev görmek için ViewPlatformBehavior sınıfı bulunmaktadır. Behaviour (davranış) sınıfları kullanıcı ile üç boyutlu evren arasında iletişimi sağlar. ViewPlatformBehavior ise, farenin sol tuşuyla şeklin etrafında dönülen, sağ tuşuyla da şeklin karşısında harekete edilen eylemleri destekler. Bir evrene bir 'davranış' eklemek için public class ColorCubeTest extends Frame { Burada orijin etrafında dönme yapılabilen kullanıcı etkileşimi kodlanmıştır. BranchGroup (Dal Grubu) Java3D'de her nesne bir 'dal grubu' (BranchGroup) içerisindedir. Yani bir veya daha fazla nesne belli bir orijine göre belli bir koordinat sistemini taban alarak konumlandırılmıştır ve bu nesnelerden oluşan 'dal grubu' birlikte hareket eder. Bir üç boyutlu nesne oluşturulduktan sonra bir dal grubu oluşturulur ve nesne o dal grubuna eklenir. O dal grubu da evrene eklenir. Evren > Dal Grubu > Şekil şeklinde bir yapı vardır. Evrende dal grupları, dal gruplarında da şekiller bulunur. Bir dal grubu ötelenir veya döndürülürse o dal grubundakiğ bütün şekiller ötelenir veya döndürülür. Şekillere örnek olarak ColorCube adında, her yüzeyi değişik renkte olan bir nesne alınabilir. Bu şekli bir dal grubuna eklemek ve bu dal grubuna da bir evrene eklemek için public class ColorCubeTest extends Frame { public ColorCubeTest() { } şeklinde bir kod yazılır. Farenin sürükle bırak özelliğiyle etrafında gezinebildiğimiz bir ColorCube'ü içeren bir evren için tam bir örnek şu şekilde yazılabilir: Godoro.Web.Common/Upload/HTMLEditorFolder/ColorCubeTest.java Light (Işık) Java3D'de üç boyutlu ortamda ışık kullanılabilir. Işık, PointLight yani noktasal (bir noktadan her yöne doğru yayılan), DirectionalLight yani doğrusal (bir noktadan belli bir yöne doğru yayılan) ve AmbientLight yani ortamsal (belli bir merkezi ve yönü olmadan ortamda bulunan) biçimlerde olabilir. Ayrıca bir ışığın sınırları belirlenebilir, ışık o sınırlar dışına ulaşmaz. Işık nesneleri dal grubu (BranchGroup) nesnelerine bir şekil gibi eklenirler ve grubun bir parçası olurlar. Doğrusal ışık eklemek için private BranchGroup createGroup(){ şeklinde bir kod yazılabilir. Burada bir ışığın rengi, yönü ve sınırları verilmektedir. Ortam ışığı için oluşturmak için de private Light createAmbientLight(){ şeklinde bir kod yazılır. Belli bir yönden kırmızı ışık alan bir küre örneği şu şekilde yapılabilir Godoro.Web.Common/Upload/HTMLEditorFolder/LightSphereTest.java Appearance (Görünüm), Material (Malzeme) & Texture (Doku) Üç boyutlu nesnelerin fiziksel boyutları dışında Appearance sınıfıya belirtilen "görünümleri" olur. Görünümün en önemli bileşenleri Material sınıfıyla belirtilen malzeme, Texture sınıfıyla belirtilen doku özellikleridir. Material sınıfı maddenin rengini ve yaydığı ışığı tanımlar. Texture ise bir resim olabildiği gibi belli bir formüle göre görünüşü belirleyen bir yapı da içerebilir. Bir malzeme şu şekilde yüklenebilir : private Material createMaterial() { Burada malzemeye 'ambient', 'emissive', 'diffuse' gibi malzemesel renk özellikleri ve palaklık tanımı yapılmaktadır. Doku yüklemek için private Texture createTexture(){ şeklinde bir kod yazılabilir. Burda bir resim dokuya yüklenmekte ve reknk gibi parametreler ayarlanmaktadır. BoundaryMode olarak S ve T, dokunun kendi düzleminde üzerinde yatay ve dikey yönde resmin dokunun eksik kısımlarını nasıl tamamlayacağı belirtiliyor. WRAP değeri, resmi yüzey boyunca tekrar tekrar koyulması anlamına gelir. Bir küre üzerine bir vesikalık fotoğrafı koyan bir örnek şu şekilde yapılabilir : Godoro.Web.Common/Upload/HTMLEditorFolder/TextureTest.java Transformation (Dönüşüm) Bir veya daha fazla şeklin belli bir konum değişikliği yapması 'dönüşüm' (transform) işlemidir. Bir dönüşüm birden fazla dönüşümü, örneğin bir kaç ötemele (translate) ve dönme (rotate) işlemini içerebilir. Üç boyutta ödeleme veya dönme gibi dönüşüm işlemleri Transform3D sınıfıyla gösterilir. Bir veya daha fazla dönüşüm işlemini birleştirmek için TransformGroup sınıfı kullanılır. Dönüşüm uygulanacak şekiller transform grubuna eklenir. Yani bir şekle çeşitli dönüşümlerin uygulanması çeşitli dönüşümler içeren bir dönüşüm grubuna bir şeklin eklenmesiyle olur. Bir dönüşüm grubuna birden fazla şekil eklenerek çok sayıda şeklin aynı dönüşümlere tabi olması sağlanabilir. Basit bir ötemele dönüşümü private TransformGroup createTransform(float x,float y,float z){ şeklinde yapılabilir. Burda verilen x, y ve z koordinatları kadar öteleme yapan bir dönüşüm grubu oluşturlmaktadır. Bir dal grubuna bir şekli dönüştürüp eklemek için BranchGroup group = new BranchGroup(); Üç boyutlu uzayda üç boyutun eksenleri boyunca küre (Sphere), koni (Cone) ve kutu (Box) şekillerini dizmek için private BranchGroup createGroup(){ Burada Sphere, Cone ve Box nesneleri oluşturuluyor, belli bir öteleme yapacak şekilde oluşturulmuş dönüşüm grubuna ekleniyor. Oluşturulan dönüşüm grupları da asıl dal grubuna ekleniyor. Bu örneğin ışıklandırması da eklenmiş tam hali şu şekilde yazılabilir : Godoro.Web.Common/Upload/HTMLEditorFolder/TransformTest.java Animation (Canlandırma) & Interaction (Etkileşim) Üç boyutlu şekillerle canlandırma (animasyon) yapmak mümkündür. Animsayon için Java3D'ye özel bir kod yazmak gerekmez, belli bir zamanda şekilleri veya konumlarını değiştirmek yeterlidir. Hareket içeren animasyonlar, bir dal grubuna belli bir dönüşüm uygulamaktan ibarettir. Örnek olarak bir aşağı bir yukarı giden bir küre yapılabilir. Bunun için belli bir zamanda yapacağı hareketi tanımlayan bir yöntem yazmak gerekir : public void move() { Burada 'group' adındaki dal grubuna bir dönüşüm uygulanmaktadır. Dönüşüm için yükseklik ('height') sürekli değiştirlmektedir. Değişimin yönü 'sign' parametresiyle belirlenmekte, belli bir sınıra ulaştığında sign değişkeni tesr işaret almakta ve kürenin hareket yön ters yönde değişmektedir. Hareket ettiren yöntemin bir canlandırma oluşturması için bu yöntemin zaman içine tekrar tekrar çağrılmasını bir akış (thread) veya zamanlayıcı (timer) aracaılığıyla sağlamak gerekir. Bunun için javax.swing.Timer sınıfı kullanılabilir : ActionListener listener=new ActionListener(){ Burada yapılan 100 milisaniye arayla hareket ettiren yöntemin çağrılmasını sağlamaktır. Canlandırma şekilleri zamana bağlı olarak konum veya şekil değişikliği demektir. Ancak oyun gibi konularda değişimi zamanın yerine kullanıcının belirlemesi, yani etkileşim (interaction) gerekebilir. Başka bir deyişle kullanıcı fare veya klavye hareketiyle şekillerin konumu veya biçimi üzerinde değişiklik yapabilir. Örnek olarak aşağı yukarı hareket eden kürenin kullanıcı tarafından sağa ve sola doğru hareket etmesi sağlanabilir. Bunun için klavyeyi KeyListener olarak dinlemek yeterlidir. Tuşa basıldığında public void keyPressed(KeyEvent e) { şeklinde sola veya sağa tuşuna basılmasına göre x koordinatı değiştirilir. Zamanlayıcının bir sonraki zaman diliminde hareket ettiren move() yöntemini çağrılmasıyla şekil yeni konumunda dönüşüm uygulanarak gösterilecektir. Hem canlandırma hem de kullanıcı etkileşimi içeren örnek şu şekilde yazılabilir : Godoro.Web.Common/Upload/HTMLEditorFolder/AnimationTest.java
Sonuç Java 3D ile yapılabilecekler yukarıda anlatılanlardan ibaret değil. 3D grafiklerinde olan standart tüm işlemler, şekil değişikliği (Morphing), üç boyutlu ses (Audio 3D), başka üç boyutlu çizim formatlarını tanıma (örneğin Lightwave )gibi özellikler Java3D'de bulunmaktadır. Tam anlamıyla bitmiş bir kütüphane gözüyle bakmak zor ama şimdiye kadar yapılanlara bakılarak Java 3D'nin gelecek vadettiği söylenebilir. Java3D'nin OpenGL/DirectX'le doğrudan çalışan kütüphanelere göre nesneye yönelik ve daha kolay olduğu söylenebilir. Yakın bir gelecekte 3 boyutlu Java programlamayı oyun ve veri görselleştirme alanlarında sıkça görebiliriz.
public ColorCubeTest() {
setLayout(new BorderLayout());
GraphicsConfiguration config =SimpleUniverse.getPreferredConfiguration();
Canvas3D canvas = new Canvas3D(config);
add(BorderLayout.CENTER,canvas);
}
}
public ColorCubeTest() {
// ...
SimpleUniverse universe = createUniverse(canvas);
BranchGroup group =createGroup();
universe.addBranchGraph(group);
}
private SimpleUniverse createUniverse(Canvas3D canvas) {
SimpleUniverse universe = new SimpleUniverse(canvas);
ViewingPlatform platform=universe.getViewingPlatform();
platform.setNominalViewingTransform();
return universe;
}
Uzayda bir noktayı göstermek için PointXXX sınıfları bulunmaktadır. Bunlar 2 ve 3 boyutlu ve 4 boyutlu (x,y,z üç boyutu ve homojen kordinatlardaki dördüncü 'w' boyutu) gibi boyut seçeneklerine, integer,float ve double tip seçeneklerine göre çeşitli nokta sınıfları bulunmaktadır. Örneğin Point3d
// ...
private SimpleUniverse createUniverse(Canvas3D canvas) {
SimpleUniverse universe = new SimpleUniverse(canvas);
ViewingPlatform platform=universe.getViewingPlatform();
platform.setNominalViewingTransform();
ViewPlatformBehavior behavior=createBehaviour(canvas);
platform.setViewPlatformBehavior(behavior);
return universe;
}
private ViewPlatformBehavior createBehaviour(Canvas3D canvas){
int flags=OrbitBehavior.REVERSE_ALL |OrbitBehavior.STOP_ZOOM;
OrbitBehavior behavior = new OrbitBehavior(canvas,flags);
Point3d origin = new Point3d(0.0,0.0,0.0);
BoundingSphere bounds =new BoundingSphere(origin, 100.0);
behavior.setSchedulingBounds(bounds);
return behavior;
}
}
// ...
BranchGroup group =createGroup();
universe.addBranchGraph(group);
}
private BranchGroup createGroup(){
BranchGroup group = new BranchGroup();
group.addChild(new ColorCube(0.3));
return group;
}
BranchGroup group = new BranchGroup();
// ...
Light directional=createDirectionalLight();
group.addChild(directional);
return group;
}
private Light createDirectionalLight(){
Color3f color = new Color3f(1f, 1f, 1f);
Point3d origin = new Point3d(0.0,0.0,0.0);
BoundingSphere bounds = new BoundingSphere(origin, 100.0);
Vector3f direction = new Vector3f(4.0f, -7.0f, -12.0f);
DirectionalLight light = new DirectionalLight(color, direction);
light.setInfluencingBounds(bounds);
return light;
}
Color3f color = new Color3f(.5f,.5f,.5f);
AmbientLight light = new AmbientLight(color);
Point3d origin = new Point3d(0.0,0.0,0.0);
BoundingSphere bounds = new BoundingSphere(origin, 100.0);
light.setInfluencingBounds(bounds);
return light;
}
Color3f black = new Color3f(0.0f, 0.0f, 0.0f);
Color3f white = new Color3f(1.0f, 1.0f, 1.0f);
Color3f red = new Color3f(0.7f, .15f, .15f);
Material material= new Material(white, red, white, red, 1.0f);
return material;
}
String file="EPicsOnderTekerTied.jpg";
TextureLoader loader=new TextureLoader(file,"LUMINANCE",new Container());
Texture texture = loader.getTexture();
texture.setBoundaryModeS(Texture.WRAP);
texture.setBoundaryModeT(Texture.WRAP);
Color4f color= new Color4f( 0.0f, 1.0f, 0.0f, 0.0f );
texture.setBoundaryColor( color);
return texture;
}
TransformGroup group = new TransformGroup();
Transform3D transform = new Transform3D();
Vector3f vector = new Vector3f(x, y, z);
transform.setTranslation(vector);
group.setTransform(transform);
return group;
}
Sphere sphere = new Sphere(0.05f);
TransformGroup transform=createTransform(3.0f,4.0f,5.0f);
transform.addChild(sphere);
group.addChild(transform);
şeklinde bir kod yazılır. Burda bir küreye (3,4,5) miktarında dönüşüm uygulanır. Sonra dönüşüm grubu dal grubuna eklenir. Dönüşüm grubu aslında dal grubunun koordinat sistemine göre belli bir dönüşüm uygulanmış yeni bir koordinat sistemidir. Her dönüşüm, eklendiği grubun koordinat sistemine göre tanımlıdır.
BranchGroup group = new BranchGroup();
for (float x = -1.0f; x <= 1.0f; x = x + 0.1f){
Sphere sphere = new Sphere(0.05f);
TransformGroup transform=createTransform(x,.0f,.0f);
transform.addChild(sphere);
group.addChild(transform);
}
for (float y = -1.0f; y <= 1.0f; y = y + 0.1f){
Cone cone = new Cone(0.05f, 0.1f);
TransformGroup transform=createTransform(.0f, y, .0f);
transform.addChild(cone);
group.addChild(transform);
}
for (float z = -1.0f; z <= 1.0f; z = z+ 0.1f){
Box box = new Box(0.03f,0.03f,0.03f,null);
TransformGroup transform=createTransform(.0f, .0f, z);
transform.addChild(box);
group.addChild(transform);
}
return group;
}
height += .1 * sign;
if (Math.abs(height *2) >= 1 ){
sign = -1.0f * sign;
}
Vector3d vector=new Vector3d(1.0, .8, 1.0);
if (height<-0.4f) {
vector=new Vector3d(1.0, .8, 1.0);
}else {
vector=new Vector3d(1.0, 1.0, 1.0);
}
transform.setScale(vector);
Vector3f translation=new Vector3f(x,height,0.0f);
transform.setTranslation(translation);
group.setTransform(transform);
}
public void actionPerformed(ActionEvent e ) {
move();
}
};
timer = new Timer(100,listener);
if (e.getKeyCode()==KeyEvent.VK_RIGHT) {
x+=.1f;
}else if (e.getKeyCode()==KeyEvent.VK_LEFT){
x-=.1f;
}
}
Kaynak www.godoro.com/Portal/Content/Article/ArticleViewPage.aspx