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年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 | このブログの読者になる | 更新情報をチェックする

2015年11月24日

PolyVoxの使い方についてのメモ

いつの間にか11月下旬ですね…。もう来月で一年が終わってしまうとは困ったものです。
以前の記事でも使っていましたが、PolyVox[1]をMinecraft風に使う場合のメモです。

PolyVoxでは、表面ポリゴン抽出を、マーチングキューブス法を使ってなるべく滑らかな表面を作成する方法と、Minecraftのように立方体が積み重なったような表面を作成する方法のどちらかを選ぶことができます。

CubicSurfaceExtractorWithNormalsでは、ボリュームデータから表面ポリゴンを立方体ベースで作成してくれます。このとき、ポリゴン情報とボリューム格子番号の対応が取れないっぽいのですが、マテリアル番号は格納されているようです。

まず、Minecraft風のボリュームデータでは、各格子が持つデータ1バイト(0〜255)をマテリアル番号として、0を空気、それ以外を物体とします。今回は例題としてMagicaVoxel[2]のサンプルデータを読み込んで、それをボリュームデータとしてそのまま適用しました。

で、ポリゴンデータはチュートリアル[3]にもあるように、下記3行で抽出できます。


  SurfaceMesh<PositionMaterialNormal> mesh;
  CubicSurfaceExtractorWithNormals< SimpleVolume<uint8_t> >
    surfaceExtractor(&volData, volData.getEnclosingRegion(), &mesh);
  surfaceExtractor.execute();

次に、頂点座標やマテリアル番号の参照方法ですが、こちらもチュートリアルを参考にします。

  //Convienient access to the vertices and indices
  const vector<uint32_t>& vecIndices = surfaceMesh.getIndices();
  const vector<PositionMaterialNormal>& vecVertices = surfaceMesh.getVertices();

  for (int i=0; i<vecVertices.size(); i++)
  {
    float p[3], n[3], materialNo;
    p[0] = vecVertices[i].position.getX();
    p[1] = vecVertices[i].position.getY();
    p[2] = vecVertices[i].position.getZ();
    n[0] = vecVertices[i].normal.getX();
    n[1] = vecVertices[i].normal.getY();
    n[2] = vecVertices[i].normal.getZ();
    // float型
    materialNo = vecVertices[i].getMaterial();
  }

あと、テクスチャ座標は取得できない?ようでしたので、面と頂点座標情報から適当に生成して1面に1枚下記画像を張り付けるようにしてみました。
polyvox_test2_003.png

voxファイルにはマテリアル番号と対応する色パレットがありますので、色×テクスチャ色で適当に色付けしています。

実行結果
biome.voxを使用させていただいております。問題があればお知らせください。

 polyvox_test2_001.png polyvox_test2_002.png

まぁMagicaVoxelでPlyやObjで出力できますので、MagicaVoxel対応データをポリゴン化する意味はないですが、現状ではサイズ制限がありますので、大きめのボリュームデータをとりあえずボクセル化したい場合などには使えるかなとは思います。

[1] http://www.volumesoffun.com/polyvox-about/
[2] https://ephtracy.github.io/
[3] http://www.volumesoffun.com/polyvox/documentation/0.2.1/manual/tutorial1.html
web拍手 by FC2
posted by シンドラー at 00:23 | Comment(0) | TrackBack(0) | OpenGL & Metasequoia | このブログの読者になる | 更新情報をチェックする

2015年07月02日

OpenCVで海面生成のサンプル

いつの間にか7月ですね…。先月は忙しかった気がするので、今月はゆっくりしたいものです。

コメントに海面生成のソースが見たいという要望がありましたので、OpenCV 2.4.6ぐらいとfreeglutを使って作り直しました。時間が経っているので色々忘れていて困ったものです。

アルゴリズムとしては離散フーリエ変換による海面の生成 その1の参考文献に挙げている辺りです。一部のソースコードは参考文献のサイトのものをそのまま使っていた気がします。参考文献には特にライセンスに関する記述などは見当たりませんでしたが、もし問題があればどなたかお知らせください。また、OpenCVでFFTは[1]を参考にさせていただきました。

