【何の役に立つか分からない技術コーナー】
Processing で 3D を扱っていて、地味に不便だなぁと感じるのが、カメラ行列(射影行列ではない)に直接アクセスできない事。
printCamera() メソッドでカメラ行列を
コンソールに表示する事は可能だけど、これを眺めるだけでカメラの並進量や回転量を把握するのは、残念ながら普通の人には難しい。
加えて、そのカメラ行列をプログラム内で使う事は(現時点では)できないようである。
これができないと、たとえばデバッグカメラ(カメラ配置を第三者視点から確認する事)が多少やりづらい(工夫次第でなんとかなりそうだが)。
そこでひとまず、Processing 標準の
camera() メソッドをエミュレートするものを作ってしまう事にした。
以下に当該メソッドのソースコードを示す。使い方は
camera() メソッドと基本的に一緒。
// 世界座標を、カメラに合わせて動かす
// 引数の意味は、camera()メソッドと同じ
void worldToCamera() {
worldToCamera(
width/2.0, height/2.0, (height/2.0) / tan(PI*60.0 / 360.0),
width/2.0, height/2.0, 0,
0, 1, 0);
}
void worldToCamera(float eyeX, float eyeY, float eyeZ,
float centerX, float centerY, float centerZ,
float upX, float upY, float upZ) {
// カメラ座標空間の3軸
PVector r3 = new PVector(eyeX-centerX, eyeY-centerY, eyeZ-centerZ);
PVector r2 = new PVector(upX, upY, upZ);
PVector r1 = r2.cross(r3);
r2 = r3.cross(r1);
// 正規化
r1.normalize();
r2.normalize();
r3.normalize();
/* ================================================== */
/* 各軸まわりの回転量、および並進量を個別に求める例 */
/* オイラー角表現はヘディング・ピッチ・バンクに準ずる */
/* ================================================== */
float h, p, b;
p = asin(-r2.z);
if (cos(p) != 0) {
h = atan2(r1.z / cos(p), r3.z / cos(p));
b = atan2(r2.x, r2.y);
} else {
b = 0;
h = atan2(-r3.x, r1.x);
}
resetMatrix();
rotateY(h); // ヘディング角
rotateX(p); // ピッチ角
rotateZ(b); // バンク角
translate(-eyeX, -eyeY, -eyeZ);
/* ================================================== */
// 別解:applyMatrix による一括変換の例
/* ==================================================
// 並進成分の計算
PVector t = new PVector(
-(eyeX * r1.x + eyeY * r1.y + eyeZ * r1.z),
-(eyeX * r2.x + eyeY * r2.y + eyeZ * r2.z),
-(eyeX * r3.x + eyeY * r3.y + eyeZ * r3.z)
);
// 変換行列の作成・適用
resetMatrix();
applyMatrix(r1.x, r1.y, r1.z, t.x,
r2.x, r2.y, r2.z, t.y,
r3.x, r3.y, r3.z, t.z,
0.0, 0.0, 0.0, 1
);
================================================== */
}
このままでは
camera() と一緒だけど、たとえば
MyCamera クラスとかで包んでやって、カメラの並進量・回転量(あるいは行列そのもの)に対するアクセサを定義してやれば、もうちょっと高度なカメラ制御が可能になると思う。