【three.js】Lineオブジェクトを活用して線がクールなWebページをつくる

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

Three.jsには様々な立体物を作成するジオメトリがありますが、単なる線を引くLineオブジェクトのような奥行きのない要素はめったに使われていない印象があります。

本記事ではそんなLineオブジェクトを使った線の引き方と、Lineオブジェクトを活用したWebページ例を紹介します。



# 1. Lineオブジェクトの使い方
```
Line( geometry: Geometry, material: Material );
```
Lineオブジェクトに表面素材を指定する`Material`と、三次元ベクトルの頂点座標を与えた`Geometry`を渡すことで空間内に線を引くことができます。

```javascript:lines.html
        (...省略...)
        // マテリアルを設定
        const line_material=new THREE.LineBasicMaterial({ color:0xffffff });
        // ジオメトリを作成
        var line_geometry=new THREE.Geometry();
        // ジオメトリに頂点座標を追加
        line_geometry.vertices.push(
            new THREE.Vector3(-100,0,0),
            new THREE.Vector3(0,100,0),
            new THREE.Vector3(100,0,0),
        );
        var newline=new THREE.Line(line_geometry, line_material);
        scene.add(newline);

        tick();
        function tick() {       
          // カメラを回転させる
          rot+=0.5;
          const radian=Math.PI/180*rot;
          camera.position.x=500*Math.sin(radian);
          camera.position.z=500*Math.cos(radian);
          camera.lookAt(0,0,0);
       
          // レンダリング
          renderer.render(scene, camera);
          requestAnimationFrame(tick);
        }
        (...省略...)
```
実行例
<p class="codepen" data-height="265" data-theme-id="default" data-default-tab="html,result" data-user="chitomo12" data-slug-hash="zYxjyNW" style="height: 265px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="Draw Lines 01 by three.js">
  <span>See the Pen <a href="https://codepen.io/chitomo12/pen/zYxjyNW">
  Draw Lines 01 by three.js</a> by MasatomoFukuda (<a href="https://codepen.io/chitomo12">@chitomo12</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>

より細かい情報は公式リファレンスをご参照に。
https://threejs.org/docs/#api/en/objects/Line

# 2. Lineを使ったWebページをつくる
では早速、Lineを活用した簡単なWebページを作っていきます。

まず、ただの線で印象深いグラフィックを作るにはどうすれば良いかを考えます。
兎にも角にもアイデア出しです。

ざっと思いついたのが次の３つの案です。

1. 一本の線を動かしてみる
2. 複数の線を配置して幾何形体を作る
3. ランダムに線を配置して複雑な形態を作る

今回は「**3. ランダムに線を配置して複雑な形態を作る**」のアイデアを採用しました。

まずは、先程作成した線のパラメータを乱数に置き換え、複数描画してみます。
線の描画部分を以下の通りに書き換えてください。

```javascript:lines.html
        // 線のグループを作る
        var lines=new THREE.Group();
        const line_material=new THREE.LineBasicMaterial({ color:0xffffff });
        // for文でランダムな線（newline）を複数作る
        for(var i=0;i<10;i++){
          var line_geometry=new THREE.Geometry();
          line_geometry.vertices.push(
              new THREE.Vector3(-Math.random()*100, 0, 0),
              new THREE.Vector3(0, Math.random()*100, 0),
              new THREE.Vector3(Math.random()*100, 0, 0),
          );
          var newline=new THREE.Line(line_geometry,line_material);
          // newlineをグループに追加
          lines.add(newline);
        }
        // linesグループをシーンに追加
        scene.add(lines);
```

大きな変更点として、複数の線をまとめるためのグループを作成し、線の頂点を乱数に変更しました。
実行すると下図のようになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245143/ac31adca-2c68-32a1-7619-6e58b796ecbe.png)
すでに良い感じに見えますが、少し物足りなさは感じるのでどうにかしたい。

ここはシンプルに情報量を増やしたいと思います。
具体的には**頂点の数を増やし、さらにz座標のパラメータもいじる**ことで奥行きもつくっていきます。

