2016年09月27日

Alembic形式の読込みに関するメモ

MMDBridgeなどでAlembic形式が使われていますので、Alembicのライブラリのコンパイルとか使用方法のメモです。最初は[2]を参考に、HDF5を使ったAlembicファイルを読み込んでいたのですが、なぜか1000フレームを超えるデータを読み込もうとするとエラー終了してしまうので、諦めてOgawa形式のAlembicファイルを使用することにしました。MMDBridgeでは、mmdbridge_alembic_for_houdini.pyをそのまま使っています。

Alembicのホームページは[1]にあります。SourceをクリックするとGitHubのページに移動しますので、そこからソースコードをダウンロードします。必要なのかどうかわかりませんが、ilmbaseのライブラリも準備しました。これは以前OpenEXRの準備をしたときに作っていたものを使用しました。

Alembicのコンパイルは、CMakeを使ってやりました。generate先はbuild64というディレクトリを作成してそこにソリューションファイルを生成するようにしました。設定した項目は下記の感じです。

ilmbaseのライブラリやインクルードディレクトリの設定
ALEMBIC_ILMBASE_HALF_LIB
ALEMBIC_ILMBASE_IEXMATH_LIB
ALEMBIC_ILMBASE_IEX_LIB
ALEMBIC_ILMBASE_ILMTHREAD_LIB
ALEMBIC_ILMBASE_IMATH_LIB
ILMBASE_INCLUDE_DIR

USE_HDF5などもあるのですが、最初に書いたようにうまくいかなかったので今回はOFFのままにしました。後はVisual Studioを起動してビルドすれば、Alembic.libができているはずです。

使用方法

ライブラリを使用する場合の設定です。Cドライブに直入れしている場合の例なので適当です。

追加するインクルードディレクトリ
C:\alembic-1.6\lib
C:\alembic-1.6\build64\lib
↑ Config.hのために必要
C:\OpenEXR\include

追加するライブラリディレクトリ
C:\alembic-1.6\build64\lib\Alembic\Release
C:\OpenEXR\lib
追加するライブラリファイル
Alembic.lib

ソースコードの一部
#include <Alembic/AbcCoreAbstract/All.h>
#include <Alembic/Abc/ErrorHandler.h>
#include <Alembic/Abc/All.h>
#include <Alembic/AbcGeom/All.h>
#include <Alembic/AbcCoreOgawa/All.h>

using namespace Alembic::Abc;
using namespace Alembic::AbcGeom;

