search
LoginSignup
34

More than 5 years have passed since last update.

posted at

updated at

劇的に3Dデータ容量を削減するDRACO圧縮のデータを自前描画する方法

はじめに

前半で、Draco圧縮の概要説明を行い、後半に実際にDraco圧縮したデータを自前描画でWebGLで表示してみます。

概要編:Dracoについて

draco3d-vert-360x274.png

GoogleがDracoというオープンソースの3Dデータ圧縮方式をご存知でしょうか?
https://google.github.io/draco/

Googleの発表時のブログ(https://opensource.googleblog.com/2017/01/introducing-draco-compression-for-3d.html) によると、既存のデータをZIP圧縮したものよりも、高い圧縮率を実現できるようです。

Draco1.png
上記ブログURLより引用

実際によく利用されるOBJフォーマットのデータサイズとDraco圧縮したサイズを比較しました。
テストデータはよく利用されるBunny.objです。
bunny.png
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にデコードされたバッファが入るようになっています。
geodata.png

とりあえず、決め打ちで入れていますが、いじりたい人はdracoloader.jsをいじりましょう。

Dracoのデータ形式ですが、EncodedGeometryTypeというものがあり、
TRIANGULAR_MESHPOINT_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ライフを送りましょう。

それではまた!

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
34