でらうま倶楽部

バカってゆうか、ゲームを作る事しか能の無いプログラマの、面白おかしな日々を綴ってみる実験。

2014年12月

2014年を振り返る

photo

コード書いとる??

今年もあと残り少ないですね。

ちょっと油断した隙に東京オリンピック2020が始まっていそうな勢いで時間が進んでます。

という訳で、手元にある今年のメモ帳片手に今年一年を振り返ってみるよ。

photo2

今年は友人知人らと楽器演奏する機会に恵まれました。2013年末に始めた友人らとの楽器練習会もほぼ毎月一回続いて一周年。んで、10Wくらいのアンプ買ったりエフェクター買ったりと、楽器機材もいろいろと出入りのあった一年でした。MIKU STOMPも買ったしね!! 笑

日本電子専門学校の方は、去年急遽請け負ったコンピュータミュージック科の代講→正式講義と変更になり、なんと人生初の「成績をつける」という経験もしました。これまでアシスタント的な立場だったので、また違った責任を感じています。ゲーム制作科の方は相変わらずアシスタント的な立場ですが、以前より教え子たちの距離を縮めようと努めています。

4月からはバンタンゲームアカデミーでもゲームプログラミングてきな内容を教える事になりました。こちらは週6時間×2クラス。完全に実習内容を任される形です。なので、これまでの自分の経験を踏まえつつ、教え子たちに「無茶振り」をして色々と経験してもらってます。

読書の方は小説・技術書合わせて27冊。去年よりややペースが落ちたのは、行きの電車が混雑してて本を開けないのが原因です。「読む量減ったなぁ…」と感じておるので、何か対策を考えねば。


そして何より、先輩、元同僚、知人、友人にとても助けられた一年でした。「ちょいと面白げな案件があるんだけど…」と相談してくれ、「専門学校で講師します??」と紹介してくれ、「一緒に楽器練習しよう!!」と誘ってくれ、「ライブ観に行くよ!!」と声を掛けてくれ、「皆んなで飲もうぜ!!」と一席設けてくれ、「皆んなで飲もうぜ!!」と一席設けてくれ、いつも何かしら気にかけてもらっているというのは、とてもありがたいことですね。少しでも恩返しできるよう、自分も主催していかねば!!!!


さて以下月ごとのイベントなんかをつらつらと。

2月、友人に誘われて、武道館で催された声優ユニット「スフィア」のライブを観たのも今年初体験。以前、武道館でプロレスを観た経験はあったのですが、ライブは初。ペンライトを振ったのも人生初!! 笑。観客の声援の迫力とか、ライブならでは。いや〜楽しかった!! 別の友人に誘われて行った横浜の古典楽器演奏会は吹雪いてて大変だったw でも、教会に響くチェンバロの音色はほんとうに素晴らしく、また聴きたいですな。

3月は、バイク好きな先輩らとモーターサイクルショーへ行ってきました。自分はバイク乗らないんですがめっちゃ楽しい!! BMWとかDUCATIのバイクにまたがって「ふむ…」とかしてた。 広い会場がすっごい混雑してて、「バイク好きな人も多いんだな〜」という発見もありました。

4月に自宅から歩いて行ける距離にIKEAができたり、12月には歩いて行ける距離にコメダができたりと、ますます引っ越す理由がなくなっていってて困っています。だが中目黒はさすがに遠いw

photo1