...

  // Ogawa形式のAbcファイル読込み
  Alembic::Abc::IArchive archive(Alembic::AbcCoreOgawa::ReadArchive(), FileName);
  Alembic::Abc::IObject obj = archive.getTop();
  // 子データ数の取得
  unsigned int numChildren = obj.getNumChildren();
  std::cout << "found " << numChildren << " children in file\n";

  // 子データの数だけ繰り返し
  for (int i = 0; i < numChildren; ++i)
  {
    // 子データのオブジェクトを取得
    IObject child(obj, obj.getChildHeader(i).getName());
    // さらに子データの数を取得
    std::cout << "Children " << child.getNumChildren() << "\n";
    const MetaData &md = child.getMetaData();
    std::cout << md.serialize() << "\n";

    // 子データが見つかった場合
    for (int x = 0; x<child.getNumChildren(); x++)
    {
      // 子データのオブジェクトを取得
      IObject child2(child, child.getChildHeader(x).getName());
      const MetaData &md2 = child2.getMetaData();
      std::cout << md2.serialize() << "\n";

      // 子データがメッシュデータの場合(今回はカメラの時は未処理)
      if (IPolyMeshSchema::matches(md2) || ISubDSchema::matches(md2))
      {
        std::cout << "Found a mesh " << child2.getName() << "\n";
        // we have a mesh so build a mesh object
        IPolyMesh mesh(child, child2.getName());
        // grab the schema for the object
        IPolyMeshSchema schema = mesh.getSchema();

        // now grab the time sample data for the object
        IPolyMeshSchema::Sample mesh_samp;
        // ISampleSelectorを使って取得するフレームを設定する(今回は11200番目のフレーム)
        // HDF5だと1000以上の値を設定するとなぜかエラーが発生した
        ISampleSelector ss(Abc::index_t(11200));
        schema.get(mesh_samp, ss);
        // get how many points (positions) there are
        Alembic::Util::v8::uint32_t size = mesh_samp.getPositions()->size();
        // 面数の取得
        Alembic::Abc::Int32ArraySamplePtr faceNum = mesh_samp.getFaceCounts();
        // 法線情報の取得
        IN3fGeomParam N = schema.getNormalsParam();
        // we didn't set any on write, so on read, it should be an invalid container
        // getExpandedValue() takes an optional ISampleSelector;
        // getVals() returns a TypedArraySamplePtr
        N3fArraySamplePtr nsp = N.getExpandedValue().getVals();
        // UV情報の取得
        IV2fGeomParam uv = schema.getUVsParam();
        IV2fGeomParam::Sample uvsamp = uv.getIndexedValue();
        std::cout << "size=" << size << ", faceCount=" << faceNum->size() << "\n";
        // 面の数だけ繰り返す
        for (Alembic::Util::v8::uint32_t m = 0; m < size; ++m)
        {
          // get the current point
          V3f p = mesh_samp.getPositions()->get()[m];
          N3f Normal = nsp->get()[m];
          V2f uv2 = (*(uvsamp.getVals()))[m];
          // 以上で頂点座標/法線ベクトル/テクスチャ座標を取得
        }
        for (Alembic::Util::v8::uint32_t m = 0; m < faceNum->size(); ++m)
        {
          unsigned int index[3];
          // get the current face
          for (int k = 0; k < 3; k++)
          {
            index[k] = mesh_samp.getFaceIndices()->get()[3 * m + k];
          }
          // 以上で頂点インデックスを取得
        }
      }
    }
  }

  ...

基本的に[2]を参考にさせていただいて、Alembic 1.6でちょっと仕様が変わっている感じです。

実行結果

AlembicTest_001.png

変換のサンプルがAlembicに入っているので意味はないですが、Alembicを読み込んでObjで出力してMeshLabで表示したものです。Tda式ミクさんとマリエルさんステージを使用させていただいております。

[1] http://www.alembic.io/
[2] http://jonmacey.blogspot.jp/2011/12/getting-started-with-alembic.html

web拍手 by FC2
posted by シンドラー at 22:14 | Comment(0) | TrackBack(0) | OpenGL & Metasequoia | このブログの読者になる | 更新情報をチェックする

2016年09月11日

Oculus SDK 1.7.0とGLUTのテスト

前回はサンプルを改変しましたので、今回は自分のプログラムで使用する場合のテストです。といってもウィンドウ作成などをglut/glewで行って、残りはほぼサンプルのWin32_GLAppUtil.hを書き換えた感じです。

元のサンプルの部分がそうなのかどうか詳しく見てないですが、面の方向が逆だったのかglFrontFace(GL_CW)で時計回りにしないとうまく表示されませんでした。他にもGL_CULL_FACEを有効化しないとおかしくなったので、何か間違っているかもしれません。

実行結果

ovr170_test_001.png

以下ソース
Win32_GLAppUtil_Custom.h
OVR1.7.0OpenGL.cpp
simple170.vert
simple170.frag

OculusRoomTiny(GL)からの転用が多いので、その辺のライセンスになるかと思います。シェーダはWin32_GLAppUtil.hに直書きしていたのをそのままファイルに出力しただけです。何か問題が起こっても責任はとりません。
web拍手 by FC2
posted by シンドラー at 22:17 | Comment(0) | TrackBack(0) | Oculus Rift | このブログの読者になる | 更新情報をチェックする

2016年08月27日

Oculus SDK v1.7.0のOculusRoomTiny(GL)に関するメモ