iOSということは携帯端末っぽいのでメモリとか計算時間とか厳しい気がしますので、事前計算してアニメーションメッシュに落とし込んでおくとか画像化しておくとかそういう工夫が必要そうです。

後バグがいろいろありそう(横幅と縦幅の文字を間違えていそうなところがあったので正方形(横幅=縦幅)以外ではエラーがでるかも)なので、気を付けてください。(このソースコードを使用して発生した如何なる不利益などについての責任を取りません)

以下ソース
opencv_ocean.zip

実行結果
opencv_ocean_001.png

シェーダもVBOも使わないシンプルワイヤフレーム表示しかしていません。

[1] http://opencv.jp/opencv2-x-samples/2d_dft_idft

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

2015年06月04日

マウスでカメラ操作のテスト その2

以前の記事で極座標ベースのカメラ操作を実装していたのですが、マウス操作量に掛けるスケールは定数で、視野角やカメラから注視点までの距離などを考慮していなかったため、カメラパラメータによっては操作感が悪い状態でした。

そこで、その辺りのカメラパラメータを考慮したマウス操作量を検討してみました。

まずは平行移動量です。UpとRightベクトルは以前の方法で、それに掛けるスケールを1ピクセル分を基準に計算します。
mouse_view_001.png

次は回転です。シンプルですが、視野角などに依存しないのでこれで良さそうです。
mouse_view_002.png

最後はホイールでの距離操作です。これもシンプルなもので特に問題なさそうです。
mouse_view_003.png

実行結果


ボクセル化を行うなら、平行投影の方がいい気がしますが、今は透視投影で視野角小さ目でやっていたりするので、その辺は試してみますかね。
web拍手 by FC2
posted by シンドラー at 02:32 | Comment(0) | TrackBack(0) | OpenGL & Metasequoia | このブログの読者になる | 更新情報をチェックする

2015年05月30日

PCLと深度バッファを用いたポリゴンモデルのボクセル化

とりあえずボクセル関係で出来そうなこととして、深度バッファを用いたポリゴンのボクセル化を試してみました。

流れ
1. モデル読込み
2. 最大長を指定して、読込んだモデルを囲むボクセルを生成(PCL)
3. Octreeを生成(PCL)

4. 1パスでカラー値と深度値をFBOに出力(OpenGL)
5. 2パスで、OctreeとRayが衝突した表面ボクセルのみを描画(PCL、OpenGL)
6. 3パスで1. と2. をMixして表示(OpenGL)

ここまでの結果
voxelize_001.png
カラーバッファだけの表示も可能
voxelize_002.png

7. 視点からRayを飛ばし、FBOの深度値を参照して深度値より手前にある色のついていないボクセルを削除、深度値付近のボクセルを、FBOのカラー値の色に設定。色を付けたことを示すためにラベルをインクリメント。(PCL)
8. OctreeとCloudの再構築(PCL)

正面から削除/色付けした結果
voxelize_003.png
前後方向から削除/色付けした後横から見た結果
voxelize_004.png

9. いくつかの視点から削除(PCL、OpenGL)

視点を変更することで、前後上下左右で隠れていた部分も削除/色付け可能
voxelize_006.pngvoxelize_007.png

10. 削除されていないかつ色がついていないボクセルを、近傍ボクセルに色がついていればその色に設定し、色がついたボクセルがひとつも見つからなければGUIで設定した基本色を設定(PCL)
(時間がかりすぎるので要最適化)
11. MagicaVoxelに対応している.voxもしくは.slab.voxファイルとして出力
 (.voxファイルは最大256色なので、パレットから似た色に変換)

実行結果
テストとして、[2]〜[4]のモデルを使用させていただき、ボクセル化してMagicaVoxelのレンダラで表示してみました。すべて最大長のボクセル数を512とした結果です。これより小さくするとナンバープレートが潰れたり微妙でした。計算はボクセル数多めにしておいて、出力時にうまく縮小するなどできるといいのかもしれません。

snap0000.png snap0001.png

snap0003.png snap0004.png

snap0002.png snap0005.png

ボクセル数が増えると使用メモリ量・処理時間ともに増えてなかなかつらいところです。

