こんにちは。
KRAYアルバイトの浅海です。
今回は、最近ちまたで噂のThree.jsで遊んでみようとおもいます。
http://github.com/mrdoob/three.js
webgl対応のブラウザで見ている方は、右上に3Dのボックスが表示されていると思います。
Three.jsを使えば、このようなことを簡単にJavascriptで実現できるのです。
半信半疑で使ってみたところ、たしかにThree.jsでは、3D特有の行列計算や数学的思考をせずとも、3Dを表示、操作することができました。
「ちょっとのプログラムで3Dができる。」
この面白さを皆さんに届けたいです。
目次
Three.js
web上には、HTML5のcanvasを利用した様々なサンプルページやアプリケーションが色々できてきました。
その中でも、ひと際目を引くのがThree.jsを使用したサンプルです。
ブラウザの進歩もここまできたか・・・、というようなものばかりです。
ついこの間まで人類は、marqueeタグやマウスポインタを変な画像が追っかけるjavascriptで遊んでいたはずですが・・・
このThree.jsのサンプル、どれを見ても、とても難しそうなことをしています。
人の顔が3Dで精巧に作られていたり、なんか凄い綺麗な空間がはじけていたり・・、ですが、ソースを見てみると、JavaScript部分は非常に短い事がわかります。
Three.jsがほとんどの計算をやってくれるからです。
立方体を表示する
Hello world!!
さて、単純な立方体を表示してみます。
HTML
canvasが無いことに気づくと思います。
canvasはThree.jsが内部的に生成するので必要ありません。
[cc lang='html']
[/cc]
JavaScript
Three.jsには大きさを指定するだけで立方体を作ってくれる機能があり、四角形を表示するだけならJavaScriptは約15行書くだけです。お手軽!
[cc lang='js']
var width = 640;
var height = 480;
var geometry = new THREE.CubeGeometry(100, 100, 100); // 立方体作成
var material = new THREE.MeshBasicMaterial({color: 0x0000aa}); // 材質作成
var mesh = new THREE.Mesh(geometry, material); // 立方体と材質を結びつけてメッシュ作成
var camera = new THREE.PerspectiveCamera(40, width / height, 1, 1000); // カメラ作成。画角40、距離1〜1000の部分を表示できる。
camera.position.z = -400; // カメラの位置はz軸の-400
camera.lookAt(mesh.position); // メッシュの位置にカメラを向ける。
var scene = new THREE.Scene(); // シーン作成
scene.add(mesh); // シーンにメッシュ追加
var renderer = new THREE.WebGLRenderer(); // レンダラ作成
renderer.setSize(width, height);
window.onload = function(){
document.getElementById('canvas-wrapper').appendChild(renderer.domElement);
renderer.render(scene, camera); // sceneをcameraで映す(表示)
}
[/cc]
四角形が表示されました。
レンダラー
Three.jsには、canvasのレンダラとwebGLのレンダラを切り替えることができる機能があります。
canvasのレンダラを使えばiPad等、webGLに対応していない環境でも見ることができるのですが、WebGLレンダラでないと実現できないことも多くあるようです。
といっても今回のサンプルはCanvasレンダラでも十分やっていけますので、
WebGL対応してない方のために、この記事の最後にCanvasレンダラ対応のものを用意してあります。
canvasのレンダラを使用したい場合は、上記javascript15行目
THREE.WebGLRenderer()
の部分を
THREE.CanvasRenderer()
とすることにより、canvasのレンダラを使用できます。
光をあてる
とりあえず表示されましたが、ベタ塗りされているので立方体なのかどうかはまだわかりません。
非常にダサい感じがあります。
なので光をあてて輪郭をはっきりさせてみます。
JavaScript
光源を作成してsceneに追加します。
[cc lang='js']
// scene作成後に追加
var light = new THREE.DirectionalLight(0xffffff, 1.5); // 光源の色, 強さ
light.position = {x: 0, y: 0.2, z: -1} // 光源の位置
scene.add(light);
[/cc]
また、現在の立方体に設定されているマテリアル(材質)だと光を反射しないので、光を反射してくれるマテリアルに変更します。
[cc lang='js']
// material作成の行を以下と入れ替え
// ランバート反射を行うマテリアル
var material = new THREE.MeshLambertMaterial({color: 0x0000aa});
[/cc]
これで立方体の輪郭がはっきりして、3Dプログラムをしている気分になりました!
テクスチャを貼り付ける
テクスチャとしてこの画像を使います。
テクスチャがなんなのかは、サンプルをどうぞ。
JavaScript
Materialの作成した部分を以下のように書き換えます。
[cc lang='js']
var texture = new THREE.ImageUtils.loadTexture('/images/anko.jpg');
var material = new THREE.MeshLambertMaterial({map: texture}); // テクスチャ反映
[/cc]
これだけで、方向なども自動にテクスチャが貼られます。
お手軽ですね。
マウスで回転させる
マウスドラッグで立方体を回転できるようにします。
単純化のため、マウスのx移動、y移動をそのままx方向、y方向の回転に対応させます。
なので、立方体を逆さにするとx回転が逆になりますが、そこは目をつぶって下さいませ・・・。
JavaScript
角度の変更は、meshのrotationを変えるだけでできるようになっています。
簡単です。
[cc lang="js"]
var mousedown = false;
renderer.domElement.addEventListener('mousedown', function(e){
mousedown = true;
prevPosition = {x: e.pageX, y: e.pageY};
}, false);
renderer.domElement.addEventListener('mousemove', function(e){
if(!mousedown) return;
moveDistance = {x: prevPosition.x - e.pageX, y: prevPosition.y - e.pageY};
mesh.rotation.x += moveDistance.y * 0.01;
mesh.rotation.y -= moveDistance.x * 0.01;
prevPosition = {x: e.pageX, y: e.pageY};
render();
}, false);
renderer.domElement.addEventListener('mouseup', function(e){
mousedown = false;
}, false);
[/cc]
描画をrender関数にまとめました。
[cc lang='js']
function render(){
renderer.render(scene, camera);
}
[/cc]
マウスで移動させる
シフトを押しながらマウスドラッグで立方体を移動できるようにします。
JavaScript
mousemoveのイベントリスナを以下のように書き換えます。
positionを変更することにより移動できます。
[cc lang='js']
if(e.shiftKey){
mesh.position.x += moveDistance.x * 0.5;
mesh.position.y += moveDistance.y * 0.5;
}
else{
mesh.rotation.x += moveDistance.y * 0.01;
mesh.rotation.y -= moveDistance.x * 0.01;
}
[/cc]
これで移動できるようになりました。
移動や回転、基本的なことはやったので、次はもう少し高度なことを試してみます。
クリック判定する
マウスが立方体をクリックしたかどうかを簡単に検出することができます。
そう、Three.jsならね。
3Dのプログラムでよく使われる、レイによる衝突判定を行います。
イメージとして、クリックした位置から、3Dの世界に向かってレイ(光線)が発射されます。
その光線が、メッシュと衝突したかどうかを計算して判定しているのです。
といっても、全ての計算はThree.jsがやってくれますが・・。
JavaScript
以下を追加します。
[cc lang="js"]
var projector = new THREE.Projector();
renderer.domElement.addEventListener('mousedown', function(e){
var mouse_x = ((e.pageX-e.target.offsetParent.offsetLeft) / renderer.domElement.width) * 2 - 1;
var mouse_y = - ((e.pageY-e.target.offsetParent.offsetTop) / renderer.domElement.height) * 2 + 1;
var vector = new THREE.Vector3(mouse_x, mouse_y, 0.5);
projector.unprojectVector(vector, camera);
var ray = new THREE.Ray(camera.position, vector.subSelf(camera.position).normalize()); // レイの作成
var obj = ray.intersectObjects([mesh]); // レイがメッシュに衝突するかどうか
// 戻り値は、衝突したメッシュが配列で入っている。
if(obj.length > 0){
var text = document.createTextNode("立方体をクリックしました。 x = "+obj[0].point.x+"y = "+obj[0].point.y+"z = "+obj[0].point.z);
var p = document.createElement('p')
p.appendChild(text);
document.body.appendChild(p);
}
}, false);
[/cc]
ray.intersectObjects(array)
このメソッドには、meshを配列で渡します。
今回の例はmeshが一つですが、複数のメッシュを配列で持っておけば、全てのメッシュとレイの衝突判定を行なってくれます。
戻り値は衝突したメッシュ毎に配列で返ってきます。
その内容は以下のとおりで、これだけの情報が簡単に取得できるThree.jsは凄いですね。
[cc lang="js"]
distance: // 距離
point: {x: y: z:} // クリック座標
object: {} // クリックされたオブジェクトの情報
face: {} // クリックされた面の情報
[/cc]
最後に
Three.jsで3Dした気分になりました。
簡単な3Dゲームですら実現できそうです。
はたしてこの先、ブラウザ上の3Dはどう活用されていくのでしょうか・・・。
個人的に、最後の例のように、文章など普通のwebコンテンツの上に3Dがドカーンと置いてあるのは、中々面白いと思います。
おまけその1 Canvasレンダラ使用Ver
上記と同じものを、Canvasレンダラを使用して作ったバージョンです。
WebGLに対応していないiPadなどでも見えるかと思います。
タッチイベントに対応するために、プログラムは少し変えています。
webGLレンダラ版と違い、動きがもっさりして、ポリゴンの継ぎ目が見えてしまいます。
さらに、初代のiPadでは、テクスチャが壊れてしまいました。
操作方法
1本指:回転
2本指:移動
おまけその2 lookAt
camera.lookAt(position)
でカメラをpositionの位置に向けられます。
クリックした物体の方向にカメラを向けるサンプル。
lookAtのサンプル
おまけその3 githubリポジトリ
全てのサンプルページではソースを見れますが、githubにも公開しました。
ご自由にお使い下さい!
https://github.com/ttakuru88/webgl_sample
リポジトリ git://github.com/ttakuru88/webgl_sample.git
以上です。
- トラックバック
-
- [Javascript]Three.jsを触る | えっ?・・・・・・あっ・・・・・・うん。2013/05/29, 11:55 PM
[…] 簡単に立方体を書いてみる。 以下、ここのまねをして書いてみる。 […]
「いいね!」で応援よろしくお願いします!