軽量three.jsを作成した話

![Kapture 2018-12-20 at 12.06.06.gif](https://qiita-image-store.s3.amazonaws.com/0/42248/538d3e3a-4e40-7be2-084c-d5b669d093cc.gif)

この記事は、WebGL Advent Calendar 2018 17日目の記事です。


## WebGL採用されない問題
気づけばthree.jsも`r99`までバージョンが上がっており、歴史も長く認知度も非常に高いライブラリとなりました。しかし、実際に多くのサイトで使用されているかと言うとそうでもありません。そもそもWebGLを使用したサイトと出会うことも少ないです。なぜなのでしょうか？


### ① WebGLは難しい :smiling_imp: 
皆さんご存知の通り**WebGLを扱うには多くの知識が必要**です。そもそも3DCGと関わりのないエンジニアにとっては**ハードルが高すぎ**ます。筆者も元から詳しい方ではなかったので今もまだ勉強中です。。。しかしthree.jsの登場により、WebGLを簡単に扱えるようになりました。しかしWebGLを採用するサイトが爆増したわけではありません。


### ② three.jsはファイルサイズが大きすぎる :cry: 
`three.min.js`ファイルは`125KB`もあります。昨今ではアクセシビリティの向上を図り、SSRやServiceWorkerによるキャッシュなどページ読み込みを早くする工夫がなされています。そんな中で**ファイルサイズの大きいライブラリを使用してまでWebGLを採用しようとはならない**でしょう。

## もっとWebGLを気軽に採用してほしい :pray:
これらの問題により、WebGLを採用しているサイトは少し凝ったキャンペンページや、ツール系のサイト、それらを作っているエンジニアのポートフォリオサイトくらいでしかお目にかかれません。自分はもっとサイトの背景やちょっとしたマイクロインタラクションなどに**気軽に使われてもいいのでは？**と日々考えていました。

## 軽量なthree.jsなら使えるのでは？ :thinking: 
筆者のような雑魚WebGL使いにとってやはりthree.jsの使いやすさは格別なものでした。であれば、three.jsの使いやすさを保ったままファイルサイズを抑えられれば気軽にWebGLを採用できるようになるのではと考えました。


## 作ってみました
ということで軽量版three.jsの[shree.js](https://sawa-zen.github.io/shree/)を作ってみました。(名前は適当です。)詳しくは[ドキュメントページ](https://sawa-zen.github.io/shree/)を作ったのでそちらを参照ください。

[shree.jsのリポジトリはこちら](https://github.com/sawa-zen/shree)

### 主な仕様
- RawShaderMaterialとBufferGeometryのみで扱えるthree.jsを目指す
- なるべくthree.jsとインターフェースを合わせる
- 用意したクラス
 - Renderer - THREE.WebGLRendererっぽいやつ
 - Scene - THREE.Sceneっぽいやつ
 - Camera - THREE.PerspectiveCameraっぽいやつ
 - Object3D - THREE.Object3Dっぽいやつ
 - Geometry - THREE.BufferGeometryっぽいやつ
 - Material - THREE.RawShaderMaterialっぽいやつ
 - Mesh - THREE.Meshっぽいやつ
 - Points - THREE.Pointsっぽいやつ
 - Vector3 - THREE.Vector3っぽいやつ
 - Matrix4 - THREE.Matrix4っぽいやつ
 - Quaternion - THREE.Quaternionっぽいやつ


### 簡単な使用例

例えば三角ポリゴンを表示する場合は以下のようなコードになります。

![thumbnail_basic.png](https://qiita-image-store.s3.amazonaws.com/0/42248/dfd38356-413a-14d5-8105-038d09b5d055.png)


```javascript
// sample.js

var wrapper = document.getElementById('wrapper');

// レンダラー
var renderer = new SHREE.Renderer();
renderer.setSize(wrapper.clientWidth, wrapper.clientHeight);
wrapper.appendChild(renderer.domElement);

// カメラ
var camera = new SHREE.Camera();
camera.position.z = 2;

// シーン
var scene = new SHREE.Scene();

// マテリアル
var material = new SHREE.Material({
  vertexShader: document.getElementById('vs').text,
  fragmentShader: document.getElementById('fs').text,
});

// ジオメトリ
var geometry = new SHREE.Geometry();
geometry.addAttribute('position', 3, [
  0.0,  0.5,  0.0,
 -1.0, -0.5,  0.0,
  1.0, -0.5,  0.0,
]);
geometry.addAttribute('color', 4, [
  1.0, 0.0, 0.0, 1.0,
  0.0, 1.0, 0.0, 1.0,
  0.0, 0.0, 1.0, 1.0,
]);
geometry.index = [0, 1, 2];

// メッシュ
var mesh = new SHREE.Mesh(geometry, material);
scene.add(mesh);

// 描画
renderer.render(scene, camera);
```

```glsl
// vertex shader

attribute vec3 position;
attribute vec4 color;
uniform mat4 pMatrix;
uniform mat4 mvMatrix;
varying vec4 vColor;

void main(void){
  vColor = color;
  gl_Position = pMatrix * mvMatrix * vec4(position, 1.0);
}
```

```glsl
// fragment shader

precision mediump float;
varying vec4 vColor;

void main(void){
  gl_FragColor = vColor;
}
```

three.jsと同じ感覚で描画することができました。

## three.jsとの比較

記事の冒頭に貼ったGIFのアニメーションをthree.js版とshree.js版で作成してファイルサイズを比較してみました。尚、three.js版では余分なthree.jsのクラスを読まないようにTree Shakingがされるようにコードを書いています。

### three.js版 343KB
<img width="657" alt="スクリーンショット 2018-12-20 12.23.24.png" src="https://qiita-image-store.s3.amazonaws.com/0/42248/6376a1ac-bbd0-fdbd-0f97-3b322250c56d.png">
[three.js版デモのコードを見たい方はこちら](https://github.com/sawa-zen/three_js_demo/tree/master/points_three)

### shree.js版 31.8KB
<img width="656" alt="スクリーンショット 2018-12-20 12.21.36.png" src="https://qiita-image-store.s3.amazonaws.com/0/42248/cf14596a-3a48-c5ce-c6e3-5dc720aac2e0.png">
[shree.js版デモのコードを見たい方はこちら](https://github.com/sawa-zen/three_js_demo/tree/master/points_shree)

### 約1/10にダイエット成功!!! :smiley: 

なんと1/10まで軽量化に成功しました！まだ機能が不十分なので軽量になっている部分もありますが、three.jsの便利なコア機能のみを残して不要な部分が削ぎ落すことができました！


## まとめ
実は今年(2018年)の夏にdoxasさんのWebGLスクールを受講したのですが、その後何もしていなかったので復習を兼ねてshree.jsを作成していました。まだまだ皆さんに使ってくださいと言えるレベルではないですが、ほそぼそと自分の作品で使用していき少しずつバージョンアップしていこうと思います :smile: