タグベースの WebGL ライブラリを比較してみる

この記事は[Grimoire.js Advent Calender 2016](http://qiita.com/advent-calendar/2016/grimoire)の４日目の記事です。

## はじめに

Grimoire.js はタグベースで Web の 3D 表現を拡張するライブラリのようです。少し興味があったので、他にも類似したライブラリが無いか、調べてみました。

## タグベースの WebGL ライブラリの一覧

探してみると、そこそこあるようですね。Link とあるのは、[jsdo.it](http://jsdo.it) に投稿したサンプルへのリンクになります。

|ライブラリ名                                                |ライセンス                                                          |バージョン          |作者                     |![texture](http://jsdo-static-contents.s3.amazonaws.com/images/capture/j/q/D/jqD6_100.jpg)|![glTF](http://jsdo-static-contents.s3.amazonaws.com/images/capture/a/X/t/aXtZ_100.jpg)|
|:-----------------------------------------------------------|:-------------------------------------------------------------------|:-------------------|:------------------------|:-------------------------------|:-------------------------------|
|[GLAM](https://github.com/tparisi/glam)                     |[MIT](https://github.com/tparisi/glam/blob/master/LICENSE)          |beta(2015.02.13)    |Tony Parisi              |[Link](http://jsdo.it/cx20/qK0x)|[Link](http://jsdo.it/cx20/8oO3)|
|[X3DOM](https://github.com/x3dom/x3dom)                     |[MIT and GPL](https://github.com/x3dom/x3dom/blob/master/LICENSE)   |1.7.2(2016.12.19)   |X3DOM                    |[Link](http://jsdo.it/cx20/MGpZ)|[Link](http://jsdo.it/cx20/40CU)|
|[xml3d.js](https://github.com/xml3d/xml3d.js)               |[MIT](https://github.com/xml3d/xml3d.js/blob/master/LICENSE)        |5.2.1(2016.08.09)   |DFKI, Saarland University|[Link](http://jsdo.it/cx20/ueDz)|[Link](http://jsdo.it/cx20/mcnn)|
|[A-Frame](https://github.com/aframevr/aframe/)              |[MIT](https://github.com/aframevr/aframe/blob/master/LICENSE)       |v0.3.2(2016.10.13)  |A-Frame                  |[Link](http://jsdo.it/cx20/k7B2)|[Link](http://jsdo.it/cx20/aXtZ)|
|[three-canvas](https://github.com/technohippy/three-canvas) |[MIT](https://github.com/technohippy/three-canvas)                  |-(2015.08.12)       |technohippy              |[Link](http://jsdo.it/cx20/IueB)|                                |
|[model-tag](https://github.com/mrdoob/model-tag)            |[MIT](https://github.com/mrdoob/model-tag/blob/master/LICENSE)      |-(2016.11.19)       |Mr.doob                  |                                |[Link](http://jsdo.it/cx20/oA5v)|
|[Grimoire.js](https://github.com/GrimoireGL/GrimoireJS)     |[MIT](https://github.com/GrimoireGL/GrimoireJS/blob/develop/LICENSE)|v0.11.17(2016.12.08)|GrimoireGL               |[Link](http://jsdo.it/cx20/yNCh)|[Link](http://jsdo.it/cx20/irCY)|

![texture](http://jsdo-static-contents.s3.amazonaws.com/images/capture/j/q/D/jqD6_100.jpg) は、キューブにテクスチャを貼り付けるサンプル、
![glTF](http://jsdo-static-contents.s3.amazonaws.com/images/capture/a/X/t/aXtZ_100.jpg) は、3D モデルファイル（COLLADA や glTF ファイル等）の表示サンプルになります。

## 各ライブラリの特徴

### [GLAM](https://github.com/tparisi/glam)

作者の [Tony Parisi](https://en.wikipedia.org/wiki/Tony_Parisi_(software_developer)) さんは、古くは VRML、最近だと glTF の仕様策定に関わっている、Web3D 界の巨匠のような方です。Three.js 版の glTF Loader も初版は Tony Parisi さんによるものです。
また、GLAM（GL A nd M arkup）は、3D Webコンテンツを作成するための宣言的言語とのことです。残念ながら開発は beta で止まっているようです。レンダリングには Three.js が用いられているようです。

```html:index.html
<glam>
    <scene>
        <box id="photocube" class="animRotateY"></box>
    </scene>
</glam>
```

```css:style.css
#photocube {
    image:url(frog.jpg);
    rx:30deg;
}

@keyframes kfRotateY {
    from {
        transform: rotateY(0deg);
    }
    
    to {
        transform: rotateY(360deg);
    }
}
.animRotateY
{
    animation-duration: 10s;
    animation-name: kfRotateY;
    animation-iteration-count: infinite;
    animation-timing-function:linear;
}
```

### [X3DOM](https://github.com/x3dom/x3dom)

[X3DOM](http://www.x3dom.org/) は X3D シーンを HTML5 DOM 要素として統合および操作するためのフレームワークとのことです。

```html:index.html
<x3d width='465px' height='465px'> 
    <scene> 
        <transform DEF="cube" rotation="0 1 0 3.14159265359">
        <shape> 
            <appearance> 
                <ImageTexture url="frog.jpg"></ImageTexture>
            </appearance> 
            <box size='3, 3, 3'></box> 
        </shape> 
        </transform>
        <timeSensor DEF="time" cycleInterval="2" loop="true"></timeSensor>
        <OrientationInterpolator DEF='move' key='0 0.5 1' keyValue='1 0 0 0, 1 1 1 3.14, 1 0 0 6.28'/>
        <Route fromNode="time" fromField ="fraction_changed" toNode="move" toField="set_fraction"></Route>
        <Route fromNode="move" fromField ="value_changed" toNode="cube" toField="rotation"></Route>
    </scene> 
</x3d>
```

### [xml3d.js](https://github.com/xml3d/xml3d.js)

XML3D は [Intel が推奨する 3D 拡張規格](http://pc.watch.impress.co.jp/docs/column/kaigai/543300.html) のようです。
以下はサンプルコードですが、少し冗長になってしまいました。もしかしたらプリミティブ型があるのかもしれませんが、見つけることができませんでした。

```html:index.html
<xml3d id="MyXml3d" style="width: 100%; height: 100%" xmlns="http://www.xml3d.org/2009/xml3d" onload="initAnim()" view="#myview">
    <defs>
        <transform id="myxfm" rotation="1 0 1 0"></transform>
        <transform id="lightTransform" translation="-2 5 10" />

        <material id="phongShader" model="urn:xml3d:material:phong">
            <float3 name="diffuseColor">1.0 0.9 0.8</float3>
            <float name="ambientIntensity">1.0</float>
            <texture name="diffuseTexture">
                <img src="frog.jpg"/>
            </texture>
        </material>
        
        <lightshader id="lightShader" script="urn:xml3d:lightshader:point">
            <float3 name="intensity">1.0 1.0 1.0</float3>
            <float3 name="attenuation">1.0 1.0 1.0</float3>
        </lightshader>

        <data id="box">
            <int name="index">
                 0  1  2    0  2  3  <!-- Front face -->
                 4  5  6    4  6  7  <!-- Back face -->
                 8  9 10    8 10 11  <!-- Top face -->
                12 13 14   12 14 15  <!-- Bottom face -->
                16 17 18   16 18 19  <!-- Right face -->
                20 21 22   20 22 23  <!-- Left face -->
             </int>
            <float3 name="position">
                <!-- Front face -->
                -0.5 -0.5  0.5   0.5 -0.5  0.5   0.5  0.5  0.5  -0.5  0.5  0.5 
                <!-- Back face -->
                -0.5 -0.5 -0.5   0.5 -0.5 -0.5   0.5  0.5 -0.5  -0.5  0.5 -0.5 
                <!-- Top face -->
                 0.5  0.5  0.5  -0.5  0.5  0.5  -0.5  0.5 -0.5   0.5  0.5 -0.5 
                <!-- Bottom face -->
                -0.5 -0.5  0.5   0.5 -0.5  0.5   0.5 -0.5 -0.5  -0.5 -0.5 -0.5 
                <!-- Right face -->
                 0.5 -0.5  0.5   0.5  0.5  0.5   0.5  0.5 -0.5   0.5 -0.5 -0.5 
                <!-- Left face -->
                -0.5 -0.5  0.5  -0.5  0.5  0.5  -0.5  0.5 -0.5  -0.5 -0.5 -0.5 
            </float3>
            <float3 name="normal">
                <!-- Front face -->
                -0.5 -0.5  0.5   0.5 -0.5  0.5   0.5  0.5  0.5  -0.5  0.5  0.5 
                <!-- Back face -->
                -0.5 -0.5 -0.5   0.5 -0.5 -0.5   0.5  0.5 -0.5  -0.5  0.5 -0.5 
                <!-- Top face -->
                 0.5  0.5  0.5  -0.5  0.5  0.5  -0.5  0.5 -0.5   0.5  0.5 -0.5 
                <!-- Bottom face -->
                -0.5 -0.5  0.5   0.5 -0.5  0.5   0.5 -0.5 -0.5  -0.5 -0.5 -0.5 
                <!-- Right face -->
                 0.5 -0.5  0.5   0.5  0.5  0.5   0.5  0.5 -0.5   0.5 -0.5 -0.5 
                <!-- Left face -->
                -0.5 -0.5  0.5  -0.5  0.5  0.5  -0.5  0.5 -0.5  -0.5 -0.5 -0.5 
            </float3>
            <float2 name="texcoord">
                <!--  Front face -->
                0.0  0.0   1.0  0.0   1.0  1.0   0.0  1.0 
                <!--  Back face -->
                1.0  0.0   1.0  1.0   0.0  1.0   0.0  0.0 
                <!--  Top face -->
                0.0  1.0   0.0  0.0   1.0  0.0   1.0  1.0 
                <!--  Bottom face -->
                1.0  1.0   0.0  1.0   0.0  0.0   1.0  0.0 
                <!--  Right face -->
                1.0  0.0   1.0  1.0   0.0  1.0   0.0  0.0 
                <!--  Left face -->
                0.0  0.0   1.0  0.0   1.0  1.0   0.0  1.0 
            </float2>
        </data>
    </defs>
    <view id="myview" style="transform: translate3d(0px, 0px, -4px) rotate3d(0, 1, 0, 180.0deg)"><float name="fovVertical">0.7854</float></view>
    <group transform="#lightTransform">
        <light shader="#lightShader"/>
    </group>
    <group transform="#myxfm" style="material: url(#phongShader)" >
        <mesh src="#box" type="triangles">
        </mesh>
    </group> 
    
</xml3d>
```

```js:index.js
var anim = {};
function initAnim() {
    anim.xfm = document.getElementById("myxfm");
    anim.lastTime = (new Date()).getTime();
    anim.rotAngle = 0.0;
    anim.intervalID = window.setInterval(function () {
        var currentTime = (new Date).getTime();
        var delta = currentTime - anim.lastTime;
        anim.rotAngle += (30 * delta) / 1000.0;
        anim.xfm.setAttribute("rotation", "1.0 0.0 1.0 "+(anim.rotAngle * Math.PI / 180));
        anim.lastTime = currentTime;
    }, 20);
}
```

### [A-Frame](https://github.com/aframevr/aframe/)

A-Frame は VR シーンを記述するのに優れた最近人気のフレームワークのようです。レンダリングには Three.js が用いられています。

```html:index.html
<a-scene>
    <a-assets>
      <img id="my-texture" src="frog.jpg">
    </a-assets>
    <a-entity scale="2 2 2" geometry="primitive: box;" position="0 1 0" material="src: #my-texture">
        <a-animation easing="linear" attribute="rotation" dur="10000" to="360 360 360" repeat="indefinite"></a-animation>
    </a-entity>
    <a-entity position="0 0 3.8">
        <a-camera></a-camera>
    </a-entity>
</a-scene>
```

### [three-canvas](https://github.com/technohippy/three-canvas)

three-canvas は technohippy さんによる Three.js を用いた [Custom Elements](http://caniuse.com/#search=custom%20elements) ライブラリです。使用している Three.js のバージョンは少し古いようです。
残念ながら、今のところ、Custom Elements は仕様策定中であり、一部のブラウザしか対応していません。実行には、Chrome を推奨します。

```html:index.html
<three-canvas width="465" height="465" antialias="true" clear-color="#000000" default-light="true">
    <three-camera position="0,0,8" look-at="0,0,0" controls="orbit"></three-camera>
    <three-mesh name="box">
        <!-- frog.jpg -->
        <three-material color="#ffffff" map="frog.jpg"></three-material>
        <three-box-geometry width="1" height="1" depth="1"></three-box-geometry>
    </three-mesh>
</three-canvas>
```

```js:index.js
function rotateBox() {
    var threeCanvas = document.getElementsByTagName('three-canvas')[0];
    var threeJsMesh = threeCanvas.scene.getObjectByName('box');
    threeJsMesh.rotation.x += 0.01;
    threeJsMesh.rotation.y += 0.01;
}

function animate() {
    requestAnimationFrame(animate);
    rotateBox();
}

animate();
```

### [model-tag](https://github.com/mrdoob/model-tag) 

model-tag は Three.js の作者でもある Mr.doob さんによる実験プロジェクトです。
three-canvas 同様、[Custom Elements](http://caniuse.com/#search=custom%20elements) を用いたライブラリである為、一部のブラウザしか対応していません。実行には、Chrome を推奨します。

```html:index.html
<model-gltf src="BoxTextured.gltf"/>
```

### [Grimoire.js](https://github.com/GrimoireGL/GrimoireJS)

Grimoire.js は以前は jThree V3 と呼ばれていましたが、途中で名前が変わったようです。
Grimoire.js は XML に準拠した記法である GOML(Grimorie Object Markup Language) が特徴です。別ファイルに書くことが推奨されているようですが、HTML に埋め込むことも可能なようです。

```html:index.html
  <script id="main" type="text/goml" src="index.goml"></script>
```

```html:index.goml
<goml width="465" height="465">
    <import-material type="sprite" src="index.sort" />
    <material id="sprite" type="sprite" color="blue" sun="0.5,0.5" texture="frog.jpg"/>
    <scene>
        <camera>
            <camera.components>
                <MouseCameraControl rotateX="10" moveSpeed="1" />
            </camera.components>
        </camera>
        <mesh geometry="cube" material="#sprite"/>
    </scene>
</goml>
```

```js:index.js
var ratio = 0;

function rot() {
    gr('#main')('mesh').setAttribute('rotation', ratio + ',' + ratio + ',' + ratio);
    ratio += 1;
    requestAnimationFrame(rot);
}

rot();
```

## おわりに

いかがでしょうか。Web 上の 3D 表現に関しては、標準的な仕様が確立されていない為、多くの実験プロジェクトが存在するように思います。
ライブラリやフレームワークは十人十色なので、一概にこれが良い、というのは言えないですが、
その中で、Grimoire.js には、勢いがあるので是非 Web 3D 界の未来を切り開いていってほしいですね。