【メモ】Three.jsでObjectのTextureを切り替える

# きっかけ
WebXRの仕事でTexture切り替えをすることになった。
大層なものは作っていないが、動くものをメモで残しておきたい。
本番はVue.jsでやるものの、まずはJavaScriptでやる方法から残しておく。
※ [Vue.js版も投稿しました](https://qiita.com/yunayunanaka/items/42db508c5a1b8ceca06f)

# やりたいこと
CubeのTextureを

![circle.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431932/d4bad88b-5487-e4f9-fd09-82c9cbf1caeb.png)
から
![gori128.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431932/0aa27f99-fab0-22ca-8240-ebea1152b90a.png)
に切り替える

# ソースコード

まずはCubeを表示させて、Textureを張る。
SelectBoxでTextureのイメージを切り替える。

```html:changeTexture.html
<!DOCTYPE html>
<html>

<head>
	<meta charset=utf-8>
	<title>change texture</title>
	<select onchange="onChange(this.value)">
		<option value="circle.png">Circle</option>
		<option value="gori128.png">Gori</option>
	</select>
</head>

<body>
	<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/85/three.min.js"></script>
	<script>
		// scene
		const scene = new THREE.Scene();

		// renderer
		const renderer = new THREE.WebGLRenderer({ alpha: true, antialias: true }); // force background transparent
		renderer.setSize(window.innerWidth, window.innerHeight);
		document.body.appendChild(renderer.domElement);

		// camera
		const camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 1000);
		camera.position.set(0, 0, 10);

		// light
		const light = new THREE.AmbientLight(0xffffff);
		scene.add(light);

		// cube
		const cubeGeometry = new THREE.BoxGeometry(1, 1, 1);
		const cubeMaterial = new THREE.MeshBasicMaterial({
			map: new THREE.TextureLoader().load("circle.png")
		});
		const cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
		cube.name = "cube" // need name to find object afterwards

		scene.add(cube);
		render();

		function render() {
			cube.rotation.x += 0.1;
			cube.rotation.y += 0.1;
			requestAnimationFrame(render);
			renderer.render(scene, camera);
		}

		function onChange(val) {
			const object = scene.getObjectByName("cube"); // find obj by name
			object.material.map = new THREE.TextureLoader().load(val);
			object.material.needsUpdate = true; // necessary
		}
	</script>
</body>

</html>
```


# プロジェクトの起動

[http-server](https://www.npmjs.com/package/http-server)を使う。
Three.jsのテクスチャ関連は、Http起動してあげないと読み込まない。

```
$npm install --global http-server
$http-server . -p 8000
```

[ブラウザにアクセス](http://localhost:8000/changeTexture.html)

# 結果


1. ○が表示される
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431932/e07658cc-6e03-6abc-cb48-ffa3994b7342.png)

2. テクスチャ切り替え
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431932/bf1ed0eb-13d9-627f-bfa7-cb13c286cd04.png)

3. Goriちゃんに切り替わった
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431932/916c18b7-a5a2-afd7-69f4-35b1c16705c7.png)

# メモ

Cubeオブジェクトに名前を付けておいて、後からCubeを検索してMaterialを更新する。
`needsUpdate = true;` がないと、Materialは更新されない。

テクスチャの貼り方はググると色々やり方がありそうなものの、thee.js公式ホームページによると、[THREE.TextureLoader](https://threejs.org/docs/#api/en/loaders/TextureLoader)が一番一般的なやり方のよう。
Cubeの6面に違うイメージを張る場合には[THREE.CubeTextureLoader](https://threejs.org/docs/#api/en/textures/CubeTexture)が良い。
SkyBoxみたいなのに役立つ。↓昔作ったSkyBoxのメモ。いつかまたキレイにしたい。

```javascript:skybox.js
class Skybox {
    constructor(urls) {
        var texCube = new THREE.CubeTextureLoader().load(urls);
        texCube.format = THREE.RGBFormat;
        texCube.mapping = THREE.CubeReflectionMapping;

        // skybox用のマテリアルを生成
        var cubeShader = THREE.ShaderLib["cube"];
        var cubeMat = new THREE.ShaderMaterial({
            fragmentShader: cubeShader.fragmentShader,
            vertexShader: cubeShader.vertexShader,
            uniforms: cubeShader.uniforms,
            depthWrite: false,
            side: THREE.BackSide,
        });

        cubeMat.uniforms["tCube"].value = texCube;

        // Skybox用ジオメトリ生成
        var d = 10000;
        var cubeGeo = new THREE.BoxGeometry(d, d, d);
        this.cubeMesh = new THREE.Mesh(cubeGeo, cubeMat);
        return this.cubeMesh
    }
}
```