MagicaVoxelの.voxは8ビットと書いているように、ボクセルの座標も8ビット(0〜255)でしか指定できないので、最大でも255x255x255のサイズのボクセルしか構築できません。それ以上のサイズを使用する場合は、生データでサイズが大きくなりますがslabというファイルフォーマットを使用する必要があります。.voxのエクスポータを書いているとき、一部分しか出力されていなくて無駄に悩みました。

MagicaVoxelのレンダラで表示できるのはいいのですが、よく考えるとあまり使い道がないですね…。ポリゴンの方がきれいで軽いですし3Dプリンタなども使う予定はありませんし。マインクラフトとかに取り込めるんですかね。

メモ:マウス操作の操作性が悪いので改善する(距離に応じて移動/回転量を変更)

[1] http://www.naturalsoftware.jp/entry/2012/11/06/161132
[2] 軽自動車:http://seiga.nicovideo.jp/seiga/im4099340
[3] 頭蓋骨:http://flower-dam.com/wp/?p=3824
[4] らば式利根改二:http://www.nicovideo.jp/watch/sm24544789
続きを読む
web拍手 by FC2
posted by シンドラー at 13:07 | Comment(0) | TrackBack(0) | OpenGL & Metasequoia | このブログの読者になる | 更新情報をチェックする

2014年12月29日

OpenGLでビルボードのテスト その4

年末年始は油断するとすぐ終わってしまうので困ったものです。今回はビルボードの復習です。

billboard004_001.png

上の図のように、ビルボードはワールド座標で指定するのではなく、正規化デバイス座標系やスクリーン座標系で指定するとわかりやすくなります。しかし、正規化デバイス座標系やスクリーン座標系で指定した場合は、それをワールド座標に戻してボリゴンを描画しないと画面に表示することができません。

CGで線形代数が必要な部分ですが、座標変換を行列計算で行っているので、逆行列を使えば正規化デバイス座標系やスクリーン座標系をワールド座標に変換することができます。

billboard004_002.png

この辺の変換をきちんと把握していると、シェーダが読みやすくなりますね。ここでこの行列を掛けているから、今は視点座標系のデータだな…とか。

billboard004_003.png

今回は正規化デバイス座標系をワールド座標に戻したいので、ViewProjection行列の逆行列を、適当に設定したスクリーン座標4つに掛けてあげればワールド座標に戻すことができます。

実行結果

ティーポットとビルボードを表示しています。視点を回転させていて、ティーポットはそのまま、ビルボードは視点の方向に向くことを確認しています。

billboard004_004.png

glut+glmを使用したサンプルコード
billboard_004.cpp
web拍手 by FC2
posted by シンドラー at 23:40 | Comment(0) | TrackBack(0) | OpenGL & Metasequoia | このブログの読者になる | 更新情報をチェックする

2014年12月09日

GSLを用いた球面調和関数の計算 その8

あぴミクさんモデルでのテストが終わりました。OpenCLを使用しても、1頂点1秒とすると、2万頂点で2万秒(6時間弱)ほどかかりますので、もうちょっと高速化が必要かもしれません。

実行結果

今回は半球ではなく全球で遮蔽情報を計算しましたが、本来は半球で計算するべきかもしれません。後、完全に遮蔽されている頂点があると真っ暗になり、その頂点に隣接する面が必要以上に暗くなってしまっていますので、真っ暗な場合にもある程度0番目の項にだけ値を設定する等の工夫が必要かもしれません。



たまに表示されないみたいなのでその場合はDOWNLOADを押してみてください。

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

2014年12月06日

GSLを用いた球面調和関数の計算 その7

遮蔽情報の計算が怪しかったので修正してみました。

SH_GSL07_001.png

SH_GSL07_002.png

実行結果

床を平面で作ったので、端っこの頂点がほぼ遮蔽されずに明るくなりすぎていますが、何となくそれっぽくできていると思います。計算に時間がかかりますが、あぴミクさんのモデルでの再計算をやりたいと思います。