Oculus Rift製品版届いていたのですが、あまり使っていなかったのでボチボチ触ってみようかと思います。今回はOculusSDK/SamplesのひとつであるOculusRoomTiny(GL)に関するメモです。一応OpenGLのサンプルですが、Oculus的にはDirectXがメインで、OpenGLはあまりサポートしていないみたいです。

サンプルプログラムの構成としては、main.cppと、OculusRoomTiny_Advanced/Common/Win32_GLAppUtil.hの2つがメインで、Win32_GLAppUtil.hの方で色々定義されているようです。

サンプルは単純なRoomのデモということですので、今回はこれを書き換えて、Tda式ミクさんを表示してみることにします。シーンの生成は、Sceneクラスで行っているため、この辺りを書き換えていくことになります。

// シーンの生成
roomScene = new Scene(false);

Sceneは構造体で、最大モデル数10個の配列になっていますので、モデル数を増やしたい場合はここも書き換える必要があります。

また、Init関数でシェーダをテキスト形式で直書きしているので、ここをファイル読込みに書き換えれば、GLSLのシェーダを使用できるようになると思います。デフォルトはかなり単純にテクスチャの色などをそのまま表示しているようです。その後、タイルパターンのテクスチャを生成しているので、ここを画像を読み込むようにすれば、モデルのテクスチャも使用できるようになるはずです。最終的に、Model構造体にテクスチャバッファを割り当てて、頂点やIndexに必要なメモリ領域を確保してからSceneに追加しています。

Model構造体では、頂点数及び面数が最大値2000と少ないので、ここはファイル読込みで動的確保するように書き換える必要があります。サンプルとしてAddSolidColorBoxという関数があり、この関数でBoxの追加をすることができますが、基本的にはAddVertex関数とAddIndex関数で頂点情報とIndex情報を設定して、最後にAllocateBuffers関数を呼ぶ流れになるかと思います。

今回はMMDBridge[1]でTda式ミクさん[2]に[3]のポーズをしてもらったものをobjで出力して、それを読み込むことにしました。で、実際にやってみたところ、HMDの姿勢の追従はかなり高い精度でしてくれているのですが、どうもPosition trackingの方が殆ど効いていないようです。コントローラで操作することをメインにしているのか、顔を前後左右に平行移動しても、視点の位置が変わらない感じです。
(2016.9.13追記:環境の問題だったみたいです。違うPCだと位置も追従しました)

DK2の時にはできていたので、製品版でもできるはずなのですが、やり方がよくわかりませんので、どなたかわかる方がいましたら教えてください。UnityのUtilityでも使えば行けるのでしょうか。

頭の位置が固定で周りを見回すYouTubeの360度動画のような感じではなく、頭を動かしてのぞき込んだり近づいたりといった操作ができた方が直感的だと思いますので、main.cppのMainLoopを書き換えることにしました。元のサンプルでは下記のような動作を想定しているようです。

oculus_sdk_v170_test001.png

本来、shiftedEyePosには左右の眼の位置であるEyeRenderPose[eye].Positionが反映されているはずなので、平行移動なども効いてくれるはずなのですが、この位置をファイルに出力してみても、平行移動ではあまり数値が変わらなかったりよくわかりませんでした。平行移動の情報が入ってそうなovrPoseStatefのLinearVelocityもなぜかずっと0になっていたり。

そこで無理やりですが、注視点を動かすのではなく、注視点を固定して、視点位置の方を動かすように変更してみました。

oculus_sdk_v170_test002.png
これで頭を動かすと、平行移動というか円運動したような感じになります。

実行結果

targetPosが、見えづらいですが赤いBoxで表示している位置です。そこを周りから眺めるような動作になります。


Seesaaの動画はFlash使ったPlayerをやめたんですね…。wmvが表示されなくなったみたいです。

[1] MMDBridge
[2] Tda式初音ミク・アペンドVer1.00
[3] あぴミクさんのポーズ
web拍手 by FC2
posted by シンドラー at 02:33 | Comment(0) | TrackBack(0) | Oculus Rift | このブログの読者になる | 更新情報をチェックする

