Three.jsとReactThreeFiberの実装の違い（サンプル）

# サンプル
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1781492/37660bd0-07fd-b6c3-ddf8-abefff245670.png)
正方形をマウスドラッグで回転させることができるプログラム
## Three.js
Three.jsを用いると以下の書き方になります。

```
/** @jsxImportSource @emotion/react */
import * as React from 'react';
import * as THREE from "three";
import { OrbitControls } from "three-stdlib";
import { Vector3 } from "three";
import { css } from '@emotion/react';

const styles = {
  container: css({
    height: "90vmin",
    width: "100%",
    flexDirection: "column",
    alignItems: "center",
  }),
  canvas: css({
    width: "100%",
    height: "100%",
    touchAction: "auto",
  }),
};

const ThreejsCube = () => {
  const canvasRef = React.useRef<HTMLCanvasElement>(null);

  React.useEffect(() => {
    const canvas = canvasRef.current;
    if (canvas === null) {
      return;
    }

    const width = canvas.clientWidth;
    const height = canvas.clientHeight;

    const renderer = new THREE.WebGLRenderer({
      canvas: canvas
    });
    renderer.setPixelRatio(window.devicePixelRatio);
    renderer.setSize(width, height);
    renderer.setClearColor(0x000000);

    const scene: THREE.Scene = new THREE.Scene();

    const camera: THREE.PerspectiveCamera = new THREE.PerspectiveCamera(90, 1.0);
    camera.aspect = width / height;
    camera.position.set(0, 0, 10);
    camera.updateProjectionMatrix();

    let oribitControls = new OrbitControls(camera, canvas);
    oribitControls.target = new Vector3(0, 0, 0);
    oribitControls.minDistance = 2.0;
    oribitControls.maxDistance = 50.0;

    const geometry = new THREE.BoxGeometry( 2, 2, 2 );
    const material = new THREE.MeshBasicMaterial( {color: 0xff0000} );
    const cube = new THREE.Mesh( geometry, material );
    scene.add( cube );

    const glRender = () => {        
      oribitControls.update();
      renderer.render(scene, camera);
      requestAnimationFrame(glRender);
    };

    glRender();
  }, []);

  return (
    <div css={styles.container}>
      <canvas css={styles.canvas} ref={canvasRef} />
    </div>
  );
};

export default ThreejsCube;
```
内容を細かくみていきます。
まず、以下の部分でWebgl用のレンダラを作成します。
```
const renderer = new THREE.WebGLRenderer({
  canvas: canvas
});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(width, height);
renderer.setClearColor(0x000000);
```
シーンを作成します。
```
const scene: THREE.Scene = new THREE.Scene();
```
次にカメラを作成します。
```
const camera: THREE.PerspectiveCamera = new THREE.PerspectiveCamera(90, 1.0);
camera.aspect = width / height;
camera.position.set(0, 0, 10);
camera.updateProjectionMatrix();
```
カメラを制御するコントローラを作成します。
```
let oribitControls = new OrbitControls(camera, canvas);
oribitControls.target = new Vector3(0, 0, 0);
oribitControls.minDistance = 2.0;
oribitControls.maxDistance = 50.0;
```
Cubeオブジェクトを追加します。
```
const geometry = new THREE.BoxGeometry( 2, 2, 2 );
const material = new THREE.MeshBasicMaterial( {color: 0xff0000} );
const cube = new THREE.Mesh( geometry, material );
scene.add( cube );
```
最後に描画を行います。
```
const glRender = () => {        
    oribitControls.update();
    renderer.render(scene, camera);
    requestAnimationFrame(glRender);
};

glRender();
```
## ReactThreeFiber
ReactThreeFiberを用いると以下の実装になります。
```
/** @jsxImportSource @emotion/react */
import React from "react";
import * as Fiber from '@react-three/fiber';
import * as Drei from '@react-three/drei';
import { css } from '@emotion/react';

const styles = {
  container: css({
    height: "90vmin",
    width: "100%",
    flexDirection: "column",
    alignItems: "center",
  }),
  canvas: css({
    width: "100%",
    height: "100%",
    touchAction: "auto",
  }),
};

const R3FCube = () => {
  return (
    <div css={styles.container}>
      <Fiber.Canvas css={styles.canvas} onCreated={state => state.gl.setClearColor("black")} >
        <Drei.PerspectiveCamera makeDefault fov={90} position={[0, 0, 10]}/>
        <Drei.Box args={[2, 2, 2]}>
          <meshBasicMaterial color="red" />
        </Drei.Box>
        <Drei.OrbitControls makeDefault minDistance={2.0} maxDistance={50.0}/>
      </Fiber.Canvas>
    </div>
  );
};

export default R3FCube;
```
内容を細かく見ていきます。
以下の部分でCanvasを作成しています。
```
<Fiber.Canvas css={styles.canvas} onCreated={state => state.gl.setClearColor("black")} >
```
カメラを作成します。
```
<Drei.PerspectiveCamera makeDefault fov={90} position={[0, 0, 10]}/>
```
Cubeオブジェクトを追加します。
```
<Drei.Box args={[2, 2, 2]}>
    <meshBasicMaterial color="red" />
</Drei.Box>
```
カメラを制御するコントローラを追加します。
```
<Drei.OrbitControls makeDefault minDistance={2.0} maxDistance={50.0}/>
```
# Three.jsとReactThreeFiberの実装の違い
サンプルプログラムを比較し、ReactThreeFiberではシーン設定やカメラ設定などをライブラリの内部でやってくれていることが分かります。そのため、Threejsで実装するよりコード量が少なくなります。ReactThreeFiberはReactコンポーネントのようにThree.jsが書けるライブラリです。Reactの宣言的な記述で、WebGLの3Dモデル作成が簡単実装できる点が特徴だと思います。
# 参考
https://threejs.org/

https://docs.pmnd.rs/react-three-fiber/getting-started/introduction