動的変形に対応した手法までやる気力がないので、あぴミクさんでうまく行ったらとりあえずは大体試せたことになりますかね。
web拍手 by FC2
posted by シンドラー at 00:14 | Comment(0) | TrackBack(0) | OpenGL & Metasequoia | このブログの読者になる | 更新情報をチェックする

2014年12月03日

GSLを用いた球面調和関数の計算 その6

前回の続きです。

球面調和関数の係数を線形補間すると、滑らかに変化するのかどうか試してみることにしました。

昼・夕方・夜っぽい3枚のパノラマ画像を[1]から使用させていただき、ぞれぞれ3次の球面調和関数近似を行い、係数を線形補間してみました。

実行結果

背景はmix関数で混ぜているのですが、何かおかしいですね。雰囲気的にはうまくいっているような気はしますが、どちらかというとモデルの頂点毎の遮蔽情報の計算が怪しそうです。

背景と遮蔽情報の内積のみ

テクスチャの色と上記の混合


[1] http://www.nicovideo.jp/watch/sm10968092

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

2014年11月27日

GSLを用いた球面調和関数の計算 その5

前回の続きです。

とりあえず単純なオブジェクトということで、球を使ったテストをしてみました。遮蔽情報が必要で、本来であればレイを飛ばして遮蔽するかどうか確認、といったことをしないといけないのですが、手抜きで上(法線方向)だけ遮蔽されておらず、そこからだんだん暗くなる下記のものをベースに、法線ベクトルに応じて前回の係数回転をさせることにしました。

          原画像                 3次(係数16個)で圧縮/展開した画像
SH_GSL05_003.png      SH_GSL05_004.png

環境マップには、わかりやすいように下記のような画像を作成しました。

          原画像                 3次(係数16個)で圧縮/展開した画像
SH_GSL05_001.png      SH_GSL05_002.png

実行結果

反射なのでわかりにくいですが、正しく動作していそうです。

前回からZ軸を上方向にしたせいか、Y軸とZ軸が入れ代わってしまっているので、↑はカメラを寝させているような状態になってしまっています。その辺は修正しないと駄目ですが、とりあえず回転含め球面調和関数が使えるようになったかなと思います。
web拍手 by FC2
posted by シンドラー at 03:23 | Comment(0) | TrackBack(0) | OpenGL & Metasequoia | このブログの読者になる | 更新情報をチェックする

2014年11月22日

GSLを用いた球面調和関数の計算 その4

球面調和関数の続きです。以前まで極座標でθはY軸方向を基準にしていましたが、参考文献などではZ軸方向が基準になっているものが多かったため、それに合わせることにしました。

sh_rot_004.png

で、今回は回転のお話です。自分は遮蔽情報をオブジェクト座標系で計算して頂点毎に持たせていますので、回転等のモデリング変換を行った場合には、遮蔽情報の係数を回転させてあげる必要があります。

参考文献[1]では、回転行列RからZYZオイラー角を取得し、SH回転行列を作成して回転を行っています。とりあえず[1]を参考に実装していくことにします。ただ、自分で実装しなくても[2]にソースコード付きのライブラリがあります。こちらにはSHEXP技法の計算なども含まれているようですが、今回はSH Rotationを参考にさせていただきます。

まずは回転行列からZYZオイラー角の取得です。

sh_rot_005.png

sh_rot_006.png

sh_rot_007.png

上にも書いてますが、[1]と結果が違うのでどうなのでしょうね。続いて回転です。オイラー角が取得できたら、それを使って[1]に載っている式で回転させます。順番は違いますが行列自体は[1]のものでうまくいったのでこれも謎ですね。ちゃんと行列を自分で導出すればわかるのかもしれませんが。

sh_rot_008.png

実行結果

適当に回転行列を作って、球面調和関数の係数を回転させてみました。一応色々な角度を試してみましたが、ちゃんとZ軸方向を向いてくれています。

sh_rot_001.png

sh_rot_002.png

sh_rot_003.png

次は実際のモデル等で試してみますかね。

[1] http://www.cs.columbia.edu/~cs4162/slides/spherical-harmonic-lighting.pdf
[2] http://www.pyramid-inc.net/technical


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

2014年11月14日

OpenGL 4.xのテッセレーションのテスト

