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 クラスとかで包んでやって、カメラの並進量・回転量(あるいは行列そのもの)に対するアクセサを定義してやれば、もうちょっと高度なカメラ制御が可能になると思う。
【テスト】
適当な視点から箱を俯瞰するサンプルプログラムを作る。ただし、カメラの設定には、既存の camera() メソッドと新たに作った worldToCamera() メソッドを用意し、どちらを使用するかをマウスで切り替えられるようにした(デフォルトでは worldToCamera() メソッドが、マウスを押しているときだけ camera() メソッドが使用される)。

ソースコードは以下の通り。
- int boxSize = 90; // 箱の大きさ
- int nBox = 10; // 箱の数
- int mergine = 50; // 余白
- void setup() {
- size(800, 600, P3D);
- }
- void draw() {
- background(0);
- if(mousePressed) {
- // 既存の関数
- camera(800, -500, 700, 0, 0, 0, 1, 1, 0);
- } else {
- // 新しく作った関数
- worldToCamera(800, -500, 700, 0, 0, 0, 1, 1, 0);
- }
- // 色を設定
- colorMode(HSB, nBox, 100, 100);
- // 適当に箱をならべる
- pushMatrix();
- translate(-(nBox * (boxSize + mergine))/2, 0, 0);
- for(int i = 0; i < nBox; ++i) {
- pushMatrix();
- stroke(i, 99, 99);
- fill(i, 99, 99, 100);
- translate(i * (boxSize + mergine), 0, 0);
- box(boxSize);
- popMatrix();
- }
- popMatrix();
- }
実際に走らせてみると両者に差はなく、きちんと camera() が再現されている事が判る。
これを応用すれば、既知のカメラ視点・注視点・鉛直軸より、並進・回転成分を抽出するメソッドを書く事も可能だろう。
で。
今日はちょっと忙しいのでここまで。中途半端でごめん。
【てきとう参考文献】
カメラの回転行列からオイラー角(ヘディング、ピッチ、バンク角)の回転量を取り出す箇所に関しては、Fletcher Dunn, Ian Parberry 著、松田 晃一訳『実例で学ぶ ゲーム 3D 数学』を参考にしたよ。
0 件のコメント:
コメントを投稿
ひとことどうぞφ(・ω・,,)