Three.js ile interaktif küre çizimi

Three.js webgl ile tarayıcıda 3 boyutlu çizimler yapmaya yardımcı olan bir kütüphane.

Beach ball çizimi için bir görselleştirme yapmak istedim. Bunun için fare kontrolünde dönecek küre çizimi yaptım.

İlk önce kütüphaneyi sayfaya dahil edip

<script src="three.min.js"></script>

gerekli css kodunu yazıyoruz.

canvas {
  width: 400px;
  height: 300px;
}

Şimdi Scene, Camera ve Renderer nesnelerini oluşturuyoruz. canvas bizim dom nesnemiz olacak. mousePos döndürme işlemi için kullanacağımız ve fare pozisyonunu tutacağımız değişken. Farenin sol tuşu basılı iken dön diyeceğiz o yüzden rotateMode değişkeninde de sol tuşun basılı olup olmadığını tutacağız. Son olarak group ise küre ve üstündeki noktaları içerecek bir nesne olacak.

var w = 400;
var h = 300;
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(75, w/h, 0.1, 1000);
var renderer = new THREE.WebGLRenderer();
var canvas = renderer.domElement
var mousePos = [0, 0];
var rotateMode = false;
var group = new THREE.Object3D();

renderer boyutunu ayarlayıp, arkaplan rengini seçelim. Daha sonra ise grup nesnesini sahneye ekleyelim ve kamerayı z yönünde uzaklaştıralım.

renderer.setSize(w, h);
renderer.setClearColor(0xEEEEEE);
document.body.appendChild(canvas);
scene.add(group);
camera.position.z = 5;

Küreyi çizmek için SphereGeometry ile 1 birim çapında genişlik ve yükseklik segmentleri 10 olan bir küre geometrisi oluşturalım. Gri renkli tel kafes modunda bir material tanımlayıp küreyi Mesh ile oluşturabiliyoruz. Zaten sahnede olan group nesnesine de ekleyince sahneye eklemiş oluyoruz.

var sphereGeometry = new THREE.SphereGeometry(1,10,10);
var sphereMaterial = new THREE.MeshBasicMaterial({
  color: 0x999999,
  wireframe: true
});
var sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
group.add(sphere);

Kürenin içine ve üstüne nokta koymak için daha küçük küreler oluşturacak bir fonksiyon yazalım. Bu noktalara renk de vermek için fonksiyonunun renk parametresi olsun.

function create_dot(color) {
  var geometry = new THREE.SphereGeometry(0.05, 10, 10);
  var material = new THREE.MeshBasicMaterial({
    color: color
  });
  var dot = new THREE.Mesh(geometry, material);
  return dot;
}

bir de kolaylık olsun diye dereceyi radyana çeviren bir fonksiyon yazalım.

function d2r(d) {
  return d*Math.PI/180;
}

Biri merkezde (siyah) biri de kürenin üstünde (mavi) nokta oluşturalım. Kürenin üstünde olanın yerine belirlemek için küresel koordinatlar kullandım. Dünya gibi düşünürsek 45°K enleminde 30°D boylamında bir yerde olacak şekilde yazdım.

group.add(create_dot(0x000000));
dot2 = create_dot(0x0000FF);
dot2.position.x = Math.cos(d2r(45))*Math.cos(d2r(30));
dot2.position.y = Math.cos(d2r(45))*Math.sin(d2r(30));
dot2.position.z = Math.sin(d2r(45));
group.add(dot2);

Bir de küreyi kesen bir düzlem ekleyelim. 2.1 genişliğinde ve 2.1 yüksekliğinde x ve y yönünde biraz döndürülmüş ve sarı renkli olsun.

var planeGeometry = new THREE.PlaneGeometry(2.1, 2.1);
var planeMaterial = new THREE.MeshBasicMaterial({
  color: 0xffff00, side: THREE.DoubleSide
});
var plane = new THREE.Mesh( planeGeometry, planeMaterial );
plane.rotation.y = d2r(60);
plane.rotation.x = d2r(45);
group.add(plane);

Sıra geldi fareyi izlemeye. Sol tuşa basılı ise rotateMode u true yapıp parmak kaldırıldığı zaman false olsun. Sol tuş basılı iken fare hareket ettirilirse, canvas'ın konumunu da göz önüne alarak, farenin konumunu mousePos değişkenine bir dizi ([x, y]) şeklinde yazalım.

canvas.onmousedown = function() {
  rotateMode = true;
}
canvas.onmouseup = function() {
  rotateMode = false;
}
canvas.onmousemove = function(e) {
  if (rotateMode) {
    var rect = canvas.getBoundingClientRect();
    var x = e.clientX - rect.left;
    var y = e.clientY - rect.top;
    mousePos = [x, y];
  }
}

Son olarak render işlemini yapacağız. Yine rotateMode açık ise group'u farenin canvas'ın ortasına göre konumuna göre döndüreceğiz. Bu döndürmenin çok hızlı olmasın diye bir sayıya bölmek gerekiyor. Ben burada 5000 seçtim.

var render = function () {
  requestAnimationFrame(render);
  if (rotateMode) {
    group.rotation.y += (mousePos[0] - w/2)/5000;
    group.rotation.x += (mousePos[1] - h/2)/5000;
  }
  renderer.render(scene, camera);
};
render();

Sonuç aşağıdaki gibi oluyor. Ayrı olarak ise şurada.