CSSチームが運営するHTML5・CSS3情報サイト

HTML5 GOGO

ボーズ・オンラインストア

Mckee

OpenGL のウェブブラウザ版 WebGLの登場によって、ブラウザ上で精密な3DCGを描画できるようになりました。
GoogleマップがWebGLに対応して、プラグインなしで3D表示が可能になったり、美しい3DCGを駆使したゲームが数多く登場したりと、WebGLを使用したものが続々と誕生しています。
今回は、そんな見た目において非常に強力なAPIのWebGLに少し触れてみましたので、ご紹介させて頂きます。

サンプル概要

3DCGのスロットです。
手前のボタンをクリックすると、同色のリールが止まります。
右端のボタンを押すと、リールとボタンがリセットされます。

※対応ブラウザ:Chrome 最新
※動作にはCPUパワーが少々必要になります。
※サンプルコードは紹介用に編集してありますので、
動作しているコードと異なる部分があります。

サンプルアプリ

WebGLについて

WebGLは、Windows、Mac、Linuxなど様々な環境で2D/3DCGが扱えるAPI「OpenGL」のウェブブラウザ版です。
HTML5 の canvas 要素に3DCGを描画することができます。
使用条件はウェブブラウザがWebGLに対応していることと、グラフィックカードの OpenGL のバージョンが2.0以上であるということです。

Three.js について

Three.jsは数学的な知識が空っきしな私でも、
WebGLの素晴らしい機能を扱いやすくしてくれるJavaScriptライブラリです。
オープンソースで開発が進められており、WebGL界隈では絶大な人気を博しています。
JavaScriptが読み書きできれば、3DCGの知識は無くとも簡単なものならすぐに作れてしまうと思います。

