クリスマスカードを作って祝いたい、three.js使いたい。

みなさまいかがお過ごしでしょうか。
あっという間に今日は12月25日、つまりクリスマス
つまりアドベントカレンダー最終日です。
早かったような、遅かったような、そんな１年がもうすぐ終わろうとしています。
僕にとって入社して初めてのクリスマス＆年越しです。
そんな１年の最後は友達と呑んだくれて寝正月ならぬ飲正月を過ごそうと着々と計画をしております。
もちろんコードもたくさん書く予定です。

さて、僕がこのアドベントカレンダー最終日を選んだ理由、それは
「クリスマスが１年で一番好きな日だから」
です。
誕生日とかよりも全然好きです。
なのでしっかり祝いたいのです。

#どうやって祝うか
さてどうやって祝いましょう。
クリスマス感も欲しいけど、なんかJSが書きたいな、なんとなく。

##そうだ、クリスマスカードを書こう
ということで個人的に、クリスマスといえばクリスマスカードなのでクリスマスカードを書くことにしました。
でも書くだけなら雑貨店とかで買ってきて、メッセージ書いて、
と面白みがありません。
なので、クリスマスカードを　”書く”　ことにしました。
そして、自分の好きな飛び出す絵本的な要素を詰め込んで書こう。

飛び出す　→　立体　→　JS　→　three.js

というわけでthree.jsで3Dでクリスマスカードを作りました。



three.js関連の情報はたくさんあるので、調べればたくさんサンプルも出てきます。
なので、今回は大まかな流れでご紹介できたらと思います。

#カードを作る
ベースとなるカード部分を作ります。
three.jsで作成しました。

###three.jsとは
wikipedia曰く、
>three.jsは、ウェブブラウザ上でリアルタイムレンダリングによる3次元コンピュータグラフィックスを描画する、クロスブラウザ対応の軽量なJavaScriptライブラリ及びアプリケーションプログラミングインタフェースである。

ということです。
簡単にいえば、比較的手軽に3Dグラフィックを作成し、扱うことのできるJavaScriptのライブラリです。