いつの間にか11月も半分終わっているとか…。困ったものです。前回に引き続き、OpenGL 4.x系のテストです。最近?の機能といえば、テッセレーションだと思いますが、単純にメッシュを細かくしただけでは滑らかになるだけなので、やはりDisplacement Mappingとかやらないと恩恵が分かりにくいのかなと思いました。

まずはPer-vertex displacement mapping+bump mappingのテストです。頂点シェーダでディスプレイスメントマッピングをする場合、単純に頂点座標を法線ベクトル方向に高さ分だけ移動させるだけで行けると思います。

 vec4 tex = texture(NormalMapTex, VertexTexCoord);
 float height = SCALE * tex.a + OFFSET;
 vec4 pos = ModelViewMatrix * vec4(VertexPosition+height*VertexNormal, 1.0);

NormalMapTexは、rgbに法線ベクトル、aに高さ情報を格納したテクスチャです。法線マップがあるので、移動方向も法線マップの方向にした方がいいのかなと思って、法線マップをオブジェクト座標系に変換してみましたが、変なことになってしまったので普通の頂点法線方向がいいみたいでした。

これだと、頂点だけ移動して、法線ベクトルが正しくなくなるので、バンプマッピングで法線ベクトルはフラグメントシェーダ側でテクスチャから取得することにしました。

バンプマッピングについては[1]などを参考にさせていただきました。

実行結果
[1]の高さマップを反転して法線マップを計算したものを使用させていただいております。

約8万ポリゴン
displacement_001.png

111ポリゴン
displacement_003.png

十分ハイポリならちゃんと凹凸しますが、頂点ベースのディスプレイスメントマッピングでは、頂点しか移動できませんので、ローポリだと全然出っ張りません。そこで、視差遮蔽マッピングなど、ピクセルベースでのディスプレイスメントマッピングが色々提案されているようです。資料としては[2]が充実していました。

で、時代はテッセレーション+ディスプレイスメントマッピングということのようなので、[3]のソースコードを色々変更してTessellation + Displacement mapping + Bump mappingを試してみました。分割した頂点の法線ベクトルをどう計算すればいいのかがよく分からなかったので、もうバンプマッピングでやってしまえばいいか、ということにしています。

基本的には、テクスチャで法線マップと高さマップ、VAOで法線だけでなく接線ベクトルも渡すようにして、TES内で高さマップから頂点を移動させたりバンプマッピング用に視線ベクトルや光原ベクトルを回転させたりして、フラグメントシェーダ内で法線マップから法線ベクトルを取得して色出力をしました。

また、TCSの中のforループで値代入しているだけなのに全部0になっていて、forループ使わずに代入したらちゃんと動いたり、良くわからないことも色々ありました。

その他参考文献として[4][5]などがあります。ジオメトリシェーダで法線ベクトルを計算すればいけるかなと思ったのですが、3頂点からだと面の法線ベクトルしか計算できないようでしたので、とりあえず諦めました。ジオメトリシェーダで頂点法線を求める方法もあったはずだとは思います。

実行結果

111ポリゴンのものを分割しています。流石に少なすぎますが、1000ポリゴンぐらいあれば今回の対象では問題ないレベルになると思います。対象や法線マップと高さマップの解像度などにもよると思いますが。

レベル1
displacement_005.png
displacement_006.png

レベル8
displacement_007.png
displacement_008.png

レベル32
displacement_009.png
displacement_010.png

レベル64
displacement_011.png
displacement_012.png

このPCでは64が最大レベルみたいでした。ここまで分割すると元が荒いポリゴンでもワイヤフレーム表示でほぼ隙間がなくなるほどですね。あと面倒だったので光原計算はかなり適当でちゃんとしたバンプマッピングになっていないと思います。

[1] http://marina.sys.wakayama-u.ac.jp/~tokoi/?date=20051014
[2] Displacement Mapping on the GPU - State of the Art
[3] Tutorial 31 - PN Triangles Tessellation
[4] GLSL Tessellation Displacement Mapping
[5] OpenGL でテセレーションのシェーダー(TCS,TES)を使う
web拍手 by FC2
posted by シンドラー at 15:08 | Comment(0) | TrackBack(0) | OpenGL & Metasequoia | このブログの読者になる | 更新情報をチェックする

