Next.jsのアプリでThree.jsの3Dモデルを使用

@TK-Cさんの"[Next.jsにThree.jsを導入して表示させるまでの手順【TypeScript】](https://qiita.com/TK-C/items/1a7ce969333284f57079)"には、Next.jsアプリにThree.jsを導入する手順が書いてある。本記事は、Three.jsの３DモデルをNext.jsのサイトに描写する方法を解説していきます。

# 3Dモデルを表示するコンポーネント
最初に、3Dモデルを表示するコンポーネントを作成します。アプリのルートに`components`フォルダーを作ります。3Dモデルを表示するコンポーネントは`components/tokyo-model.tsx`である。


今回は、[Glen Fox](https://www.artstation.com/glenatron)の[Littlest Tokyo](https://www.artstation.com/artwork/1AGwX)モデルを使用します。

```js:components/tokyo-model.tsx
import { useEffect } from 'react'
import * as THREE from 'three'
import Stats from 'three/addons/libs/stats.module.js';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
import { RoomEnvironment } from 'three/addons/environments/RoomEnvironment.js';
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js';

import path from 'path'

const TokyoModel = () => {
  useEffect(() => {
	let mixer;

	const clock = new THREE.Clock();
	const container = document.getElementById( 'container' );

	const stats = new Stats();
	container.appendChild( stats.dom );

	const renderer = new THREE.WebGLRenderer( { antialias: true } );
	renderer.setPixelRatio( window.devicePixelRatio );
	renderer.setSize( window.innerWidth, window.innerHeight );
	renderer.outputEncoding = THREE.sRGBEncoding;
	container.appendChild( renderer.domElement );

	const pmremGenerator = new THREE.PMREMGenerator( renderer );

	const scene = new THREE.Scene();
	scene.background = new THREE.Color( 0xbfe3dd );
	scene.environment = pmremGenerator.fromScene( new RoomEnvironment(), 0.04 ).texture;

	const camera = new THREE.PerspectiveCamera( 40, window.innerWidth / window.innerHeight, 1, 100 );
	camera.position.set( 5, 2, 8 );

	const controls = new OrbitControls( camera, renderer.domElement );
	controls.target.set( 0, 0.5, 0 );
	controls.update();
	controls.enablePan = false;
	controls.enableDamping = true;

	const dracoLoader = new DRACOLoader();
	dracoLoader.setDecoderPath( 'jsm/libs/draco/gltf/' );

	const loader = new GLTFLoader();
	loader.setDRACOLoader( dracoLoader );
	loader.load('models/gltf/LittlestTokyo.glb', function ( gltf ) {

		const model = gltf.scene;
		model.position.set( 1, 1, 0 );
		model.scale.set( 0.01, 0.01, 0.01 );
		scene.add( model );

		mixer = new THREE.AnimationMixer( model );
		mixer.clipAction( gltf.animations[ 0 ] ).play();

		animate();

	}, undefined, function ( error ) {
		console.error( error );

	} );


	window.onresize = function () {

		camera.aspect = window.innerWidth / window.innerHeight;
		camera.updateProjectionMatrix();

		renderer.setSize( window.innerWidth, window.innerHeight );

	};


	function animate() {

		requestAnimationFrame( animate );

		const delta = clock.getDelta();

		mixer.update( delta );

		controls.update();

		stats.update();

		renderer.render( scene, camera );

	}

  })
  return (
    <div id='container'></div>
  )
}

export default TokyoModel

```

# アプリにコンポーネントを追加

以上の作成したコンポーネントを`index.tsx`に入れます。

```js:pages/index.tsx
import type { NextPage } from 'next'
import TokyoModel from '../components/tokyo-model'

const Home: NextPage = () => {

  return (
   <>
      <TokyoModel></TokyoModel>
    </>
  )
}

export default Home
```

# 結果
3Dモデルは以下のように表示される。
![ezgif.com-gif-maker.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2921051/93272a7b-0772-876f-49e5-5521db79323c.gif)

本記事に使用したソースコードは以下のgit repositoryに保存しますので、お気軽にご参照ください。

https://github.com/btt-17/nextjs-threejs