さきほどのfor文内に、頂点を追加するための行を追加します。

```javascript:lines.html
        for(var i=0;i<10;i++){
          var line_geometry=new THREE.Geometry();
          line_geometry.vertices.push(
              new THREE.Vector3(-Math.random()*300,0,-Math.random()*300),
              new THREE.Vector3(0,Math.random()*300,-Math.random()*300),
              new THREE.Vector3(Math.random()*300,0,-Math.random()*300),
              new THREE.Vector3(Math.random()*300,0,-Math.random()*300),
              new THREE.Vector3(Math.random()*300,0,-Math.random()*300)
          );
          var newline=new THREE.Line(line_geometry,line_material);
          lines.add(newline);
        }
```
実行すると次のようになります
![979635255e2a1d2b734f2dd95682e806.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245143/26494b37-cfba-d441-ff5e-e14611cb4e2d.gif)
最初はただの線でしたが、ここまで来たら複雑な立体物を使いこなしている感が出てきたかと思います！

さて、今回作ろうとしているのはWebページのサンプルです。

今のままだと3D空間内を大きく移動するような迫力ある視覚経験はできると思いますが、ゲームではなくWebページなのでこれをもう少し落ち着いたUXにしたい。

カメラが大きく空間を動くのではなく、カメラは動かないままオブジェクトだけが回る、そういう空間を作ろうと思います。

lineオブジェクトの集合の中心が座標 (0,0,0) に来るようにし、ページ全体に線が均等に現れるように調整を加えます。
for文を次のように書き換えてください。

```javascript:lines.html
        for(let i=0;i<20;i++){
          var line_geometry=new THREE.Geometry();
          line_geometry.vertices.push(
            new THREE.Vector3(0,0,0),
            new THREE.Vector3(Math.random()*400-200,Math.random()*400-200,Math.random()*400-200),
            new THREE.Vector3(Math.random()*400-200,Math.random()*400-200,Math.random()*400-200),
            new THREE.Vector3(Math.random()*400-200,Math.random()*400-200,Math.random()*400-200),
            new THREE.Vector3(Math.random()*800-400,Math.random()*400-200,Math.random()*800-400),
            new THREE.Vector3(Math.random()*800-400,Math.random()*400-200,Math.random()*800-400),
            new THREE.Vector3(Math.random()*400-200,Math.random()*4000-200,Math.random()*400-200)
          );
          var newline=new THREE.Line(line_geometry,line_material);
          lines.add(newline);
        }
```
そしてこれを実行すると下図のようになります。
![ezgif.com-resize (1).gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245143/b534ca3f-0241-2822-a355-f2d4e15a18c6.gif)
※画質が荒くてすみません💦　実際の見た目はもっと鮮明です。

これでもう、一つの映像として十分強度が出てきたのではないでしょうか？

仕上げとして、WebページのCSSやナビゲーション要素などを配置し、マウス位置に応じたインタラクティビティも加えます。
追加要素を列挙すると以下の通り。

・背景を水色に変更。
・回転するオブジェクトが画面右（カメラに対して少し右）に来るよう、時間経過でx-z座標を移動するように設定。
・DIVタグでテキストを作成し、CSSでpositionプロパティにabsoluteを入れて良い感じの位置に配置。
・マウス位置に合わせて回転の速度を増減する。

こうして出来上がったものが、冒頭でも紹介した以下のページになります。

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

# 締め

lineオブジェクトを使った事例が意外と少なかったので色々調べてみたのがこの記事を書いたきっかけだったのですが、マテリアルやシェーダーなど複雑な要素を用いることなく、ただの線を引くだけでそれなりに見映えするWebページを１つ作ることができたのは個人的にも意外な発見でした。

この記事がthree.jsをもっと楽しむきっかけになれば幸いです。

### 参考
公式リファレンス：https://threejs.org/docs/index.html#manual/en/introduction/Drawing-lines
Three.js入門サイト - ICS MEDIA：https://ics.media/tutorial-three/