EmissiveMapを使ってマイクラのエンチャントを再現する

#EmissiveMapとは
テクスチャマップを部分的に光っているようにみせることができるマッピング画像で、Three.jsでは [MeshPhongMaterial](https://threejs.org/docs/#api/en/materials/MeshPhongMaterial)や、[MeshLambertMaterial](https://threejs.org/docs/#api/en/materials/MeshLambertMaterial)などのoptionで指定することができます。
AlphaMapと同じグレイスケールの画像で、白い部分はemissiveプロパティに指定した色で光って見えるようになり、黒い部分は元のまま表示されます。

EmissiveMapは、テクスチャに部分的に色を乗せるイメージではなく、半透明レイヤーのように独立しているイメージです。
また[MeshBasicMaterial](https://threejs.org/docs/#api/en/materials/MeshBasicMaterial)のようにライティングの影響を受けないため、暗いライティングでも光って見えます。

#指定方法

```javascript
	// マテリアルの作成
	let material = new THREE.MeshLambertMaterial({
		map: texture,
		emissiveMap: emissiveTexture, /// グレイスケールのTHREE.Textureオブジェクト
		emissive: "#FF0000", /// 光らせたい色
	})
```

ざんねんながら、複数の色は指定できません。

```javascript
	// マテリアルの作成
	let material = new THREE.MeshLambertMaterial({
		map: texture,
		emissiveMap: [emissiveTexture1, emissiveTexture2], 
		emissive: ["#FF0000", "#0000FF"]
	})
        //////// 何も起きない ////////
```

<p class="codepen" data-height="440" data-theme-id="0" data-default-tab="html,result" data-user="pneuma01" data-slug-hash="QeQJzE" style="height: 440px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="Minecraft Enchanted item">
  <span>See the Pen <a href="https://codepen.io/pneuma01/pen/QeQJzE/">
  Minecraft Enchanted item</a> by Urushibara (<a href="https://codepen.io/pneuma01">@pneuma01</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>

#解説のようなもの

##モデルの初期化
ゆらめく様にアニメーションさせたいので、あらかじめアニメーション分のEmissiveMapを作成しておきます。

```javascript

	// Sceneの初期化
	const scene = new THREE.Scene();

	// Geometryの作成
	let img = document.querySelector("#item");
	let canvas = createCanvas(img.width, img.height);
	canvas.context.drawImage(img, 0, 0);
	let geometry = new THREE.PixelArtGeometry(canvas.canvas, 1);
	canvas.canvas.remove();
	
	// 基本テクスチャの作成
	let texture = new THREE.TextureLoader().load(img.src);
	texture.magFilter = THREE.NearestFilter;
	texture.minFilter = THREE.NearestFilter;
	texture.type = THREE.FloatType;
	
	// アニメーション Emissive Map の作成
	let emissiveMaps = [];
	for(let i=0; i<56; i++){
		let canvas = drawAnimated(i);
		let texture = new THREE.CanvasTexture(canvas);
		texture.magFilter = THREE.NearestFilter;
		texture.minFilter = THREE.NearestFilter;
		texture.type = THREE.FloatType;
		emissiveMaps.push(texture);
	}

	// マテリアルの作成
	let material = new THREE.MeshLambertMaterial({
		map: texture,
		emissiveMap: emissiveMaps[0],
		emissive: "#7F007F", /// Emissive Color
		transparent: false,
		side: THREE.DoubleSide,
		alphaTest: 0.99 
	})

	// Mesh の作成
	let box = new THREE.Mesh(
		geometry, 
		material
	);
	box.scale.set(10, 10, 10);
	main_object = box;

	// Sceneへ追加
	scene.add(main_object);
```

##アニメーション
描画tick呼び出し時に、emissiveMapを次々と入れ替えます。
CanvasTextureを使ってリアルタイムでEmissiveMapを描画するのも試してみましたが、かなり重たかったのでやめました。

```javascript

		//// Tick ////
		function tick() {

			// オブジェクトの位置
			pos_cycle = pos_cycle < 360 ? pos_cycle + 1 : 0;
			var y = Math.sin(deg2rad(pos_cycle * 2)) * 20;
			main_object.position.set(0, -y, 0);
			main_object.rotation.set(0, deg2rad(pos_cycle * 1), 0);
			
			// Emissive Map の切り替え
			main_object.material.emissiveMap = emissiveMaps[Math.floor(glint_cycle)];
			glint_cycle = glint_cycle < emissiveMaps.length ? glint_cycle + 0.1 : 0;

			// 描画
			renderer.render(scene, camera);
			requestAnimationFrame(tick);
		}
```