[公式はこちら](https://threejs.org/ "three.js")
![three-ss.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/404414/183d432a-12ee-4a4b-49e8-8474c236646f.jpeg)



webサイトをリッチにするエフェクトやゲームなどにもよく使用されています。
たくさんサンプルも用意されているので、割と気軽に始められますね！
早速触ってみましょう。

##下準備
ますはthree.jsをダウンロードしてきます。

![Screen Shot 2019-12-25 at 12.12.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/404414/189440c2-4228-34ac-28ee-97ed4baf1d3c.png)

様々なファイルが入っていますが、
three.js/build/　内にメインのJSファイルがあります。
今回はモジュールで使う方法をとったので、

three.module.js

を使用しました。


##書いていく
怒涛に書いていきます。
three.jsには描画するにはお決まりの流れがあります。

まずはブラウザで描画するための土台を準備します。

```html:christmas.html
<!DOCTYPE html>
<html lang="ja">
	<head>
		<title>three.js</title>
		<meta charset="utf-8">
		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
		<link type="text/css" rel="stylesheet" href="main.css">
		<style>
			body {
				margin-top: 200px;
			}
			div {
				text-align: center;
			}
		</style>
	</head>
	<body>
		<div id="container"></div>
	</body>
</html>
```

そして、基本部分をどんどん書いていきます。
まずはこんな感じです。

```javascript
<script type="module">
			import * as THREE from './three.js/build/three.module.js';
			init();
			animate();

                        var scene, camera, renderer;

			function init() {

	        scene = new THREE.Scene();

	        camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);

	        renderer = new THREE.WebGLRenderer( { antialias: true } );
```

function内にあるこの３つが基本です。
最初はこれらの理解がはてなでした。
ですが特に、threeでは欠かせないものです。

###scene
```javascript
scene = new THREE.Scene();
```

sceneはthreeで生成した物体を表示させるための土台を作る感覚です。


###camera

```javascript
camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);
```
threeでは用意したカメラに映った部分が描画されて表示されるという感じです。
そのカメラを作っているのがこの部分です。
プロパティは、

1. 画角　
2. アスペクト比　
3. ニアクリッピング　
4. ファークリッピング 

ニアクリッピングは、その値より手前はレンダーされず、
対照的にファークリッピングは、その値より遠いとレンダーされなくなります。

###renderer
```javascript
renderer = new THREE.WebGLRenderer();
```
WebGLRendererはGPUで処理するため、グラフィックスにはもってこいですね！
古いブラウザなどは使えない場合がありますが俄然Chrome派の僕は被害にあったことはありません。
が、公式によれば、
>Google Chrome 9+, Firefox 4+, Opera 15+, Safari 5.1+, Internet Explorer 11 and Microsoft Edge.

というサポート状況のようです。
基本最新バージョンなら大丈夫そうですね。

色々やるとこんなカード土台ができました。
![Screen Shot 2019-12-25 at 18.32.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/404414/45774d0a-bb8c-96c6-03c5-35af6d07ba2e.png)

コードはこのようになっています。

```javascript
renderer.setClearColor(new THREE.Color(0x000000, 0));
					renderer.setPixelRatio( window.devicePixelRatio );
	        renderer.setSize(window.innerWidth / 1.5 , window.innerHeight / 1.5);

	        planeGeometry = new THREE.PlaneGeometry(70, 50, 0);
	        planeMaterial = new THREE.MeshBasicMaterial({color: 0xffffff});
	        plane = new THREE.Mesh(planeGeometry, planeMaterial);

	        plane.rotation.x = -0.5 * Math.PI;
	        plane.position.x = 0;
	        plane.position.y = 0;
	        plane.position.z = 0;

	        scene.add(plane);
```

ここにツリーを乗せて色々やればいけそう！
さあ、ツリーを作ろう！

#ツリーを作る
シンプルなツリーを作成します。

```javascript
<script type="module">
			import * as THREE from './three.js/build/three.module.js';
			import { OrbitControls } from './three.js/examples/jsm/controls/OrbitControls.js';
			import { GLTFLoader } from './three.js/examples/jsm/loaders/GLTFLoader.js';
			init();
			animate();

			var scene, camera, renderer;
			var planeGeometry, planeMaterial, plane;
			var controls;
			var snowCube = [];

			function init() {


	        scene = new THREE.Scene();

	        camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);

	        renderer = new THREE.WebGLRenderer( { antialias: true } );

	        renderer.setClearColor(new THREE.Color(0x000000, 0));
					renderer.setPixelRatio( window.devicePixelRatio );
	        renderer.setSize(window.innerWidth / 1.5 , window.innerHeight / 1.5);

	        planeGeometry = new THREE.PlaneGeometry(70, 50, 0);
	        planeMaterial = new THREE.MeshBasicMaterial({color: 0xffffff});
	        plane = new THREE.Mesh(planeGeometry, planeMaterial);

	        plane.rotation.x = -0.5 * Math.PI;
	        plane.position.x = 0;
	        plane.position.y = 0;
	        plane.position.z = 0;

	        scene.add(plane);

					//木の幹
					var Cylinder = new THREE.Mesh(
     			new THREE.CylinderGeometry(2,2,4,50),
     			new THREE.MeshPhongMaterial({
               color: 0xE69138
      		}));
					Cylinder.position.x = 0;
					Cylinder.position.y = 2;
					Cylinder.position.z = 0;
					scene.add(Cylinder);

					//木の緑の部分
					var material = new THREE.MeshBasicMaterial( {color: 0x38761D} );
					var geometry1 = new THREE.BoxGeometry( 10, 2, 8 );
					var geometry2 = new THREE.BoxGeometry( 6, 2, 8 );
					var geometry3 = new THREE.BoxGeometry( 3, 2, 8 );
					var cube1 = new THREE.Mesh( geometry1, material );
					var cube2 = new THREE.Mesh( geometry2, material );
					var cube3 = new THREE.Mesh( geometry3, material );
					cube1.position.y = 5;
					cube2.position.y = 7;
					cube3.position.y = 9;
					scene.add( cube1,cube2,cube3 );
//light
					var directionalLight = new THREE.DirectionalLight( 0xffffff, 1 );
 					directionalLight.position.set( 1, 1, 0.5 ).normalize();
 					scene.add( directionalLight );

					//camera
	        camera.position.x = -30;
	        camera.position.y = 40;
	        camera.position.z = 30;
	        camera.lookAt(scene.position);

	        // html要素に出力
	        document.getElementById("container").appendChild(renderer.domElement);
					controls = new OrbitControls( camera, renderer.domElement );
					window.addEventListener( 'resize', onWindowResize, false );
	    }

			function onWindowResize() {
				camera.aspect = window.innerWidth / window.innerHeight;
				camera.updateProjectionMatrix();
				renderer.setSize( window.innerWidth, window.innerHeight );
			}

			function animate() {
				requestAnimationFrame( animate );

				render();
			}

			function render() {
				renderer.render( scene, camera );
			}
```



そうするとこのように表示されます。

<img width="954" alt="Screen Shot 2019-12-25 at 23.48.21.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/404414/0b8c9bb4-1eec-fe22-c6ea-2ee8d09c41fe.png">

超シンプルな木の完成。
今回はマイクラ的な世界をイメージしていたので、あえてシンプルに。
threeでは外部で作成したモデルをインポートして使用することもできます。
方法はまた今度紹介できればと思います。

クリスマスカードなので、文字を入れます。
下記を追加します。

```javascript
//テキスト
					var loader = new THREE.FontLoader();
					loader.load( 'three.js/examples/fonts/helvetiker_regular.typeface.json', function ( font ) {

						var text1 = new THREE.TextGeometry( 'Happy Holidays', {
							font: font,
							size: 4,
							height: 5,
							curveSegments: 12
						} );

						var text2 = new THREE.TextGeometry( 'from', {
							font: font,
							size: 2,
							height: 5,
							curveSegments: 12
						} );

						var text3 = new THREE.TextGeometry( 'PLACOLE', {
							font: font,
							size: 3,
							height: 5,
							curveSegments: 12
						} );

						var materials = [
							new THREE.MeshBasicMaterial( { color: 0xF1C232, overdraw: 0.1 } ),
							new THREE.MeshBasicMaterial( { color: 0x000000, overdraw: 0.1 } ),
							new THREE.MeshBasicMaterial( { color: 0x000000, overdraw: 0.1 } ),
						];
						var textMesh1 = new THREE.Mesh(text1, materials[0]);
						textMesh1.position.x = -20;
						textMesh1.position.y = 25;
						textMesh1.position.z = -13;
						textMesh1.rotation.x = -0.5 * Math.PI;

						var textMesh2 = new THREE.Mesh(text2, materials[1]);
						textMesh2.position.x = -20;
						textMesh2.position.y = 25;
						textMesh2.position.z = 10;
						textMesh2.rotation.x = -0.5 * Math.PI;

						var textMesh3 = new THREE.Mesh(text3, materials[2]);
						textMesh3.position.x = -10;
						textMesh3.position.y = 25;
						textMesh3.position.z = 15;
						textMesh3.rotation.x = -0.5 * Math.PI;

						scene.add(textMesh1,textMesh2,textMesh3);
					} );
```

ここでは、TextGeometryを使って、フォントを読み込み、
マテリアルで色をつけ、表示させています。
そうすると、このように表示されます。

<img width="985" alt="Screen Shot 2019-12-25 at 23.50.14.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/404414/19ca8b3f-a449-6be8-5f8d-ecd3921b61eb.png">

あ、冬だし雪もつけよう。

```javascript
//雪
					var count = 1000;
					var snowCube = [];
					var cubeGeometry = new THREE.BoxGeometry(2, 2, 2);
					var cubeMaterial = new THREE.MeshBasicMaterial({color: 0xffffff});

					for (var i = 0; i < count; i++) {
		        var cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
		        cube.position.x = Math.random() * 2500 - 1000;;
		        cube.position.y = Math.random() * 2500 - 1000;;
		        cube.position.z = Math.random() * 2500 - 1000;;
		        scene.add(cube);
					}
```
BoxGeometryでサイズなどを指定し、ランダムな位置に表示させています。
すると、このように周り白く点々と表示されます。
<img width="1023" alt="Screen Shot 2019-12-25 at 23.53.23.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/404414/4da1eea7-f98d-7c9d-78f9-14fdf75e9920.png">

もっと色々やりたかったのですが、
勉強不足でやりきる時間がないのと、目が痛いのと、酔いそうなので、
今回は中途半端ですがここまでです。。

[![Image from Gyazo](https://i.gyazo.com/821d5a7ad4a2863e0c6e7ba174370917.gif)](https://gyazo.com/821d5a7ad4a2863e0c6e7ba174370917)

#最後に
特にすごいものを作ったわけではないのですが、
今年は学びが多かったので、その感謝の気持ちを込めて。
もっとリッチなものを作れなかったのは怠けすぎたというので、反省です。

##改めて
学ぶべきことはたくさんあるし、
楽しいものがもっと作れる！という可能性を再確認できました。
まだまだ勉強中！来年も頑張ります。

読んでくださったかた、ありがとうございました。
良いお年をお過ごしください。

再度、読んでくださりありがとうございました。
皆様に健康で、素晴らしい一年が待っていますことを。