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

平面の方程式計算
// Ax+By+Cz+D=0の平面のパラメータを計算する関数
void getPlaneParam(const Vector3d v1, const Vector3d v2, const Vector3d v3, double param[4], Vector3d &normal)
{
    Vector3d e2, e3, n;
    double d;
    // 三角形の2辺のベクトルを計算
    e2 = v2 - v1;
    e3 = v3 - v1;
    // 外積で法線ベクトル(正規化なし)を計算
    n = e3.cross(e2);
    // 平面の方程式のDを計算
    d = -v1.dot(n);
    // 平面のパラメータを設定
    param[0] = n[0];
    param[1] = n[1];
    param[2] = n[2];
    param[3] = d;
    // ついでに法線ベクトル(正規化)を計算
    normal = n.normalized();
}

左回り/右回り判定(2次元用)
// 注意!2次元用。外積を計算し符号で判定
bool checkCCW(const Vector3d v1, const Vector3d v2, const Vector3d v3)
{
    double S = 0;
    S += v1.x() * v2.y() - v2.x() * v1.y();
    S += v2.x() * v3.y() - v3.x() * v2.y();
    S += v3.x() * v1.y() - v1.x() * v3.y();
    // 軸の取り方で符号が変わる(通常はS>0の時左回り?)
    if (S > 0)
    {
        return false;
    }
    return true;
}