2016年07月22日

ビットマップフォントに関するメモ

ここのところ月1回しか更新してないですね…。困ったものです。

今日はビットマップフォントに関するメモです。画面や画像に文字を表示する方法は、OpenGLやOpenCVなどにも用意されているので、それを使えば問題ないかと思います。今回のメモはライブラリ等を使わずに無理矢理表示するためのものです。

フォントについては、大きく分けてビットマップフォントとアウトラインフォントがあります。綺麗な文字を描きたいのであればアウトラインフォントだと思いますが、今回は簡単のためビットマップフォントを使用します。

フォントに関しては、ライセンスに気を付ける必要があります。今回は[1]のm+ fontsを使用させていただくことにしました。

こちらのフォントですが、BDFというファイルフォーマットのフォントデータがあります。BDFはBitmap Distribution Formatの略のようで、UNIXで標準的に使用されていたフォーマットのようです。

テキストベースのファイルフォーマットですが、自分で読み込むのも面倒ですので、[2]のbdf2bmpというソフトウェアを使用させていただき、bdfファイルをbmpファイルに変換しました。

後は文字ごとに切り出して拡大/縮小し、白:背景、黒:文字として元の画像にmixしてあげれば画像上に文字を描くことができます。

実行結果

Lenna画像上にHello worldを表示してみました。

chartest.png

[1] m+ fonts
[2] bdf2bmp
web拍手 by FC2
posted by シンドラー at 19:00 | Comment(0) | TrackBack(0) | Image Processing | このブログの読者になる | 更新情報をチェックする

2016年06月02日

平面の方程式に関するメモ

前回のソースコードで自分で追加した部分に関するメモです。間違っていたら教えてください。



web拍手 by FC2
posted by シンドラー at 18:54 | Comment(0) | TrackBack(0) | OpenGL & Metasequoia | このブログの読者になる | 更新情報をチェックする

2016年05月31日

libiglの使い方メモ その4

いつの間にか5月も終わりですね…。来月からはもう少し更新するようにしたいです。今回は前回の続き+αで、展開したテクスチャにAO計算した結果を焼き込んでみました。

1. Camelheadを読み込む
2. 前回と同じ方法でUV展開
3. Example 606を参考にAmbient Oculusionの計算
4. 各ポリゴンのテクスチャ画像上の座標とAO値を3次元ベクトル(tx*texSize, ty*texSize, ao)としてラスタライズ[1]〜[3]
5. テクスチャにラスタライズ結果のAO値を出力
6. UV値やテクスチャ情報を含むモデルとしてCamelheadを出力

ラスタライズに関しては、[3]のものをEigenを使ったものに書き換えたのと、[2][3]を参考にz値を補間するように追加しました。注意点としては、今回参考にしたラスタライザはおそらく右手系、ポリゴンは反時計回り順を想定していますので、テクスチャ座標も左回りになるように外積を使って判定する必要があります(頂点座標が左回りでも、対応するテクスチャ座標が左回りになっているとは限りません)。判定して右回りの場合は、ラスタライザに渡す頂点の順番を入れ替えます。

実行結果

CamelheadのテクスチャとしてAO情報を出力した結果
libigl004_camelhead1.png

MeshLabで出力したテクスチャ付きモデルを表示した結果libigl004_camelhead2.png
光源なし・テクスチャなし
libigl004_camelhead3.png
光源なし・テクスチャあり
libigl004_camelhead4.png
光源あり・テクスチャあり

耳や口のあたりはテクスチャ展開の結果の影響もあってあまりきれいじゃないですね。目の周りあたりはそれなりにうまくいっている気がします。

[1] http://ushiroad.com/rasterizer-refs/
[2] http://memo.render.jp/tekitou-hon-yaku-memo/re
[3] http://forum.devmaster.net/t/advanced-rasterization/6145
続きを読む
web拍手 by FC2
posted by シンドラー at 18:28 | Comment(0) | TrackBack(0) | OpenGL & Metasequoia | このブログの読者になる | 更新情報をチェックする

