LotosLabo

プログラミング技術とか気になった情報を載せていきます

Three.jsについて

Three.jsとは

WebGLをサポートした3D描画ライブラリです。

また、WebGLは実行できないブラウザがあるので確認が必要です。
http://caniuse.com/#search=webGl


開発環境

http://threejs.org/
のdownloadよりファイルをダウンロード出来ます。

そして解凍したら、 three.min.js というファイルを取り出してください。
bulidファイルに入ってると思います。
これをhtmlファイルに組み込んで使っていきます。
ちなみにthree.jsもCDNを使えますが今回は他のプラグインも使うので公式よりファイルをダウンロードしました。

CDNはこちらから出来ます。
http://cdnjs.com/
テキストボックスにthreeと打てば出ます。


まず,index.htmlファイルを作成します。

<!doctype html>
<html lang="ja">
<head>
	<meta charset="UTF-8">
	<title>ThreeJS</title>
</head>
<body>
	<script src="three.min.js"></script>
	<script>
		(function(){

		})();
	</script>
</body>
</html>



こんなかんじで先ほどのthree.min.jsも指定して書きます。


また今回は Controlsファイルにある、OrbitControl.jsというのも使いますので、
取り出しておきます。マウスで3D空間を操作できるために組み込むものです。

3DCGの構成について

まず構成について説明します。

Sceneというものを1つの枠組みがあり、その中に