2014年10月27日

Normal Matrix についてのメモ

最近少しOpenGL 4.3辺りを試しているのですが、GLSLも結構変わっていますね。gl_ModelViewMatrixとかgl_NormalMatrixとか廃止?非推奨?になってるんですね。そういうわけで、uniform変数として渡す必要があり、glmを使ってModelViewProjection行列などは比較的簡単に作れたのですが、gl_NormalMatrixって何?って感じでした。調べてみると、ModelView行列の左上3x3の逆行列の転置で良いと書いていて、なんでそんな面倒な計算がいるのかなと思ったのですが、[1]などに説明がありました。

接線ベクトルと法線ベクトルの内積は本来0(直交)ですが、Model行列にスケーリングの要素が入っているなど、ModelView行列の3x3成分が直交基底になっていない場合は、内積が0にならなくなります。そのようなわけで、この3x3の行列を法線ベクトルにかけた後も法線ベクトルと接線ベクトルが直交するように導いていくと、最終的に逆行列を計算してから転置するということになるみたいです。

また、LookAt関数で計算したView行列は直交行列で、Model行列も回転と平行移動しかなければ直交行列なので、そういった場合は単純にModelView行列の3x3成分を使ったので構わないということのようです。

実行結果

トーラスに対してX軸方向に2倍するModel行列を使用する場合で試してみました。

ModelView行列の3x3をそのまま使った場合

NormalMatrix_002.png NormalMatrix_004.png

逆行列を計算して転置した場合

NormalMatrix_003.png NormalMatrix_005.png

ちゃんとしたNormal Matrixを使わないと周辺が暗くなってしまっていますね。スケーリングやせん断等はあまり使わないかもしれませんが。まぁこれでgl_NormalMatrixが使えなくても大丈夫ですね。

[1] http://www.lighthouse3d.com/tutorials/glsl-tutorial/the-normal-matrix/

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

2014年10月24日

GSLを用いた球面調和関数の計算 その3

[1]の球面調和関数の説明に、いびつなウニという言葉が使われていていい表現だなと思いました。というわけで、いびつなウニを適当に表示してみることにしました。

入力画像として、[2]を使用させていただいております。[2]は"Photo by DAVID ILIFF. License: CC-BY-SA 3.0"ですので、加工したこのページの結果画像も同じライセンスになります。

実行結果

原画像から点をサンプリングして、明るさ(HSVのV)を中心からの距離にして描画したものです。カラーバッファをクリアせずに毎フレーム40000点ずつGL_POINTSで色はglColor4f(テクスチャのRGB,α=0.2);でやってます。ウニというより爆発っぽいですね。

sh_point_001.png

以下、これを球面調和関数で圧縮して適当な次数で展開したものを同様の方法で描画したものです。係数はRGBでやっていますので、実際には×3になります。

sh_point_002.png
maxL=0, 係数1個。ただの球です。

sh_point_003.png
maxL=1, 係数4個

sh_point_004.png
maxL=2, 係数9個

sh_point_005.png
maxL=3, 係数16個

sh_point_006.png
maxL=4, 係数25個

sh_point_007.png
maxL=5, 係数36個。maxLが3〜5ぐらいで十分と[1]には書かれていますね。

sh_point_008.png
maxL=6, 係数49個

sh_point_009.png
maxL=7, 係数64個

sh_point_010.png
maxL=8, 係数81個

sh_point_L20.png
maxL=20, 係数441個。このぐらいになるととげとげしすぎて遠ざかっている気がしますね…。遮蔽情報との内積を取る分には問題ないのでしょうか…。やっぱり3〜5ぐらいでいいのかもしれません。

[1] マイナビ:3Dグラフィックス・マニアックス 第68回
[2] WikipediaのPanorama画像
web拍手 by FC2
posted by シンドラー at 19:26 | Comment(0) | TrackBack(0) | OpenGL & Metasequoia | このブログの読者になる | 更新情報をチェックする

2014年10月18日