2016年04月30日

libiglについてのメモ その3

いつの間にか4月が終わって5月ですね。困ったものです。月1回は更新しておきたいので小ネタです。チュートリアルの501〜503ですが、UV展開のようなサンプルがあります。ただ、サンプルのcamelhead以外では、boundary_loopの所でエラー終了してしまうので、凸包など、何か適用できる条件があるのかもしれません。

harmonic関数で、展開したUVの計算ができるようです。その際、5番目の引数で展開方法が選べるようです。
(1: harmonic function, 2: biharmonic, 3: triharmonic, ...)

igl::harmonic(V, F, bnd, bnd_uv, 1, V_uv);

実行結果

数値を変えて画像として出力した結果です。V_uvは各頂点のUV座標ですが、0〜1の範囲ではなく、今回の結果では-1.2〜1.1等中途半端な値になっていましたので、適切に0〜1の間にマッピングする必要がありそうです。


libigl_camelhead1.png libigl_camelhead2.png libigl_camelhead3.png
   1: harmonic      2: biharmonic      3: triharmonic

次はAOを計算してテクスチャに焼き込むテストでもするかもしれません。

[1] http://libigl.github.io/libigl/tutorial/tutorial.html
web拍手 by FC2
posted by シンドラー at 23:20 | Comment(0) | TrackBack(0) | OpenGL & Metasequoia | このブログの読者になる | 更新情報をチェックする

2016年03月04日

libiglについてのメモ その2

前回の続きです。

とりあえずExample 203を参考に、その他のモデルで曲率とベクトルを計算してみました。その際、下記のようなエラーが出ました。

Could not compute curvature of radius 0.827856

エラーで検索してみると、重複する頂点があると失敗するので、igl::remove_duplicate_verticesを使って先に削除するように書かれていました[1]。

使い方ですが、下記のように使用します。
#include <igl/remove_duplicate_vertices>
...
  MatrixXd SV;
  MatrixXi SVI, SVJ;
  // 重複頂点の削除
  igl::remove_duplicate_vertices(V, 0.0000001, SV, SVI, SVJ);
  // SVに削除後の頂点
  V = SV;
  // SVJに元の頂点番号→削除後の頂点番号が格納されているので、
  // インデックスリストを更新
  // ループを回さず一括処理する方法があるはず?
  for (int i = 0; i < F.rows(); i++)
  {
    for (int j = 0; j < F.cols(); j++)
    {
      int temp = F(i, j);
      F(i, j) = SVJ(temp);
    }
  }
関数の引数としては、一つ目が頂点の位置座標行列、2つ目が重複判定のためのパラメータ、3番目が削除後の頂点の位置座標行列、4番目が、削除後の頂点番号→削除前の頂点番号のマップ、5番目が削除前の頂点番号→削除後の頂点番号のマップになります。これを使用して、面のインデックスを更新します。

実行結果

あぴミクさんをOFFに変換してテストしてみました。OFFというのは初めて見ましたが、3D CADなどで使われているファイルフォーマットのようですね[2]。

libigl_015.png

頂点を削除したくない場合は、削除前/削除後のマップは用意されていますので、計算するときだけ一時的に削除して、元の接線ベクトル/従法線ベクトルに割り当てるといった使い方はできるかと思います。

後、結果としては結構統一性がない気がするので、可視化にこれを使うと色々問題があるのかもしれません。頂点単位ではなく、テクスチャ単位で欲しい気もします。

[1] principle_curvature "Could not compute curvature" on STL file
[2] Wikipedia - OFF (fileformat)
web拍手 by FC2
posted by シンドラー at 00:30 | Comment(0) | TrackBack(0) | OpenGL & Metasequoia | このブログの読者になる | 更新情報をチェックする

2016年03月01日

libiglについてのメモ