ラスタライザ(ライセンスなどは未確認)
// 
// http://forum.devmaster.net/t/advanced-rasterization/6145
// 改変
void rasterize(const Vector3d v1, const Vector3d v2, const Vector3d v3, MatrixXd &DepthBuffer)
{
  // 28.4 fixed-point coordinates
  const int Y1 = round(16.0 * v1.y());
  const int Y2 = round(16.0 * v2.y());
  const int Y3 = round(16.0 * v3.y());

  const int X1 = round(16.0 * v1.x());
  const int X2 = round(16.0 * v2.x());
  const int X3 = round(16.0 * v3.x());

  // Deltas
  const int DX12 = X1 - X2;
  const int DX23 = X2 - X3;
  const int DX31 = X3 - X1;

  const int DY12 = Y1 - Y2;
  const int DY23 = Y2 - Y3;
  const int DY31 = Y3 - Y1;

  // Fixed-point deltas
  const int FDX12 = DX12 << 4;
  const int FDX23 = DX23 << 4;
  const int FDX31 = DX31 << 4;

  const int FDY12 = DY12 << 4;
  const int FDY23 = DY23 << 4;
  const int FDY31 = DY31 << 4;

  // Bounding rectangle
  int minx = (tmpMin3(X1, X2, X3) + 0xF) >> 4;
  int maxx = (tmpMax3(X1, X2, X3) + 0xF) >> 4;
  int miny = (tmpMin3(Y1, Y2, Y3) + 0xF) >> 4;
  int maxy = (tmpMax3(Y1, Y2, Y3) + 0xF) >> 4;

  // 直線の方程式
  double param[4];
  Vector3d normal;
  getPlaneParam(v1, v2, v3, param, normal);
  // param[0]*x + param[1]*y + param[2]*z + param[3] = 0
  // z = -(param[0]*x + param[1]*y+param[3])/param[2];
  // z0 = -(param[0]*minx+param[1]*miny+param[3])/param[2];

  // 点(minx, miny)におけるz座標(z0)を計算
  const double z0 = -(param[0] * minx + param[1] * miny + param[3]) / param[2];
  // x及びy方向に移動した場合のzの変化量を計算
  const double dzdx = -param[0] / param[2];
  const double dzdy = -param[1] / param[2];
  // z = z0+dzdx*(x-minx)+dzdy*(y-miny);

  // Block size, standard 8x8 (must be power of two)
  const int q = 8;

  // Start in corner of 8x8 block
  minx &= ~(q - 1);
  miny &= ~(q - 1);

  // Half-edge constants
  int C1 = DY12 * X1 - DX12 * Y1;
  int C2 = DY23 * X2 - DX23 * Y2;
  int C3 = DY31 * X3 - DX31 * Y3;

  // Correct for fill convention
  if (DY12 < 0 || (DY12 == 0 && DX12 > 0)) C1++;
  if (DY23 < 0 || (DY23 == 0 && DX23 > 0)) C2++;
  if (DY31 < 0 || (DY31 == 0 && DX31 > 0)) C3++;

  // Loop through blocks
  double z;
  for (int y = miny; y < maxy; y += q)
  {
    for (int x = minx; x < maxx; x += q)
    {
      // Corners of block
      int x0 = x << 4;
      int x1 = (x + q - 1) << 4;
      int y0 = y << 4;
      int y1 = (y + q - 1) << 4;

      // Evaluate half-space functions
      bool a00 = C1 + DX12 * y0 - DY12 * x0 > 0;
      bool a10 = C1 + DX12 * y0 - DY12 * x1 > 0;
      bool a01 = C1 + DX12 * y1 - DY12 * x0 > 0;
      bool a11 = C1 + DX12 * y1 - DY12 * x1 > 0;
      int a = (a00 << 0) | (a10 << 1) | (a01 << 2) | (a11 << 3);

      bool b00 = C2 + DX23 * y0 - DY23 * x0 > 0;
      bool b10 = C2 + DX23 * y0 - DY23 * x1 > 0;
      bool b01 = C2 + DX23 * y1 - DY23 * x0 > 0;
      bool b11 = C2 + DX23 * y1 - DY23 * x1 > 0;
      int b = (b00 << 0) | (b10 << 1) | (b01 << 2) | (b11 << 3);

      bool c00 = C3 + DX31 * y0 - DY31 * x0 > 0;
      bool c10 = C3 + DX31 * y0 - DY31 * x1 > 0;
      bool c01 = C3 + DX31 * y1 - DY31 * x0 > 0;
      bool c11 = C3 + DX31 * y1 - DY31 * x1 > 0;
      int c = (c00 << 0) | (c10 << 1) | (c01 << 2) | (c11 << 3);

      // Skip block when outside an edge
      if (a == 0x0 || b == 0x0 || c == 0x0) continue;

      // Accept whole block when totally covered
      if (a == 0xF && b == 0xF && c == 0xF)
      {
        for (int iy = 0; iy < q; iy++)
        {
          for (int ix = x; ix < x + q; ix++)
          {
            // 直線上
            // z座標の値を計算
            z = z0 + dzdx*(ix - minx) + dzdy*(y + iy - miny);
            DepthBuffer(y + iy, ix) = z;
          }
        }
      }
      else // Partially covered block
      {
        int CY1 = C1 + DX12 * y0 - DY12 * x0;
        int CY2 = C2 + DX23 * y0 - DY23 * x0;
        int CY3 = C3 + DX31 * y0 - DY31 * x0;

        for (int iy = y; iy < y + q; iy++)
        {
          int CX1 = CY1;
          int CX2 = CY2;
          int CX3 = CY3;

          for (int ix = x; ix < x + q; ix++)
          {
            if (CX1 > 0 && CX2 > 0 && CX3 > 0)
            {
              // 内部
              z = z0 + dzdx*(ix - minx) + dzdy*(iy - miny);
              DepthBuffer(iy, ix) = z;
            }

            CX1 -= FDY12;
            CX2 -= FDY23;
            CX3 -= FDY31;
          }

          CY1 += FDX12;
          CY2 += FDX23;
          CY3 += FDX31;
        }
      }
    }
  }
}

posted by シンドラー at 18:28 | Comment(0) | TrackBack(0) | OpenGL & Metasequoia | このブログの読者になる | 更新情報をチェックする
この記事へのコメント
コメントを書く
お名前:

メールアドレス:

ホームページアドレス:

コメント:

認証コード: [必須入力]


※画像の中の文字を半角で入力してください。
※ブログオーナーが承認したコメントのみ表示されます。
この記事へのトラックバックURL
http://blog.seesaa.jp/tb/438475189
※ブログオーナーが承認したトラックバックのみ表示されます。

この記事へのトラックバック