Mesh(部品)
Geometry(形状)
Material(材質)
Light(光

があります。
でそれを映し出すためにCamera(カメラ)を使っていきます。


ではこの順番で、htmlファイルの枠組みを作っていきます。

基本構成を作る

先ほどの構成となる要素を作っていきます。

Sceneの追加

var scene = new THREE.Scene();



これでシーンが作られます。

Mesh(Geometry)とMaterialの追加

これは一緒に作っていきます。

var geometry = new THREE.CubeGeometry(50,50,50);
var material = new THREE.MeshBasicMaterial({color:"#FF0000"});



これでMeshとMaterialを追加されました。

Meshは今回はCubeGeometryという立方体のを使っていきます。
()の中にはそれぞれ、(幅、高さ、奥行き)が設定できます。

MaterialにはMeshBasicMaterialという普通のを使っていきます。
色は16進数で赤色に設定しました。色の指定方法はCSS方式で出来ます。


そしてこの下にもう少し追加します。

var cube = new THREE.Mesh(geometry, material);
cube.position.set(0,0,0);
scene.add(cube);



cubeというのを作成し、先ほど作ったgeometryとmaterialを追加します。
そして、cubeのポジションを原点に配置し、
一番最後にcubeをシーンに追加します。


Lightの追加

これはマテリアルの方で、BasicMaterialという自ら発光するマテリアルを使っているので、
特に設定する必要ないのですが、そのマテリアルを変更して調整してみましょう。

まずMaterialの変更をします。

//var material = new THREE.MeshBasicMaterial({ color: "#FF0000"});
var material = new THREE.MeshLambertMaterial({ color: "#FF0000"});



BasicMaterialをLambertMaterialというのに変更しました。

次にLightの設定です。

var light = new THREE.DirectionalLight("#ffffff", 1);
var ambient = new THREE.AmbientLight("#550000");
light.position.set(0, 100, 30);
scene.add(light);
scene.add(ambient);


まず、DirectionalLightという太陽光のライトを用意します。
()の中身は(色,光の強さ)です。#ffffffは白です。

また、もう一つ環境光のAmbientLightというのを配置します。こちらは色のみ指定します。
#550000というのは若干暗い赤色かな?
そして太陽光のほうのみポジションをセットしておきます。
そして二つをシーンに追加します。

Cameraの追加

次にカメラを追加していきます。

var camera = new THREE.PerspectiveCamera(45,width / height, 1, 1000);
camera.position.set(200,100,500);
camera.lookAt(cube.position);



カメラにはPerspectiveCameraという奥行きを見渡せる遠近法のカメラを使います。
()の中身は(視野角,画面サイズ,カメラの見える範囲最小値,最大値)をセットできます。
画面サイズは、変数で後でwが500、hが300に指定します。

最後にカメラの向きをcubeの場所に向けます。

renderingの設定

最後にレンダリングの設定をしていきます。

var renderer = new THREE.WebGLRenderer();
renderer.setSize(width, height);
renderer.setClearColor("#eeeeee", 1);
document.getElementById('stage').appendChild(renderer.domElement);
renderer.render(scene, camera);


レンダラーにはWebGLを使います。
そしてレンダラーのサイズ、色を指定します。
そして、getElementByIdよりhtmlのstageというdiv idを取得し、追加します。
最後にレンダラーにシーンとカメラを追加すれば描画できます。



うまくいけばこんなかんじです。

f:id:lo25131:20140401185823j:plain


helperの追加

座標の位置がわかりにくいと思いますので、軸を表示させてみます。

AxisHelperというのを使っていきます

var axis = new THREE.AxisHelper(1000);
axis.position.set(0,0,0);
scene.add(axis);


ヘルパーを用意し、ポジションを0,0,0にして、最後にシーンに追加すれば出来ます。

f:id:lo25131:20140401190857j:plain

Cubeが0,0,0の位置、そしてSphereという球体を100,100,100の位置に作成しました。


地面の追加

地面を追加していきます。
Unityとかで使ってる方はわかると思いますが、Planeというのを使っていきます。

var pgeometry = new THREE.PlaneGeometry(300,300);
var pmaterial = new THREE.MeshLambertMaterial({color:"#0096d6",side:THREE.DoubleSide});
var plane = new THREE.Mesh(pgeometry, pmaterial);
plane.position.set(0,0,0);
plane.rotation.x = 90 * Math.PI / 180;
scene.add(plane);



PlaneGeometryというのを使っていきます。()は幅と高さしかないのでこれでいいです。
また、マテリアルにはside:THREE.DoubleSideというのを追加します。これはPlaneには片方しか面が無いので
両サイド追加するということです。
そしてplane.rotationでプレーンの向きを初期設定では縦になっているので横にします。

そうするとこんなかんじになります。

f:id:lo25131:20140401195616j:plain

影の追加

まずrenderingの方に影を追加するためのプログラムを書きます。

renderer.shadowMapEnabled = true;



これで影が使えるようになります。

これをそれぞれに追加していきます。

// cube
cube.castShadow = true;

//plane
plane.receiveShadow = true;

//light
light.castShadow = true;



castは影を出す方、receiveは影を反映する方です。



アニメーションを実装

renderingのところにアニメーションをつける関数を書いていきます。

function render() {
	  requestAnimationFrame(render);
	  cube.rotation.x += 1 * Math.PI / 180;
          renderer.render(scene, camera);
}
render();



こういうのを書いていきます。
まずアニメーションを使うよという文を定義します。
次に変化させる内容を書いていきます。
ここではcubeをx方向に一度ずつ回転させるという処理をさせています。

こんなかんじです。


f:id:lo25131:20140401200953g:plain

また、これを更に複雑にしたものがこちらです。

function render() {
	requestAnimationFrame(render);
	cube.rotation.x += 1 * Math.PI / 180;
        cube.rotation.y += 1 * Math.PI / 180;
	cube.rotation.z += 1 * Math.PI / 180;
	cube.position.x = Math.sin(new Date().getTime() /200) * 100;
	cube.position.z = Math.cos(new Date().getTime() /200) * 100;
	renderer.render(scene, camera);
	}
	render();


f:id:lo25131:20140401201215g:plain


マウスによる操作を行う

ここで一番最初に取っておいたOrbitControlsというのを使います。
これはマウスで3D空間を見渡したり出来るものです。
角度を変えたり、ズームをしたり出来るようになります。
まずはこれのスクリプトを反映させます。

//スクリプト
<script src="OrbitControls.js"></script>

//controlの追加 renderingの中
var controls = new THREE.OrbitControls(camera,renderer.domElement);

//function render()の中
controls.update();



こんな感じで追加していきます。

f:id:lo25131:20140401202211g:plain

応用して作ってみる


これらを応用してブロックをプラグラムで生成してぐるぐる回ってるやつを作りました。

<!doctype html>
<html lang="ja">
<head>
	<meta charset="UTF-8">
	<title>ThreeJS</title>
</head>
<body>
	<div id="stage"></div>
	<script src="three.min.js"></script>
	<script src="OrbitControls.js"></script>
	<script>
		(function(){

			var width = 800,
			   height = 500,

			   cube = [],
			   count = 500,
			   i,
			   cubeSize;

			//Scene
			var scene = new THREE.Scene();

			//mesh

			//cube
			for (i = 0; i< count; i++){
			   cubeSize = r(50);
			   var geometry = new THREE.CubeGeometry(cubeSize,cubeSize,cubeSize);
			   var material = new THREE.MeshLambertMaterial({ color:
                                        'rgb('+r(255)+','+r(255)+','+r(255)+')' });
		           cube[i] = new THREE.Mesh(geometry,material);
		           cube[i].castShadow = true;
		           cube[i].position.set(0,50+r(200),0);
			   scene.add(cube[i]);
			}


			//plane
			var pgeometry = new THREE.PlaneGeometry(500,500);
			var pmaterial = new THREE.MeshLambertMaterial({ color: "lightgreen",
                                       side:THREE.DoubleSide });
			var plane = new THREE.Mesh(pgeometry,pmaterial);
			plane.receiveShadow = true;
			plane.position.set(0,0,0);
			plane.rotation.x = 90 * Math.PI / 180;
			scene.add(plane);

			//light
			var light = new THREE.DirectionalLight("white",1);
			light.position.set(0,100,30);
			light.castShadow = true;
			scene.add(light);
			var ambient = new THREE.AmbientLight(0x550000);
			scene.add(ambient);


			//camera
			var camera = new THREE.PerspectiveCamera(45, width / height, 1, 2000);
			camera.position.set(200,300,500);


			// helper
			var axis = new THREE.AxisHelper(1000);
			axis.position.set(0,0,0);
			scene.add(axis);


			//renderring
			var renderer = new THREE.WebGLRenderer();
			renderer.setSize(width,height);
			renderer.setClearColor("lightgray", 1);
			renderer.shadowMapEnabled = true;
			document.getElementById('stage').appendChild(renderer.domElement);

			// control
			var controls = new THREE.OrbitControls(camera,renderer.domElement);

			function render() {
				requestAnimationFrame(render);
				for (i = 0; i< count; i++){

					cube[i].rotation.x += i * Math.PI / 180;
					cube[i].rotation.y += i * Math.PI / 180;
					cube[i].rotation.z += i * Math.PI / 180;
					cube[i].position.x = 
                                                 Math.sin(new Date().getTime() / (200+i)) * 100;
					cube[i].position.z = 
                                                 Math.cos(new Date().getTime() / (200+i)) * 100;
				}
				renderer.render(scene,camera);
				controls.update();
			}
			render();

			function r(n) {
				return Math.floor(Math.random() * (n + 1));
			}
		})();
	</script>
</body>
</html>



完成品

f:id:lo25131:20140401205439g:plain