みなさん,こんにちは
おかしょです.
Processingで3Dアニメーションを作る時に視点を変更すると,アニメーションの幅が大きく広がります.
しかし,視点を変更すると2次元の図形や文字を同時に表示している場合,図形や文字が傾いて表示されてしまいます.
これは2次元のものがxy平面に書かれているためです.2次元で描いた図形や文字が傾いてしまうと,描いた意味が全くなくなってしまうので視点の変更に対応して表示ができるようにする必要があります.この記事では視点の変更に対応して2次元のものを表示する方法について解説します.
この記事を読むと以下のようなことがわかる・できるようになります.
- 3Dのアニメーションに2Dの図形を表示する方法
- 視点の変更に対応して2Dの図形を表示する
この記事を読む前に
この記事では視点の変更に対応する方法を解説しています.
その前に,以下の記事で3Dのアニメーションに2Dの図形を表示する方法を理解していることが前提となっているので,以下の記事を先に読んでおくことをおすすめします.
視点の変更に対応する方法
2次元のものを3次元のアニメーションに描画するには,z軸を無効化してz軸方向から見ることを想定されています.
そのため,視点を変更した時はz軸を視点の方向に向ける必要があります.
さらに,2次元で描画されるものはx軸が右向き,y軸が下向きとして描画されます.
なので,z軸を視点に向けるだけではなく,x軸を右向き,y軸を下向きにする必要があります.
軸を自由な方向に向けるには座標軸を回転させて調整します.
また,2次元のものはxy平面上に描画されます.つまり,z=0のところに描画されます.
なので,視点が遠ざかると図形や文字が小さく表示されてしまいます.このようなことにも対応するために図形の大きさにも気を付けなければなりません.
以上の2点を解決することで,視点の変更に対応して2次元のものを表示することができるようになります.
座標軸を回転させる
まずは座標軸を回転させて,z軸を視点の方向に向け,x軸を右に向け,y軸を下に向けます.
この座標軸の回転は空間把握能力が必要になるので,数学が苦手な方,特に空間図形が苦手だった方は難しいかもしれませんが,できるだけかみ砕いて説明するので頑張ってください.もしくは,プログラムを公開するのでそちらをコピペしてもいいかもしれません.
さて,座標軸の回転を行う場合はどの軸から回転させるのかが重要です.
回転させる軸の順番によって結果が変わってくるので注意しましょう.
軸を回転させる前に,回転させた後の目標を確認します.
軸を回転させる前の座標軸と視点の関係がこのようになっていたとします.視点は黄色い丸で表しています.
そして,座標軸を回転させた後の状態はこのようになります.
このような状態にするには,座標軸を2回回転させる必要があります.
まず,z軸方向に回転させて座標軸を以下のような状態にします.
ここで問題になるのが,回転させる角度\(\psi\)がどのくらいであるかです.
視点の座標が\((x, y, z) = (cameraX, cameraY, cameraZ)\)だったとすると,\(\psi\)は以下のようにして求められます.
$$ \psi = \tan^{-1} \frac{cameraX}{cameraY} $$
以上の式によって求められた\(\psi\)を使ってz軸周りに回転をさせます.
最後にx軸周りに回転させます.
この時の回転角度\(\phi\)は以下の式で求められます.
$$ \phi = \tan^{-1} \frac{\sqrt{cameraX^{2}+cameraY^{2}}}{cameraZ} $$
このような角度で回転させることで,視点が変化するのに対応して座標軸の向きも変化させることができます.
図形や文字の大きさを一定にする
座標軸の回転の次は視点の位置が離れても文字のサイズが変わらないようにします.
これは先ほどの座標軸の回転よりも簡単です.
座標軸を回転させたことによって,z軸は視点を向いています.
2次元の文字や図形はz=0のところに描画されるようになっているので,視点に対するz=0までの距離を一定に保てば良いのです.
例えば,常に視点から250離れた位置に2Dのものを表示したいのであれば,回転させた座標軸を以下の式によって求められる距離だけz軸方向に平行移動させます.
$$ z = \sqrt{cameraX^2+cameraY^2+cameraZ^2}-250 $$
座標軸の平行移動にはtranslateというコマンドを使用します.
これを座標軸を回転させた後にすることによって,2Dの図形や文字が視点が変更されても同じ大きさで表示されるようになります.
視点が変更されても2Dの図形を表示するプログラム
それでは,これまでに解説した方法を確かめるプログラムを以下で公開します.
import processing.opengl.*;
float cameraX, cameraY, cameraZ;
void setup()
{
background(0); // 背景の色指定黒に指定
size(displayWidth,displayHeight, OPENGL); // windowのサイズを画面いっぱいにする
translate(width/2, height/2, 0); // 原点を画面の中心に
cameraX = 0.0;
cameraY = 10.0;
cameraZ = 250.0;
camera(cameraX, cameraY, cameraZ, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0);
arrow(0, 0, 0, 150, 0, 0, 150, 0, 0);
arrow(0, 0, 0, 0, 150, 0, 0, 150, 0);
arrow(0, 0, 0, 0, 0, 150, 0, 0, 150);
}
void draw()
{
background(0); // 背景の色指定 黒に指定
if (keyPressed) {
if (keyCode == LEFT) cameraX -= 10;
if (keyCode == RIGHT) cameraX += 10;
if (keyCode == UP) cameraY -= 10;
if (keyCode == DOWN) cameraY += 10;
if (keyCode == CONTROL) cameraZ -= 10;
if (keyCode == SHIFT) cameraZ += 10;
}
camera(cameraX, cameraY, cameraZ, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0);
stroke(50);
for(int i=-500; i<=500;)
{
line(-500, i, 0, 500, i, 0);
line(i, -500, 0, i, 500, 0);
i += 20;
}
arrow(0, 0, 0, 150, 0, 0, 150, 0, 0);
arrow(0, 0, 0, 0, 150, 0, 0, 150, 0);
arrow(0, 0, 0, 0, 0, 150, 0, 0, 150);
// 2次元描画
pushMatrix(); // 座標系の保存
rotateZ(-atan2(cameraX, cameraY));
rotateX(-atan2(sqrt(sq(cameraX)+sq(cameraY)), cameraZ));
translate(0, 0, sqrt(sq(cameraX)+sq(cameraY)+sq(cameraZ))-250);
hint(DISABLE_DEPTH_TEST); // z軸を無効化
textSize(height*0.05); // 文字の大きさ
textAlign(CENTER); // 文字の座標指定位置
text(cameraX, 0, 100); // 使用センサ描画
text(cameraY, 0, 0); // 使用センサ描画
text(cameraZ, 0, -100); // 使用センサ描画
hint(ENABLE_DEPTH_TEST); // z軸を有効化
popMatrix(); // 保存した座標系の出力
}
void cone(int L, float radius, int Color1, int Color2, int Color3)
{
float x, y;
noStroke();
fill(Color1, Color2, Color3);
beginShape(TRIANGLE_FAN); // 底面の円の作成
vertex(0, 0, -L);
for(float i=0; i<2*PI; )
{
x = radius*cos(i);
y = radius*sin(i);
vertex(x, y, -L);
i = i+0.01;
}
endShape(CLOSE);
beginShape(TRIANGLE_FAN); // 側面の作成
vertex(0, 0, 0);
for(float i=0; i<2*PI; )
{
x = radius*cos(i);
y = radius*sin(i);
vertex(x, y, -L);
i = i+0.01;
}
endShape(CLOSE);
}
void arrow(int x1, int y1, int z1, int x2, int y2, int z2, int Color1, int Color2, int Color3)
{
int arrowLength = 10;
float arrowAngle = 0.5;
float phi = -atan2(y2-y1, x2-x1);
float theta = PI/2-atan2(z2-z1, x2-x1);
stroke(Color1, Color2, Color3);
strokeWeight(4);
line(x1, y1, z1, x2, y2, z2);
strokeWeight(1);
pushMatrix();
translate(x2, y2, z2);
rotateY(theta);
rotateX(phi);
cone(arrowLength, arrowLength*sin(arrowAngle), Color1, Color2, Color3);
popMatrix();
}
このプログラムではキーボードで視点を変更することができます.
y軸は上下のキー
z軸はSHIFTとCTRLキー
これを実行すると,以下のように視点を変更しても2次元の文字が問題なく表示できていることが確認できます.
表示されている文字は下から視点のx, y, z座標を表しています.
まとめ
この記事では視点の位置の変更に対応して2Dの図形や文字を表示する方法について解説しました.
座標軸の回転については,数学が苦手な方にもわかるようにできるだけかみ砕いて解説をしました.この記事が皆さんのお役に立てれば幸いです.
この記事で解説したことができるようになれば,視点の設定が自由にできるのでアニメーションの幅が広がると考えています.
続けて読む
この視点の変更に対応して2Dの図形を表示する技術を使った例として,以下の記事で解説しているものがあります.
以下の記事では3Dの倒立振子ロボットを表示して,2Dのグラフや文字などを表示できるようにしています.興味のある方は続けて参考にしてください.
Twitterでは記事の更新情報や活動の進捗などをつぶやいているので気が向いたらフォローしてください.
それでは最後まで読んでいただきありがとうございました.
コメント