いつの間にかもう3月ですね。2月は29日があったのにすぐ終わった気がします。

頂点座標に法線ベクトルは一般的に用意されていますが、接線ベクトルは用意されていない場合が多く、外積を使用したりテクスチャ座標から計算したりすることが多いです。頂点座標と法線ベクトルから計算できるのは接平面であり、ベクトルとしては無数に取れるため、どの方向を接線ベクトルとして採用するかが問題となります。

この辺を、例えば近傍頂点から曲面を推定し、勾配などから決める方法はないのかなと考えてみたところ、libigl[1]というライブラリを使えば良さそうな気がしました。ヘッダオンリーのC++ジオメトリ処理ライブラリで、MPLv2.0ライセンスのものです。内部でEigenを使っているようです。

[1]のリンクにもありますが、Tutorial[2]があり、かなり充実しているようですので、そちらを参考にすれば色々できると思います。

0. Mesh representation (Example 101)

libiglでは、Eigenライブラリを使用しており、ベクトルと行列を使用します。三角メッシュの頂点と面情報も行列のペアで表現されます。
    Eigen::MatrixXd V;
    Eigen::MatrixXi F;
Vは、頂点数×3(x,y,z座標)の行列で、Fは面数×3(面のインデックス(左回り))の行列のようです。こうすることで3つぐらいいいことがあるそうです。libiglでは一般的なメッシュフォーマットの読み書きはサポートしていて、read*.hやwrite*.hなどのファイルに記述されています。

libigl_001.png
[1]より引用した説明図

1. Curvature directions (Example 203)

ガウス曲率を計算する時の最大と最小の曲率を持つ主曲率の方向を計算できるようです。この2つのベクトルの片方を接線ベクトル、もう一つを従法線ベクトルにすれば、今回の目的は果たせそうではありますが、その結果がいいものなのかどうかは不明です。

実行結果

libigl_014.png

2. Baking ambient occlusion (Example 606)

レイキャスティングによる頂点ごとの環境遮蔽を計算してくれる関数が提供されています。使用する場合、外部ライブラリとしてEmbree[3]が必要でした。

実行結果

libigl_013.png

この他にも、DualQuaternionSkinningとか面白そうな機能がたくさんあります。

[1] GitHub - libigl
[2] libigl tutorial notes
[3] Embree - High-performance ray tracing kernels
web拍手 by FC2
posted by シンドラー at 22:55 | Comment(0) | TrackBack(0) | OpenGL & Metasequoia | このブログの読者になる | 更新情報をチェックする

2016年02月10日

Extreme Learning Machineのテスト

ニューラルネットワークのような感じでバックプロパゲーションで重みを学習するのではなく、最小二乗法で計算するELM(Extreme Learning Machine)というものがあるみたいなので、試してみました[1][2]。

ネットワーク構成
elm_001.png
最初バイアス入力を入れずにやっていて、近似できないので困っていました。出力の閾値の代わりとして入れているものなので、ELMでも当然必要でした。

elm_002.png

最小二乗法の計算は、[2]にも書かれていますが、データ数>未知数の場合と、データ数<未知数の場合で場合分けするべきですが、今回は片方しか書いていません。

elm_003.png

実行結果

活性化関数にシグモイド関数を使っています。とりあえずsinc関数に一様乱数ノイズを付与した入力データに対して、ELMを使用して関数近似してみました。中間層50,データ数100です。

elm_004.png

[1] Extreme Learning Machine
[2] Extreme Learning Machine
web拍手 by FC2
posted by シンドラー at 20:38 | Comment(0) | TrackBack(0) | Machine Learning | このブログの読者になる | 更新情報をチェックする

広告


この広告は60日以上更新がないブログに表示がされております。

以下のいずれかの方法で非表示にすることが可能です。

・記事の投稿、編集をおこなう
・マイブログの【設定】 > 【広告設定】 より、「60日間更新が無い場合」 の 「広告を表示しない」にチェックを入れて保存する。