Three.jp公式サイト(http://mrdoob.github.com/three.js/

サンプルアプリの解説

今回作成したサンプルアプリの工程を解説し、Three.jsの使い方を簡単にご紹介します。

スロットを作る

まずはスロット部分の作成です。
ここでは、「リール」「ボタン」「床」に分けて説明していきます。

リールを作る

まずは、スロットのリール部分に使用する多角柱を生成します。
多角注の生成には CylinderGeometry() を使用します。

var geometry = new THREE.CylinderGeometry(100, 100, 100, slot.radiusSegments, 10, false);

続いて、リールに描かれる番号を画像として用意しておき、それを読み込んでおきます。
それを先ほど作成した多角柱オブジェクトに貼り付けます。

var texture = new THREE.ImageUtils.loadTexture('images/r2.png');
var material = new THREE.MeshLambertMaterial({map: texture});
var mesh = new THREE.Mesh(geometry, material);

さらに、多角柱オブジェクトの位置を調整します。

mesh.rotation = {x: 0, y: 0, z: 1.57};

これらの工程をリールの数だけ行います。

ボタンを作る

まずは、ボタンとなるオブジェクトを生成します。
ここでは、リールと同じ多角柱を小さめに作って代用します。

var btnGeometry = new THREE.CylinderGeometry(20, 20, 20, 0, 10, false);

続いて、ボタンの色を設定します。ここでは、対応したリールと同じ色を設定することにします。

material = new THREE.MeshLambertMaterial({color: 0x00FF00});
var btnMesh = new THREE.Mesh(btnGeometry, material);

さらに、ボタンオブジェクトの位置を調整します。

btnMesh.rotation = {x: 0, y: 0, z: 0};
btnMesh.position.z = 120;
btnMesh.position.y = -100;

これらの工程をボタンの数だけ行います。

床を作る

まずは、床として使用する平面オブジェクトを生成します。

var groundGeometry = new THREE.PlaneGeometry(10000, 10000);

平面オブジェクトの色を設定します。

material = new THREE.MeshPhongMaterial({color: 0xFFFFFF});
var ground = new THREE.Mesh(groundGeometry, material);

続いて、平面オブジェクトの位置を調整します。

ground.rotation = {x: 5, y: 0, z: 0};
ground.position.z = -600;
ground.position.y = -300;

視覚効果を作る

ここでは、カメラとライトの設定を行っていきます。

カメラを作る

カメラの設定では、下記の投影方法を設定できます。

透視投影 THREE.PerspectiveCamera
平行投影 THREE.OrthographicCamera
  • 画角
  • 描画範囲の縦横比
  • 視点からの表示開始位置
  • 視点からの表示終了位置
var camera = new THREE.PerspectiveCamera(50, slot.width / slot.height, 1, 10000);
camera.position.y = 200;
camera.position.z = 450;
camera.lookAt(mesh.position);

ライトを作る

var light = new THREE.SpotLight(0xffffff);
light.position.set(0, 2000, 2000);
//light.angle = Math.PI / 4;
light.angle = 1;
light.target = mesh;
scene.add(light)

アニメーションを作る

リールを回転する

リールオブジェクトのX軸方向の回転角に、50ミリ秒ごとに0.4(ラジアン)ずつ加算しています。

slot.spin1 = function() {
if(slot.reel1.spinFlg || slot.reel1.speed > 0) {
mesh.rotation.x += slot.reel1.speed;

if(!slot.reel1.spinFlg) {
slot.reel1.speed -= 0.01;
}
slot.reel1.degree = Math.round(Math.abs(mesh.rotation.x) * 100) / 100 * slot.degreesParRadian;

if(!slot.reel1.spinFlg && slot.reel1.degree % slot.oneDeg < 1) {
slot.reel1.speed = 0;
} else if(slot.reel1.speed < 0.03) {
slot.reel1.speed = 0.02;
}
render();

setTimeout(arguments.callee, 25);
}
}

ボタンで止める

停止ボタンが押されたら、リールオブジェクトのX軸方向に加算する値を0.01ずつ小さくしていきます。
これが、リールがゆっくりと回転速度を落としていくアニメーションになります。
それと同時に、多角形の隣り合う頂点までの角度1つ分(多角形1面分)の角度で割り切れるかを確認します。
また、リールオブジェクトのX軸方向の角度に加算する値が、0.03未満になった場合、値を0.02で保ちます。
これがリールがジワジワ回転していくアニメーションになります。
割り切れるまでは、同様の動作を繰り返します。
割り切れた場合、処理を停止しいます。これでリールがストップします。

var getElementPosition = function(element) {
var top = left = 0;
do {
top += element.offsetTop || 0;
left += element.offsetLeft || 0;
element = element.offsetParent;
} while (element);
return {top: top, left: left};
}

var projector = new THREE.Projector();
renderer.domElement.addEventListener('click', function(e) {
var mouseX = e.clientX - getElementPosition(renderer.domElement).left;
var mouseY = e.clientY - getElementPosition(renderer.domElement).top;
var x = (mouseX / renderer.domElement.width) * 2 - 1;
var y = - (mouseY / renderer.domElement.height) * 2 + 1;
var vector = new THREE.Vector3(x, y, 1);
projector.unprojectVector(vector, camera);

var ray = new THREE.Ray(camera.position, vector.subSelf(camera.position).normalize());
var intersects = ray.intersectObjects(meshArray);

if(intersects.length > 0){
var color = 0x666666;
intersects[ 0 ].object.material.color.setHex( color );
}
switch(intersects[0].object.id) {
case 4:
slot.reel1.spinFlg = false;
break;
case 5:
slot.reel2.spinFlg = false;
break;
case 6:
slot.reel3.spinFlg = false;
break;
case 7:
slot.init();
slot.spin1();
slot.spin2();
slot.spin3();
btnMesh.material.color.setHex(0x00FF00);
btnMesh2.material.color.setHex(0x0000FF);
btnMesh3.material.color.setHex(0xFF0000);
break;
}

renderer.render( scene, camera );
});

まとめ

ソースコードをまったく整理できていませんが、ほとんど参考ソースのつぎはぎで大体できてしまいました。
Three.jsの登場で JavaScriptが理解できれば、WebGLを使うことがいくらか容易になりました。
ぜひ一度、3DCGに挑戦してみて下さい。
アニメーションを生成する大枠の流れは、今回のサンプルと同じようなものだと思います。
さらにThree.jsにまかせていた部分も含め、カスタムシェーダーを作ることで更に柔軟な3DCGを表現できるようです。
WebGLすごいです!!

Subscribe:
pagetop