コピペで完成！Three.js入門 1

##はじめに
「初めてのThree.js入門」　1章の内容をコピペで再現できるようにまとめました。
対応する番号にコードをコピペ、
ブラウザにドラッグ＆ドロップしてリロードしていくことで
Three.js基本の流れを追うことができます。

![picture978-4-87311-770-6.gif](https://qiita-image-store.s3.amazonaws.com/0/154186/c074c615-885c-24db-8d88-7f52af332f57.gif)


[初めてのThree.js 第2版
――WebGLのためのJavaScript 3Dライブラリ](https://www.oreilly.co.jp/books/9784873117706/)

github  [learning-three-js-2e-ja-support](https://github.com/oreilly-japan/learning-three-js-2e-ja-support)

## 用意するもの

空のファイル

```html

<!DOCTYPE html>
<!-- サーバーを立てる -->
<!-- python -m SimpleHTTPServer -->
<html>
  <head>
    <title>three.js</title>
    <!-- Three.js -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/84/three.js" ></script>
    <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/stats.js/r16/Stats.js"></script>
    <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.6.5/dat.gui.js"></script>
    <style>
      body {
        /* set margin to 0 and overflow to hidden, to go fullscreen */
        /* スクロールバー除去 */
        margin: 0;
        overflow: hidden;
      }
    </style>
  </head>
  <body>

    <div id="Stats-output">
    </div>
    <!-- Div which will hold the Output -->
    <div id="WebGL-output">
    </div>

    <!-- Javascript code that runs our Three.js examples -->
    <script type="text/javascript">

      //9-2 camera scene rendererの変数定義をinit()関数の外に移動して別の関数からもアクセスできるようにする

      function init() {

        // 1-1 scene オブジェクトを作成する

        // 1−2 camera オブジェクト作成する

        // 1−3 renderer オブジェクトを作成する

        
        // 6-1 レンラダラーに影の利用を伝える

        
        // 2-1 平面を作成する

        // 2-2 平面を傾け、位置を決める

        // 6-2 影の利用

        // 2-3 平面を追加する



        // 5-1 cubeの作成

        // 6-3 影の利用

        // 5-2 cubeの位置
        
        // 5-3 cubeの追加


        // 5−4 sphereの作成

        // 6−4 影の利用

        // 5-5 sphereの位置

        // 5-6 sphereの追加



        // 3-1 カメラの位置 見え方を設定する


        // 4 光源を追加する

        // 6−5 spotLightを影に対応させる



        // 3-2 jsのappendChild関数でdiv要素に追加
        //7-1 3−2を消す


        // 3-3 rendererにsecne cameraの描画を指示する

        
        // 8-0 変数stepの定義

        // 7−2 3−3を消して requestAnimationFrame関数を導入する

        //7-3 reder関数を呼び出す
      }
      //9-1 ブラウザサイズが変更されたら出力を自動的にリサイズする

      window.onload = init;

    </script>
  </body>
</html>
```

サーバーを立てる
`python -m SimpleHTTPServer`
デスクトップに空のファイルを作成した場合、ターミナルでデスクトップに移動して上記コマンドを入力　enter

## 1 scene camera rendererを定義する


```js
        // 1-1 scene オブジェクトを作成する
        scene = new THREE.Scene();

        // 1−2 camera オブジェクト作成する
        camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);

        // 1−3 renderer オブジェクトを作成する
        renderer = new THREE.WebGLRenderer();
        renderer.setClearColor(new THREE.Color(0xEEEEEE));
        renderer.setSize(window.innerWidth, window.innerHeight);
```

- scene オブジェクト  
表示したいすべての物体光源を保持して変更を監視するコンテナオブジェクト
- camera オブジェクト  
シーンを描画する時に何が見えるか決定する
- renderer オブジェクト  
cameraオブジェクトの角度に基づいてブラウザ内でsceneオブジェクトがどのように見えるのか計算する ここではTHREE.WebGLRenderer()をつかって描画　　 setClearColor関数を使って色を設定 setSize関数で描画するシーンの大きさをrendererに通知  
**※まだ何も表示されません。**

## 2 座標と平面を追加する

```js
        // 2-1 平面を作成する
        var planeGeometry = new THREE.PlaneGeometry(60, 20);
        var planeMaterial = new THREE.MeshLambertMaterial({color: 0xffffff});
        var plane = new THREE.Mesh(planeGeometry, planeMaterial);

        // 2-2 平面を傾け、位置を決める
        plane.rotation.x = -0.5 * Math.PI;
        plane.position.x = 15;
        plane.position.y = 0;
        plane.position.z = 0;

        // 2-3 平面を追加する
        scene.add(plane);
```

平面を追加するにはどのような平面（幅、高さ）であるか、なに色であるか設定する必要がある(planeGeometry planeMaterialで定義) これら二つを組み合わせてTHREE.Meshでplaneというオブジェクトを作成 次に配置を決める rotation.xで平面を90度傾ける x y z の位置を決めてscene.add(plane)でシーンに追加する  
**※まだ何も表示されません。**


## 3 カメラの位置を設定する

```js
        // 3-1 カメラの位置 見え方を設定する
        camera.position.x = -30;
        camera.position.y = 40;
        camera.position.z = 30;
        camera.lookAt(scene.position);
```
```js
        // 3-2 jsのappendChild関数でdiv要素に追加
        document.getElementById("WebGL-output").appendChild(renderer.domElement);
```
```js
        // 3-3 rendererにsecne cameraの描画を指示する
        renderer.render(scene, camera);
```

position.x position.y position.z を使って位置を設定する
camera.lookAt(scene.position);でlookAt関数を使ってシーンの中心を向くように指定（デフォルトで(0,0,0)に位置）

あとはjsのappendChild関数でdiv要素に追加
最後にrendererに描画の指示をする。

※ここでやっと表示されます。
<img width="1037" alt="スクリーンショット 2017-12-24 21.24.42.png" src="https://qiita-image-store.s3.amazonaws.com/0/154186/19141809-4453-b44f-5cd8-4d4f6f1e7b24.png">


## 4 光源を追加する

```js
        // 4 光源を追加する
        var spotLight = new THREE.SpotLight(0xffffff);
        spotLight.position.set(-20, 30, -5);
        scene.add(spotLight);
``` 

THREE.SpotLightは自分の位置(spotLight.position.set(-20, 30, -5);)から光を照らします。
<img width="978" alt="スクリーンショット 2017-12-24 21.25.56.png" src="https://qiita-image-store.s3.amazonaws.com/0/154186/1a321da2-4bfd-ed5c-655d-0850fe68c35b.png">


## 5 マテリアルの追加

```js
        // 5-1 cubeの作成
        var cubeGeometry = new THREE.BoxGeometry(4, 4, 4);
        var cubeMaterial = new THREE.MeshLambertMaterial({color: 0xff0000});
        var cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
        // 5-2 cubeの位置
        cube.position.x = -4;
        cube.position.y = 3;
        cube.position.z = 0;
        // 5-3 cubeの追加
        scene.add(cube);

        // 5−4 sphereの作成
        var sphereGeometry = new THREE.SphereGeometry(4, 20, 20);
        var sphereMaterial = new THREE.MeshLambertMaterial({color: 0x7777ff});
        var sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
        // 5-5 sphereの位置
        sphere.position.x = 20;
        sphere.position.y = 0;
        sphere.position.z = 2;
        // 5-6 sphereの追加
        scene.add(sphere);
```
<img width="931" alt="スクリーンショット 2017-12-24 21.27.20.png" src="https://qiita-image-store.s3.amazonaws.com/0/154186/c21e723a-2c03-5e46-43e5-088fed7f35f0.png">


## 6 影の追加

```js
        // 6-1 レンラダラーに影の利用を伝える
        renderer.shadowMap.enabled = true;
```
```js
        // 6-2 影の利用
        plane.receiveShadow = true;
```
```js
        // 6-3 影の利用
        cube.castShadow = true;
```
```js
        // 6−4 影の利用
        sphere.castShadow = true;
```
```js
        // 6−5 spotLightを影に対応させる
        spotLight.castShadow = true;
```
<img width="949" alt="スクリーンショット 2017-12-24 21.29.47.png" src="https://qiita-image-store.s3.amazonaws.com/0/154186/f2c0e1e1-d4fa-33e6-f9ae-0910edccc9f4.png">



## 7 アニメーションのために拡張する
```js
        //7-1 3−2を消す
```
```js
        // 7−2 3−3を消して requestAnimationFrame関数を導入する

```

```js
        function render() {
          document.getElementById("WebGL-output").appendChild(renderer.domElement);
          
          // 8-1 cubeのアニメーション

          // 8-2 sphereのアニメーション

          requestAnimationFrame(render);
          renderer.render(scene, camera);
         }
         // 7-3 render関数を呼び出す
        render();

```

render関数内でrequestAnimationFrameを再び呼び出してアニメーションを実行され続けるようにしています。
シーンを完全に作成し終わった後で、renderer.renderを呼び出す代わりに一度render関数を呼び出してアニメーションを開始します。  
**※まだ何も動きません**

## 8 アニメーションの追加

```js
        // 8-0 変数stepの定義
        var step = 0;
```
```js
          // 8-1 cubeのアニメーション
          cube.rotation.x += 0.02;
          cube.rotation.y += 0.02;
          cube.rotation.z += 0.02;

          // 8-2 sphereのアニメーション
          step += 0.04;
          sphere.position.x = 20 + ( 10 * (Math.cos(step)));
          sphere.position.y = 2 + ( 10 * Math.abs(Math.sin(step)));
```
立方体
render関数が呼ばれるたびにrotationプロパティのそれぞれの軸の値を0.02ずつ増やすと、立方体がすべての軸に対してなめらかに回転する。

球体
Math.cos関数とMath.sin関数を使用すればstep変数を元に軌跡を作成。
step　+=　0.04 によって弾む球の速さを定義する。  
**※動きます**

<img width="953" alt="スクリーンショット 2017-12-24 21.36.06.png" src="https://qiita-image-store.s3.amazonaws.com/0/154186/0696b4d7-56b3-2a41-23b9-78c90da81464.png">


## 9 ブラウザサイズが変更されたら出力を自動的にリサイズする

```js
      //9-1 ブラウザサイズが変更されたら出力を自動的にリサイズする
      function onResize() {
        camera.aspect = window.innerWidth / window.innerHeight;
        camera.updateProjectionMatrix();
        renderer.setSize(window.innerWidth, window.innerHeight);
      }
      window.addEventListener('resize', onResize, false);
```

```js
      //9-2 camera scene rendererの変数定義をinit()関数の外に移動して別の関数からもアクセスできるようにする
      var camera;
      var scene;
      var renderer;
```

完成...!

### 完成コード

```html
<!DOCTYPE html>
<!-- サーバーを立てる -->
<!-- python -m SimpleHTTPServer -->
<html>

  <head>
    <title>three.js</title>
    <!-- Three.js -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/84/three.js" ></script>
    <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/stats.js/r16/Stats.js"></script>
    <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.6.5/dat.gui.js"></script>
    <style>
      body {
        /* set margin to 0 and overflow to hidden, to go fullscreen */
        /* スクロールバー除去 */
        margin: 0;
        overflow: hidden;
      }
    </style>
  </head>
  <body>

    <div id="Stats-output">
    </div>
    <!-- Div which will hold the Output -->
    <div id="WebGL-output">
    </div>

    <!-- Javascript code that runs our Three.js examples -->
    <script type="text/javascript">

      //9-2 camera scene rendererの変数定義をinit()関数の外に移動して別の関数からもアクセスできるようにする
      var camera;
      var scene;
      var renderer;

      function init() {

        // 1-1 scene オブジェクトを作成する
        scene = new THREE.Scene();
        // 1−2 camera オブジェクト作成する
        camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);
        // 1−3 renderer オブジェクトを作成する
        renderer = new THREE.WebGLRenderer();
        renderer.setClearColor(new THREE.Color(0xEEEEEE));
        renderer.setSize(window.innerWidth, window.innerHeight);
        
        // 6-1 レンラダラーに影の利用を伝える
        renderer.shadowMap.enabled = true;
        
        // 2-1 平面を作成する
        var planeGeometry = new THREE.PlaneGeometry(60, 20);
        var planeMaterial = new THREE.MeshLambertMaterial({color: 0xffffff});
        var plane = new THREE.Mesh(planeGeometry, planeMaterial);
        // 2-2 平面を傾け、位置を決める
        plane.rotation.x = -0.5 * Math.PI;
        plane.position.x = 15;
        plane.position.y = 0;
        plane.position.z = 0;
        // 6-2 影の利用
        plane.receiveShadow = true;
        // 2-3 平面を追加する
        scene.add(plane);


        // 5-1 cubeの作成
        var cubeGeometry = new THREE.BoxGeometry(4, 4, 4);
        var cubeMaterial = new THREE.MeshLambertMaterial({color: 0xff0000});
        var cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
        // 6-3 影の利用
        cube.castShadow = true;
        // 5-2 cubeの位置
        cube.position.x = -4;
        cube.position.y = 3;
        cube.position.z = 0;
        // 5-3 cubeの追加
        scene.add(cube);

        // 5−4 sphereの作成
        var sphereGeometry = new THREE.SphereGeometry(4, 20, 20);
        var sphereMaterial = new THREE.MeshLambertMaterial({color: 0x7777ff});
        var sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
        // 6−4 影の利用
        sphere.castShadow = true;
        // 5-5 sphereの位置
        sphere.position.x = 20;
        sphere.position.y = 0;
        sphere.position.z = 2;
        // 5-6 sphereの追加
        scene.add(sphere);


        // 3-1 カメラの位置 見え方を設定する
        camera.position.x = -30;
        camera.position.y = 40;
        camera.position.z = 30;
        camera.lookAt(scene.position);


        // 4 光源を追加する
        var spotLight = new THREE.SpotLight(0xffffff);
        spotLight.position.set(-20, 30, -5);
        // 6−5 spotLightを影に対応させる
        spotLight.castShadow = true;
        scene.add(spotLight);


        // 3-2 jsのappendChild関数でdiv要素に追加
        //7-1 3−2を消す

        // 3-3 rendererにsecne cameraの描画を指示する
        renderer.render(scene, camera);
        
        // 8-0 変数stepの定義
        var step = 0;

        // 7−2 3−3を消して requestAnimationFrame関数を導入する
        function render() {
          document.getElementById("WebGL-output").appendChild(renderer.domElement);
          
          // 8-1 cubeのアニメーション
          cube.rotation.x += 0.02;
          cube.rotation.y += 0.02;
          cube.rotation.z += 0.02;

          // 8-2 sphereのアニメーション
          step += 0.04;
          sphere.position.x = 20 + ( 10 * (Math.cos(step)));
          sphere.position.y = 2 + ( 10 * Math.abs(Math.sin(step)));

          requestAnimationFrame(render);
          renderer.render(scene, camera);
        }
        //7-3 reder関数を呼び出す
        render();
      }
      //9-1 ブラウザサイズが変更されたら出力を自動的にリサイズする
      function onResize() {
        camera.aspect = window.innerWidth / window.innerHeight;
        camera.updateProjectionMatrix();
        renderer.setSize(window.innerWidth, window.innerHeight);
      }
      window.addEventListener('resize', onResize, false);

      window.onload = init;

    </script>
  </body>
</html>
```

### まとめ

お疲れ様でした。

Three.jsでは、はじめにTHREE.Sceneオブジェクトを作成する必要があり、それからシーンオブジェクトにカメラ、ライト、描画したいオブジェクトを追加します。
さらに基本的なシーンを拡張して影とアニメーションを追加します。
Three.jsの基本の流れは以上で、
あとは細部の密度を高めることで豊かな表現が可能になっていきます。

実際にコピペでアニメーションできた方、コメントもらえると励みになります〜

ありがとうございました。