5月はBoost勉強会@札幌に参加。人生初羽田→初札幌!! どの発表もとても興味深く、また、お会いしたいと思っていた方々にもお会いできて、とても素晴らしい旅となりました。私はこれからも「ギター片手に発表」を推進していきたいと思います(キリッ

6月には、とてもお世話になっている床井先生と再会できて色々とお話できました。私の先輩も紹介できました!! ビバOpenGL!! (違) 専門学校の講師の方が音楽を担当される舞台も観てきた。独特の響きの音楽に心奪われつつ、その伴奏のほとんどがアドリブと聞いてビックリ。

7月、専門学校の課外授業に便乗して日本科学未来館へ行ってきました。教え子たちとキャッキャウフフしながらVR技術を満喫してきた(ェ) あとで「こんなに楽しそうで賑やかな見学会は初めてでしたよ」と言われて嬉しいやら申し訳ないやら…(汗)

8月には知人に誘っていただき、念願のすきやばし次郎でお寿司!! 丁寧な職人の仕事を前に、言葉がありません。帰り際次郎さんと握手してもらった!!!! 知人が参加している楽器練習会にもお邪魔して、人前でギター演奏を披露。みんなで演奏する楽しさを発見しました。

photo

9月は「レイトレ合宿」に参加。なにげに人生初レイトレ!! これまで「なんとなく」使っていた技術を自分で実装してみて初めて気づく点も多く、やはりC++は最高だなと改めて思うのであった。 そんで、荻窪で催されたBBQに参加して、150人くらいの方々がお肉を焼いてる前でギター演奏を披露してきた。手拍子までもらってしまって感激… 東京ゲームショウでは立派になった教え子たちに再会できてこれまた感激。んで、そのギターをメンテナンスに出してみました。買って5年目で初めてのメンテナンス!! 笑 なんと、自宅と駅のちょうど中間に、メンテナンスショップがあるんだよね。 ますます引越しできーせん…

photo

10月、ずっと前から参加したかった元同僚らの「餃子の会」についに参加できた!! 4〜5年ぶりくらいの再会!! 飯田橋のお店で巨大餃子をつつきながら近況を報告しあったり。みんな元気で何よりだ。

11月は、友人に誘われて人生初豊洲ららぽーと。そーゆー施設には縁がないワタクシ(笑) 声優ユニット「スフィア」のフィルムライブだったのですが、都内でいちばん大きなスクリーンで上映されるライブ映像は、まるで目の前で本当にライブを演ってる感じでしたん。みんなでペンライト振ったよ!!

12月は、ちょいちょい参加してる「ひげひげ団」の年末飲み会で、ギャラクシアンやポールポジションのドット絵を担当されてた小野さんから、当時の話を伺えて感極まっておりました。そして、次の日にはなんと20年振りに、私の最初の上司、内藤さんと再会を果たせました!!!! 内藤さん、お仕事の関係でこちらに引っ越されてたんですね。ハイドライドの話からE.O.イマジネーションの話から、20年という時間を埋めるように盛り上がったのでした。


とまあ駆け足で2014年を振り返ってみました。今年一年は、講師業と楽器練習で充実していた反面、プログラム量が減ってしまいました。次回は両立…とまではいかなくても、プログラム量を減らさない方向で他のパラメーターを鍛えたいですね。

悲しいことや辛い事にも出会いますが、捉え方次第でなんとかなるもんだ。「起きたことは不幸でも、やれることは幸せですよ」という言葉を胸に、来年も色々なことに挑戦し続けていきたいと思っています。


では、みなさんも、よいお年を!!

Eigen - C++で使える線形代数ライブラリ

Unknown
C++ Advent Calender 2014 絶賛協賛中!!

みなさん、Eigenをご存知ですか?? EigenはC++のテンプレートで実装された線形代数ライブラリです。

私自身、配信中の2本のiPhoneアプリで使っており、「これはとても使いやすい!!」と感じています。

その経験から、「C++でベクトル・行列を扱うなら、Eigenオススメ!!」と推しまくってます!!!!

使うメリット
  • ライブラリのビルドが不要(使いたいソースでインクルードするだけ)
  • 高速(テンプレートが展開され、余計な変数が生成されない)
  • 直感的でシンプルなAPI(数式に近いコードが書ける)
  • MPL2なライセンス(closed-source なソフトでも使える)
そこで今回は、簡単ながら、ゲームアプリでよく使うであろうベクトル・行列計算を中心にEigenの使い方を紹介していきますね。

最初のコード
Eigenには数多くの定義済みベクトルと行列があります。ゲーム内の座標変換で使うならば、以下の命名法則で用意されたものを使うとよいでしょう
  • ベクトル
    Eigen::Vector[次元数][i, f, d]
    Eigen::Vector2f → float型の二次元ベクトル
    Eigen::Vector3d → double型の三次元ベクトル
    Eigen::Vector4i → int型の四次元ベクトル
  • 行列
    Eigen::Matrix[行と列の数][i, f, d]
    Eigen::Matrix3f → float型の3x3行列
    Eigen::Matrix4d → double型の4x4行列 
#include <iostream>

// Eigenのコア機能とジオメトリ機能を使う
#include <Eigen/Core>
#include <Eigen/Geometry>


int main() {
  // 以下サンプルコードでは名前空間Eigenを省略
  using namespace Eigen;

  // 二次元ベクトルv1の定義。初期値は未定義
  Vector2f v1;

  // 初期値はコンストラクタで指定できる
  Vector2f v2(1.0f, 0.5f);
  Vector3f v3(0.0f, 1.0f, -1.0f);

  // 内容を表示
  std::cout << "v2\n" << v2 << std::endl;
  std::cout << "v3\n" << v3 << std::endl;

  // double型3x3行列の定義。初期値は未定義
  Matrix3d m;

  // 行列の初期値はコンストラクタで指定できないが
  // 値は以下のようにまとめて代入できる
  m << 1.0, 0.0, 0.0,
       0.0, 1.0, 0.0,
       0.0, 0.0, 1.0;

  std::cout << "m\n" << m << std::endl;
}  

ベクトル操作
加減算、内積、外積、正規化など一通りの計算はこんな感じに書けます

#include <iostream>
#include <Eigen/Core>
#include <Eigen/Geometry>

int main() {
  using namespace Eigen;

  // あらかじめ用意されている値
  // 単位ベクトル(1, 0, 0)
  Vector3f v;
  v = Vector3f::Identity();

  // (0, 0, 0)
  v = Vector3f::Zero();

  // (1, 1, 1)
  v = Vector3f::Ones();

  // (1, 0, 0)
  v = Vector3f::UnitX();

  // (0, 1, 0)
  v = Vector3f::UnitY();

  // (0, 0, 1)
  v = Vector3f::UnitZ();

  // ランダムな値
  // FIXME:std::rand()を使っている
  v = Vector3f::Random();

  // 定数
  v = Vector3f::Constant(0.5f);


  // 各要素への読み書き
  float x, y, z;
  x = v.x();
  y = v.y();
  z = v.z();

  v.x() = x;
  v.y() = y;
  v.z() = z;

  // こういう書き方もできる
  v(0) = x;
  v(1) = y;
  v(2) = z;

  // []の場合は添え字の正当性をチェックしない
  x = v[0];
  y = v[1];
  z = v[2];

  // こういう代入も可能
  v << 0.5f, 1.2f, -2.0f;

  // コピー
  Vector3f v1;
  v1 = v;

  // 加算・減算
  Vector3f v2;
  v2 = v + v1;
  v2 = v - v1;

  // 乗算・除算(ベクトル同士の乗算、除算は無い)
  v1 = v * 2.0f;
  v1 = v / 2.0f;

  // 内積・外積
  float dot = v.dot(v1);
  Vector3f cross = v.cross(v1);

  // ベクトルの長さ
  float norm = v.norm();

  // 正規化(長さ1)したベクトルを生成
  Vector3f n = v.normalized();

  // ベクトルを正規化する
  v.normalize();
}


行列操作
ベクトルとほぼ同じ感じに操作できます

#include <iostream>
#include <Eigen/Core>
#include <Eigen/Geometry>

int main() {
  using namespace Eigen;

  Matrix3f m;
  
  // あらかじめ用意された値
  m = Matrix3f::Identity();
  m = Matrix3f::Zero();
  m = Matrix3f::Ones();
  m = Matrix3f::Random();
  m = Matrix3f::Constant(0.25f);

  // 行列の要素へのアクセス
  // matrix(列, 行)
  // 「行」が横方向のインデックス
  // 「列」が縦方向のインデックス
  // m2[0, 1] や m2[0][1] などの書き方はできない
  m(2, 0) = 1.0f;
  float value = m(0, 2);

  Matrix3f m1;
  m1 << 0.0f, 0.0f, 1.0f,
        0.0f, 0.0f, 1.0f,
        0.0f, 1.0f, 0.0f;
  
  // 加算・減算
  Matrix3f m2;
  m2 = m + m1;
  m2 = m - m1;

  // 乗算・減算
  m2 = m * 2.0f;
  m2 = m / 2.0f;
  
  // 逆行列
  m2 = m.inverse();

  // 共役行列
  m2 = m.conjugate();

  {
    // 4x4行列の一部を切り取って3x3行列へコピー
    // | a b c d |    | a b c |
    // | e f g h | -> | e f g |
    // | i j k l |    | i j k |
    // | m n o p |
    Matrix4f m_in;
    m_in <<  0.0f,  1.0f,  2.0f,  3.0f,
             4.0f,  5.0f,  6.0f,  7.0f,
             8.0f,  9.0f, 10.0f, 11.0f,
            12.0f, 13.0f, 14.0f, 15.0f;

    Matrix3f m_out = m_in.block(0, 0, 3, 3);

    // 4x4行列の一部を切り取ってベクトルへコピー
    // | a b c d |
    // | e f g h | -> | g k o |
    // | i j k l |
    // | m n o p |
    Vector3f v_out = m_in.block(1, 2, 3, 1);
  }

  // 転置行列
  // | a b c |    | a d g |
  // | d e f | -> | b e h |
  // | g h i |    | c f i |
  m2 = m.transpose();
}


要素ごとの演算
「すべての要素に加算」などの操作も可能です

#include <iostream>
#include <Eigen/Core>
#include <Eigen/Geometry>

int main() {
  using namespace Eigen;

  {
    Vector3f in(0.0f, 0.5f, 1.0f);

    // 各要素に対して加算・減算(行列も同じ操作ができる)
    Vector3f out = in.array() + 1.0f;
  }

  {
    Matrix3f in;
    in << 0.0f, 1.0f, 2.0f,
          3.0f, 4.0f, 5.0f,
          6.0f, 7.0f, 8.0f;

    // sin、cos、min、maxなど各種演算が用意されている
    // 詳細は公式のリファレンスマニュアル、Arrayクラスのメンバ関数を参照
    Matrix3f out = in.array().sin();

    Matrix3f m;
    m << 0.0f, 0.0f, 1.0f,
         0.0f, 1.0f, 0.0f,
         1.0f, 0.0f, 0.0f;

    // 各要素同士の乗算・除算はこう書ける
    out = in.array() * m.array();
  }
}


ベクトルと行列のサイズの取得

#include <iostream>
#include <Eigen/Core>
#include <Eigen/Geometry>

int main() {
  using namespace Eigen;

  // 4行3列の行列を定義
  Matrix<float, 4, 3> m;

  // メンバ関数rowsが行、colsが列を返す
  std::cout << "Rows:" << m.rows() << " Cols:" << m.cols() << std::endl;
}


行列の「行」と「列」
Eigenは「行(row)」と「列(coulmn)」の各要素が以下のように格納されています。これを『「列」優先(coulmn major)』といいます

| a b c |
| d e f | -> { a, d, g, b, e, h, c, f, i }
| g h i |

これを『「行」優先(row major)』にする場合は、以下のサンプルコードのように自分で行列の定義を行います

#include <iostream>
#include <Eigen/Core>
#include <Eigen/Geometry>

int main() {
  using namespace Eigen;

  // int型、3x3、「行」優先の行列を定義
  typedef Matrix<int, 3, 3, RowMajor> Mat3i;

  // Eigenのデフォルトは「列」優先(ColMajorは省略できる)
  // typedef Matrix<int, 3, 3, ColMajor> Mat3i;

  Mat3i m;
  // 値の代入の並びは「行」優先、「列」優先とは関係無い
  m << 1, 2, 3,
       4, 5, 6,
       7, 8, 9;

  // 「行」優先の場合は 1, 2, 3, 4, 5, 6, 7, 8, 9
  // 「列」優先の場合は 1, 4, 7, 2, 5, 8, 3, 6, 9 となる
  for (int i = 0; i < m.rows() * m.cols(); ++i) {
    std::cout << m(i) << " ";  
  }
  std::cout << std::endl;
}

OpenGLは列優先、DirectXが行優先ですが、どちらの状況にも即対応できますね。

そして素晴らしい事に、Eigenでは列優先と行優先が混在した状況でも計算ができます。


クォータニオン
Eigenにはクォータニオンの定義もあります

#include <iostream>
#include <Eigen/Core>
#include <Eigen/Geometry>

int main() {
  using namespace Eigen;

  // 変数を定義。値は未定義
  Quaternionf q1;

  // コンストラクタで値(w, x, y, z)を渡して初期化
  Quaternionf q2(1.0f, 0.0f, 0.25f, 0.5f);
  std::cout << q2.w() << "," << q2.x() << "," << q2.y() << "," << q2.z() << std::endl;

  // コンストラクタで角度とベクトルを渡して初期化
  Quaternionf q3(AngleAxisf(0.1f, Vector3f::UnitY()));

  // 2つのベクトルからクオータニオンを求める
  Quaternionf q4 = Quaternionf::FromTwoVectors(Vector3f::UnitX(), Vector3f::UnitZ());

  // 単位クオータニオン(wが1で他が0)
  q4 = Quaternionf::Identity();
  std::cout << q4.w() << "," << q4.x() << "," << q4.y() << "," << q4.z() << std::endl;

  // 乗算
  Quaternionf q_mul = q2 * q3;

  // 逆クオータニオン
  Quaternionf q_inv = q4.inverse();

  // 共役クオータニオンを求める
  Quaternionf q_conj = q4.conjugate();

  // 内積
  float dot = q3.dot(q4);

  // 回転ベクトルの長さ
  float norm = q3.norm();

  // 正規化
  q3.normalize();
  Quaternionf q_normalized = q4.normalized();

  // 球面線形補間
  // q3→q4を t[0, 1.0] で補間する
  float t = 0.5f;
  Quaternionf q_slerp = q3.slerp(t, q4);
}


アフィン変換
ベクトル、行列、クォータニオンを連携して使う事で、3D空間での座標変換が簡単に行えます。Eigenでは、アフィン変換用に定義された型(内部は4x4行列)を使って行います

#include <iostream>
#include <Eigen/Core>
#include <Eigen/Geometry>

int main() {
  using namespace Eigen;

  // 平行移動(x, y, z)
  Translation<float, 3> translation = Translation<float, 3>(10.0f, 0.5f, -3.0f);

  // スケーリング
  DiagonalMatrix<float, 3> scaling = Scaling(2.0f, 1.5f, 1.0f);

  // 回転(クォータニオン)
  Quaternionf rotate(AngleAxisf(0.2f, Vector3f::UnitY()));

  // アフィン変換用行列
  Affine3f matrix;

  // 色々掛け合わせて、変換行列を求める
  matrix = translation * scaling * rotate;

  // p0を変換行列で変換してp1に格納
  Vector3f p0(1.0f, -1.0f, 0.5f);
  Vector3f p1 = matrix * p0;

  Vector3f v1(1.0f, 0.0f, -1.0f);
  Vector3f v2;

  // 行列を介さずとも座標変換が可能
  // これがすごく便利
  v2 = translation * v1;
  v2 = scaling * v1;
  v2 = rotate * v1;
  v2 = translation * scaling * rotate * v1;

  // 汎用的な行列への変換にはメンバ関数matrixを使う
  Matrix4f& m = matrix.matrix();
}

「これでいけんじゃね??」って書き方を大抵は許容してくれるEigenかしこい。


2Dでの回転とか平行移動とか
Eigenには2D向けの座標変換も用意されています

#include <iostream>
#include <Eigen/Core>
#include <Eigen/Geometry>

int main() {
  using namespace Eigen;

  // 回転
  Rotation2Df rotate(3.1415f);

  // 平行移動
  Translation<float, 2> translation = Translation<float, 2>(10.0f, 0.5f);

  // スケーリング
  DiagonalMatrix<float, 2> scaling = Scaling(2.0f, 1.5f);

  // アフィン変換用の行列
  Affine2f m;
  m = rotate * translation * scaling;

  Vector2f v1(1.0f, 0.0f);
  Vector2f v2;
  v2 = m * v1;

  // 2Dの場合も横着して座標変換の計算をまとめて書ける
  v2 = rotate * translation * scaling * v1;
}


OpenGLやDirectXとの連携
OpenGLやDirectXでは、行列の値は「連続したメモリに並んだ16個の値」である必要があります。Eigenの場合はメンバ関数 data を使えばこれが実現できます。

#include <GL/gl.h>
// OSXの場合パスが違う
//#include <OpenGL/gl.h>
#include <Eigen/Core>
#include <Eigen/Geometry>

int main() {
  using namespace Eigen;

  // 汎用的な行列の場合
  Matrix4f m(Matrix4f::Identity());

  glPushMatrix();
  // メンバ関数dataで、行列の内容を const float* で取り出せる
  glLoadMatrixf(m.data());

  // 色々処理

  glPopMatrix();


  // アフィン変換行列の場合
  Affine3f affine(Affine3f::Identity());

  glPushMatrix();
  glMultMatrixf(affine.data());

  // 色々処理

  glPopMatrix();
}

DirectXの行列は『「行」優先(row major)』なので、先述した方法で行列を定義しておけばよいですよね。


参考サイト
私が実際にEigenを使ったiPhoneアプリ
「こなへん」第一回オリンピック開催地ってどこだっけ?? 地球をクルクル回して考えよー♪
  
「ういろう」名古屋銘菓たちの果てなき戦い!! ぽよジャンプアターック!!


線形代数Eigenクイックツアー、いかがでしたか?? ベクトル・行列の操作が、だいたい思ったように書けるのはとても素晴らしいですよね。何より、自分で実装するとけっこう大変だし!! 笑

是非みなさんの選択肢の一つに加えてみてください。

ではまた次回!!

先輩でもあるhira_kuniさんにバトンタッチ!!!!
記事検索
電子書籍発売中

「チュートリアル形式で始めるOpenAL」
サウンド怖くない。C++による8つのチュートリアルで始めるOpenALプログラミング。さああなたも、自作アプリに魅力的な音効を添えてみませんか??
⇒Kindle版 ⇒iBooks版


「iPhoneアプリ『ういろう』のレシピ」
ゲームってどうやって作ってるの?? 拙アプリ『ういろう』の製作過程を本にまとめました。もちろんソースコードつき
⇒Kindle版 ⇒iBooks版


『チュートリアル形式で始めるOpenGL[2D編]』
OpenGL怖くない。C++による16のチュートリアルで始めるOpenGLプログラミング[2D編]。さああなたも、ゲーム作りを始めてみませんか?
⇒Kindle版 ⇒iBooks版
自作ゲーム配信中

『Puzzle & Monarch』
「君主候補となって国作り!! ただし制限時間は90秒。」森を作って道をつないで...あなただけの国を作ってみませんか??
⇒AppStore


『BRICK & TRIP』
咄嗟の判断に、あなたの指先はついてこれるか?! 爽快フリックアクション!! 様々な難関をくぐり抜けて旅の終着点を目指そう!!
⇒AppStore


『ういろう』
名古屋土産ういろうがiPhoneで大活躍?! 白ういろうを守れるのはあなただけ。ひゅーん、ぼよよーん!!
⇒AppStore ⇒LITE版


『こなへん』
ヒマラヤ山脈、大西洋、世界で一番深い湖… それって地球のどこにあるのか知ってるかな?『全方位直感地理クイズ』という新ジャンルに挑戦!あ、それ。地球をくーるくるw
⇒AppStore ⇒LITE版


『GEOSPOT』
ヒマラヤ山脈、大西洋、世界で一番深い湖… それって地球のどこにあるのか知ってるかな?『全方位直感地理クイズ』という新ジャンルに挑戦!あ、それ。地球をくーるくるw
⇒Windows ⇒Mac


『TieGunner』
マウス片手に大宇宙へ飛び立とう!『しっぽシューティング』というジャンルを作って頂きました^^; WinでもMacでも動きます。ソースもあるでよw
⇒Windows ⇒Mac
QRコード
QRコード
  • ライブドアブログ