Edited at
WebGLDay 17

軽量three.jsを作成した話

Kapture 2018-12-20 at 12.06.06.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を作ってみました。(名前は適当です。)詳しくはドキュメントページを作ったのでそちらを参照ください。

shree.jsのリポジトリはこちら


主な仕様


  • 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

// 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);

// 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);
}

// 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

スクリーンショット 2018-12-20 12.23.24.png

three.js版デモのコードを見たい方はこちら


shree.js版 31.8KB

スクリーンショット 2018-12-20 12.21.36.png

shree.js版デモのコードを見たい方はこちら


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

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


まとめ

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