はじめに
前半で、Draco圧縮の概要説明を行い、後半に実際にDraco圧縮したデータを自前描画でWebGLで表示してみます。
概要編:Dracoについて
GoogleがDracoというオープンソースの3Dデータ圧縮方式をご存知でしょうか?
https://google.github.io/draco/
Googleの発表時のブログ(https://opensource.googleblog.com/2017/01/introducing-draco-compression-for-3d.html) によると、既存のデータをZIP圧縮したものよりも、高い圧縮率を実現できるようです。
実際によく利用されるOBJフォーマットのデータサイズとDraco圧縮したサイズを比較しました。
テストデータはよく利用されるBunny.objです。
69451 Triangles
データ種別 | データサイズ | 圧縮率 |
---|---|---|
オリジナルOBJ | 4,930,097 Byte | 100.0% |
OBJデータをZIP圧縮 | 1,574,690 Byte | 31.9% |
Draco圧縮 | 108,926 Byte | 2.2% |
ZIP圧縮でデータサイズが1/3になりましたが、DRACO圧縮は1/45になりました。ZIP圧縮なんて足元にも及びませんw
glTFのDraco拡張について
一昨日のWebGLアドベントカレンダーでglTFについて解説されていましたね。
https://qiita.com/cx20/items/95127986f4b9fa91fe9b
glTFのデータフォーマットを策定しているクロノスグループでは、このdraco圧縮を取り込んで行こうとする動きがあります。
仕様はほぼ固まっており、glTF 2.0の KHR_draco_geometry_compression拡張として取り込んでいく方針のようです。
https://github.com/KhronosGroup/glTF/pull/874
glTFは特にWebGLで取り扱いしやすいフォーマットであるため、データ圧縮をすることで、
モデルデータを軽量にし、データ配信の容量をさげるという狙いがあります。
上記の圧縮率をみると、このフォーマットに対応しない手はないでしょう。
上記のbunnyのモデルデータで、5MB近くのデータをサイトで配信するとなると、モバイル環境では厳しいでしょう。
ZIP圧縮して1.5MBでも少し考えてしまう容量です。100KBバイトならどうでしょう?
恐らくモバイルへの配信を考えても問題なくデータとして組み込める容量だと思います。
実践編
では実際にデータを圧縮してWebGLで表示してみましょう。
Draco圧縮してみる
Dracoのリポジトリをとってきます。
$git clone https://github.com/google/draco
cmakeでビルドです。MacOSだと以下のような感じです。
$cd /path/to/draco/
$mkdir build
$cd build
$cmake ..
$make
ビルドできたでしょうか?Windowsの人うまくいきませんか?
Windows環境で、CMAKE_C_COMPILERがないとかエラーが出る人は、パスが通ってないです。
VisualStudio 2015でのビルドの場合は以下のような感じです。
(VisualStudio2015 x64 NativeToolsコマンドプロンプトを起動する)
↓
$cd /path/to/draco/
$mkdir build
$cd build
$cmake -G "Visual Studio 14 Win64" -DCMAKE_C_COMPILER="C:/Program Files (x86)/Microsoft Visual Studio 14.0/VC/bin/cl.exe" -DCMAKE_CXX_COMPILER="C:/Program Files (x86)/Microsoft Visual Studio 14.0/VC/bin/cl.exe" ..
上記コマンドを実行すると、buildフォルダに VisualStudioのソリューション・プロジェクトファイルができているので、それを開いてビルドしてください。
さて、ビルドが完了したら、ビルドされたエンコーダツールで、試しにデータを作ってみましょう。
リポジトリにテストデータ入っているので、これを利用します。(自分の好きなデータを用意してください)
$cd build
$./draco_encoder -i ../testdata/bunny_norm.obj -o bunny.drc
Encoder options:
Compression level = 7
Positions: Quantization = 14 bits
Normals: Quantization = 10 bits
Encoded mesh saved to bny.drc (94 ms to encode)
Encoded size = 108926 bytes
For better compression, increase the compression level '-cl' (up to 10).
はい。Dracoのデータができました。 -clでCompression levelが変更できますが、10とかにしても、あまり小さくはなりません。多少小さくはなりますが、OBJからの圧縮率としては誤差レベルでしょう。
WebGL表示編
Three.js使いの人
javascriptディレクトリにthree.jsを利用したサンプルが置いてあるので、そちらを参照してください。そのまま差し替えるだけで動きますね。
https://github.com/google/draco/tree/master/javascript/example
自前エンジンの人
それでは、上で圧縮したデータ(69451三角形のbunny.obj)を自前描画WebGLで表示してみましょう。
動作サンプル(iPhoneとかでも動きます)
https://kioku-systemk.github.io/dracoSample/
githubにサンプルソース一式を置きました。
https://github.com/kioku-systemk/dracoSample
順番に解説していきます。
圧縮したのC++のプログラムでしたが、ブラウザでデータをデコードするために、
JavaScriptのDracoのデコーダが用意されています。C++のソースコードをEmscriptenを利用して、jsに変換したものになります。
DracoRepository/javascript/draco_decoder.js がデコーダ本体です。
Draco_decoder.wasm はWebAssembly版です。今回はだいたいどこでも動くjs版を利用します。
Dracoのデコーダを読み込ませておきます。
<script src="draco_decoder.js"></script>
デコーダはデコードしかしてくれないので、データのロードや、VertexBufferなどは
自分で作ってあげる必要があります。
今回は dracoloader.js というものを作成しました。 (https://github.com/kioku-systemk/dracoSample/blob/master/dracoloader.js)
<script src="dracoloader.js"></script>
dracoloaderの loadDraco 関数でデータをロードしデコードします。
https://github.com/kioku-systemk/dracoSample/blob/5611528416d4e0afb10cbec52d70493602d8a552/dracoloader.js#L210
loadDraco('bunny.drc', function (geodata) {
console.log(geodata);
});
デコードされたら、geodataにデコードされたバッファが入るようになっています。
とりあえず、決め打ちで入れていますが、いじりたい人はdracoloader.jsをいじりましょう。
Dracoのデータ形式ですが、EncodedGeometryTypeというものがあり、
TRIANGULAR_MESH と POINT_CLOUD の2種類のタイプがあります。
https://github.com/kioku-systemk/dracoSample/blob/5611528416d4e0afb10cbec52d70493602d8a552/dracoloader.js#L180
Dracoは、ポイントクラウドと呼ばれる点群と、三角形の幾何学形状をもつジオメトリデータを、効率よく圧縮できることを想定しているため、この2種類のデータ形式をサポートしています。それぞれのエンコードタイプにより、処理が異なるので注意して下さい。
頂点データおよび、インデックスデータを取得できたら、あとは自前描画のためにバッファを作成して、
描画してあげればOKです。
描画するときの注意点ですが、Dracoはデータが圧縮されるので、かなりたくさんの三角形が扱えてしまいます。
そのため、インデックスバッファが16ビット精度ではたりないので、32ビットが必要になってきます。
const stripsArray = new draco.DracoInt32Array();
const numStrips = decoder.GetTriangleStripsFromMesh(dracoGeometry, stripsArray);
indices = new Uint32Array(stripsArray.size());
https://github.com/kioku-systemk/dracoSample/blob/5611528416d4e0afb10cbec52d70493602d8a552/dracoloader.js#L70
Indexの配列を作成するときに32ビット整数の配列で取得しています。
WebGL 1.0では、標準では32ビット整数のインデックスバッファを利用できません。ローダで作ったバッファをそのまま使うには
拡張機能を有効にして、32ビット整数を利用できるようにしないといけません。
gl.getExtension("OES_element_index_uint");
以上が、Dracoを自前描画するための解説です。
みなさんもDraco対応して、Web上に流れる3Dデータの容量を削減して、
快適なWebGLライフを送りましょう。
それではまた!