点群やボリュームデータのポリゴン化について

球面調和関数などをモンテカルロ的な手法で計算した場合、基本点群ですので、可視化する場合点群をポリゴン化したいと思うと思います。以前は普通のマーチングキューブス法を使っていたのですが、その辺りの点群やボリュームデータからのポリゴン生成について調べていたら、Smooth Signed Distance[1]というソフトウェアがありました。昔試した気がする符号付き距離(Signed Distance)を使って等値面をポリゴン化するのだと思います。手法としてはDual Marching Cubes法などを使っているみたいです。おそらく。

[1]にも書いてありますが、CMakeを使えばVisual Studioでも簡単にコンパイルできました。サンプルの点群がPlyファイルで用意されており、コマンドラインで変換してMeshLabで表示したところ、少ない点群でもある程度の再構築ができているようでした。
というわけで、偏りのない球面上の点40000点をPlyファイルとして出力して、それを再構築してみました。点と法線ベクトルもいるみたいですので、球面調和関数の場合、法線ベクトルって簡単に計算できるのでしょうかね。中心からその点へのベクトルでも別にいいのかもしれませんが。

実行結果
ssd_recon.exe 変換前.ply 変換後.ply

変換前の点群
PolyVox_002.png

変換後のポリゴン
PolyVox_003.png PolyVox_004.png

Plyのヘッダ部分は下記のようになって、後は頂点座標と法線ベクトルを出力するだけですので、簡単に変換前のPlyを作成することができます。
  ply
format ascii 1.0
element vertex 40000
property float x
property float y
property float z
property float nx
property float ny
property float nz
end_header
0.127388 0.991853 0.000542 0.127388 0.991853 0.000542
...
今回表面の点だけでしたが、中身が詰まっている場合などはどうなるんでしょうかね。基本Kinect等で取得した表面点群だけなのでしょうか。

その他気になったライブラリとして、PolyVox[2]というzlibライセンスのものがあったので少し試してみることにします。

1. ダウンロードとビルド
[3]のダウンロードからリポジトリをダウンロードできます。ソースをチェックアウトしてもいいかと思います。次に、CMakeを使ってビルドします。便利ですねCMake GUI。

2. 使用するための準備
libraryというフォルダ内に、PolyVoxCoreとPolyVoxUtilというフォルダがあり、そこにヘッダファイルなどが入っているようです。ビルドしたらPolyVoxCore.libとPolyVoxUtil.libが生成されていると思います。おそらくCMakeの設定等でDLL出力もできるのではないかと思いますが、未確認です。
追加のインクルードディレクトリを指定して、PolyVoxCore.libとPolyVoxUtil.libをリンカに追加すれば準備完了です。

3. 球のテスト
ドキュメントのユーザマニュアルのTutorial 1 - Basic useに解説があります。ライブラリのサンプルプログラムにも色々含まれていますので、基本的にはそれを参考にすれば良いかと思います。

中心からの距離に応じて0か255の値に設定し、表面ポリゴンを生成するようです。この際、ボクセルの最大値の半分の値(今回は255の半分で127)を閾値とした表面ができるようです。このサンプルではCubicSurfaceExtractorWithNormalsを使用していてキューブベースの表面となっていますが、MarchingCubesSurfaceExtractorを使うと、マーチングキューブス法で生成されたもう少し滑らかな表面ができるようです。

BasicDemoでは、Qtを使ってレンダリングしていますが、OpenGLのサンプルにはOpenGLだけを使ったサンプルもあったと思います。今回は自分のプログラムに組込んでみます。

実行結果

PolyVox_001.png

点群しかない場合、結局ボリュームデータをどう作るかという問題になってしまいますので、ポリゴン化だけしたいのであれば前者でコマンドラインでというのでいいのかもしれません。

[1] http://mesh.brown.edu/ssd/software.html
[2] http://www.volumesoffun.com/polyvox-about/
[3] https://bitbucket.org/volumesoffun/polyvox/
web拍手 by FC2
posted by シンドラー at 02:09 | Comment(0) | TrackBack(0) | OpenGL & Metasequoia | このブログの読者になる | 更新情報をチェックする