みなさん,こんにちは
おかしょです.
Processingで3次元のアニメーションを作る際には視点をどこにするかが非常に重要です.
しかし,視点を設定するcameraというコマンドの使い方が結構難しいです.そこで,視点をどのようにして設定するのかについて解説します.
この記事を読むと以下のようなことがわかる・できるようになります.
- Processingの視点の変更方法
- cameraの使い方
- cameraの引数の設定方法
この記事を読む前に
この記事では3Dアニメーションで重要となる視点の変更方法を解説するのですが,その前にProcessingの座標軸の向きなどを理解している必要があります.
座標軸の向きがどのようになっているか知らない方は以下の記事で解説しているのでそちらを先に読んでおくことをおすすめします.
cameraコマンドは扱いが難しい
Processingで視点の位置を変更するには”camera”コマンドを使用します.
この視点の位置は3次元のアニメーションを作る際には非常に重要な役割を持ちます.視点の位置によって3次元のアニメーションの見え方は全く変わってくるので,この設定がうまくできていなければ,他のプログラムがどんなにうまくかけていても台無しになってしまいます.
それほど重要な視点の設定を行うcameraコマンドですが,初めての方には少し扱いずらいコマンドかもしれません.
初心者の方の大きな壁となるのが,このコマンドの引数の多さでしょう.
例えば線を引くことのできるlineコマンドは2次元の場合は引数が4つ,3次元の場合は6個の引数が必要です.このコマンドの引数の場合は,始点と終点の座標を与えればいいのでイメージもしやすく非常に扱いやすいものです.
他のコマンドも引数が少なく,それぞれの引数の意味が分かりやすく扱いやすいものが多いです.
しかし,cameraコマンドの場合は引数が9個もあります.
その上,引数に与える数値を変えてみてもよくわからない応答を示すことがあります.
この記事では,この扱いの難しいcameraコマンドの使い方を実際に実行できるプログラムを使いながら確認していきます.
cameraコマンドの引数
先程,cameraコマンドの引数は9個あると言いました.その9個の引数は以下になります.
視点の座標(x, y, z),視線を向ける座標(x, y, z),軸の向き(x, y, z)
それぞれについて解説していきます.
視点の座標とは
cameraコマンドの引数の最初の3つは視点の座標を与えます.
つまり,3次元のアニメーションのどこに自分の目を置くのかを指定します.
言葉だけではよくわからないと思うので,実際にProcessingを動かしながら確認していきます.視点の座標をキーボード変化させることができるプログラムを作成したので,それを使って解説します.
例えば,視点の位置を(x, y, z)=(250, 250, 250)としたとします.
つまりcamera(250, 250, 250, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0)としました.最初の3つで視点の座標を設定し,そのほかの引数で視線を向ける座標を原点,z軸の向きを反転させています.
その結果,以下のような画面になります.
この図ではわかりやすいように座標軸と平面を表示しています.
赤い矢印がx軸,緑色の矢印がy軸,青い矢印がz軸を表していて,xy平面も描いています.この後の図でも座標軸を表示しているので,この色と軸の関係を覚えておいてください.
さて,この図を見ると,確かに設定したとおりの視点の位置になっているように見えます.
次にx軸の視点の位置を-250にして反転させてみます.
確かに視点が移動しているのがわかります.
この視点の座標はイメージがしやすいと思います.
以下に使用しているプログラムを示しておくので,皆さんも実際に試してみてください.
import processing.opengl.*;
float cameraX, cameraY, cameraZ;
void setup()
{
background(0); // 背景の色指定 白に指定
size(displayWidth,displayHeight, OPENGL); // windowのサイズを画面いっぱいにする
translate(width/2, height/2, 0); // 原点を画面の中心に
cameraX = 250.0;
cameraY = 250.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);
}
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座標
ctrlとshiftキーでz座標
を変更することができます.
視線の向ける座標
次に視線の向ける座標を確認していきます.先程の図やプログラムでは視線の向ける座標を原点としていました.
それを(x, y, z)=(100, 0, 0)とすると以下のようになります.
上に表示されているのが視線の向ける座標を原点とした時,下の図が(x, y, z)=(100, 0, 0)したときです.視点の座標は(x, y, z)=(250, 250, 250)としています.
これらの図を比較すると,確かに視線の向ける座標がx軸の正の向きに変化していることがわかります.
これもキーボードで操作ができるプログラムを以下に示しておきます.
import processing.opengl.*;
float cameraX, cameraY, cameraZ;
float centerX, centerY, centerZ;
void setup()
{
background(0); // 背景の色指定 黒に指定
size(displayWidth,displayHeight, OPENGL); // windowのサイズを画面いっぱいにする
translate(width/2, height/2, 0); // 原点を画面の中心に
cameraX = 250.0;
cameraY = 250.0;
cameraZ = 250.0;
centerX = 100.0;
centerY = 0.0;
centerZ = 0.0;
camera(cameraX, cameraY, cameraZ, centerX, centerY, centerZ, 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) {
println(key);
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;
if (key == '4') centerX -= 10;
if (key == '1') centerX += 10;
if (key == '5') centerY -= 10;
if (key == '2') centerY += 10;
if (key == '6') centerZ -= 10;
if (key == '3') centerZ += 10;
}
camera(cameraX, cameraY, cameraZ, centerX, centerY, centerZ, 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);
}
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();
}
このプログラムでも先程のプログラムと同じように視点の座標も変更することができます.
視線の向ける座標は数字を押すことで変更できます.
2を押せばy軸を正に,5を押せば負の方向に変更します.
3を押せばz軸を正に,6を押せば負の方向に変更します.
軸の向きの設定
最後に軸の向きについて解説します.
先程から示している図はz軸を上向きとして表示していますが,初期設定ではProcessingの軸関係はこれとは異なっています.
座標軸の初期設定についてはこちらを参照してください.
3次元のアニメーションを書く場合,このままの設定ではわかりにくくなってしまうことがあります.
そこで,軸の向きを変えるのに便利なのがcameraの後ろから3つの引数です.
この引数を使えば,座標軸を思うままに設定することができます.
これまで示してきた図ではz軸を上向きとしていました.このように上を向かせたい場合は,対応する引数に-1.0をいれます.
ちなみに,軸の向きを設定する後ろから3つの引数には-1.0か0.0,1.0のどれかをいれなければなりません.
例えば,x軸を上に向けたい場合は後ろの3つの引数を(-1.0, 0.0, 0.0)とします.
すると,座標軸は以下のようになります.
反対に下に向けたい場合は1.0としてあげれば,軸は下を向きます.
また,二つの軸に対して-1.0を引数に与えるとどうなるでしょうか.
y軸とz軸の向きの引数をそれぞれ-1.0とすると以下のようになります.
x軸を下向きにした時と同じ結果になります.
これはy軸とz軸を同時に上に向けようとしたとき,お互いが相殺しあったためにこのような結果になります.
この座標軸の向きについてもキーボードで変更できるプログラムを作ったので以下に示します.
import processing.opengl.*;
float cameraX, cameraY, cameraZ;
float centerX, centerY, centerZ;
float topX, topY, topZ;
void setup()
{
background(0); // 背景の色指定 黒に指定
size(displayWidth,displayHeight, OPENGL); // windowのサイズを画面いっぱいにする
translate(width/2, height/2, 0); // 原点を画面の中心に
cameraX = 250.0;
cameraY = 250.0;
cameraZ = 250.0;
centerX = 0.0;
centerY = 0.0;
centerZ = 0.0;
topX = 0.0;
topY = 0.0;
topZ = -1.0;
camera(cameraX, cameraY, cameraZ, centerX, centerY, centerZ, topX, topY, topZ);
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) {
println(key);
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;
if (key == '4') centerX -= 10;
if (key == '1') centerX += 10;
if (key == '5') centerY -= 10;
if (key == '2') centerY += 10;
if (key == '6') centerZ -= 10;
if (key == '3') centerZ += 10;
if (key == 'q') topX = 1.0;
if (key == 'a') topX = 0.0;
if (key == 'z') topX = -1.0;
if (key == 'w') topY = 1.0;
if (key == 's') topY = 0.0;
if (key == 'x') topY = -1.0;
if (key == 'e') topZ = 1.0;
if (key == 'd') topZ = 0.0;
if (key == 'c') topZ = -1.0;
}
camera(cameraX, cameraY, cameraZ, centerX, centerY, centerZ, topX, topY, topZ);
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);
}
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();
}
このプログラムでも先程のプログラムのように視点の座標,視線の向ける座標をキーボードで変更できます.
座標軸の向きの変更はキーボードの左の9個のキーを使います.
y軸は’w’を押せば下向き(1.0)となり,’s’を押せば中間(0.0),’x’を押せば上向き(-1.0)となります.
z軸は’e’を押せば下向き(1.0)となり,’d’を押せば中間(0.0),’c’を押せば上向き(-1.0)となります.
まとめ
この記事ではProcessingのcameraコマンドについて解説しました.
他のブログや公式サイトでも解説はされていたのですが,私にはよくわからなかったためプログラムを書いて確認していきました.
今後も使い方がわかりにくいコマンドがあれば解説していこうと思います.
続けて読む
cameraコマンドは3次元のアニメーションを書くのに非常に役立ちますが,3次元のアニメーションでは文字などの2次元の図形を表示するのが難しくなります.
以下の記事では3次元のアニメーションに2次元の図形を表示する方法を解説しています.興味のある方は続けて参考にしてください.
Twitterでは記事の更新情報や活動の進捗などをつぶやいているので気が向いたらフォローしてください.
それでは最後まで読んでいただきありがとうございました.
コメント