第10章.ワイヤーフレーム図

CG プログラミング論 平成 27 年 6 月 24 日
第10章.ワイヤーフレーム図
【学習のねらい】
① 今週は、右のようにある図形を任意の角度から眺めた時
の形を描く方法の一つである、ワイヤーフレーム図の描
き方を学習します。これは内部が透けて見えるのでどの
角度から見たのか一義的に決められない、という欠陥が
あります。しかし、最も基本的な方法であり、したがっ
てより本格的な描画方法の理解の基礎にもなりますので、
しっかり理解して下さい。いよいよ本科目の最終目標で
ある3次元図形の描画学習に入ります。
10-1
ワールド座標系とボディ座標系
ワールド座標系とは、文字通り対象形状が存在する「世界」を表す座標系です。一般的
には複数の形状が、ワールド座標系内の種々の位置に配置されていて、対象形状を構成し
ます。ここに、個々の形状が定義されている座標系をボディ座標系と呼びます。
YW
yB2
ワールド座標系
yB1
xB2
ボディ座標系2
ボディ座標系1
zB2
XW
zB1
ZW
xB1
ワールド座標系とボディ座標系
ここでは、とりあえず用語の意味(定義)を頭に入れておくだけで結構です。
101
CG プログラミング論 平成 27 年 6 月 24 日
10-2
ワールド座標系と視点座標系
以降の学習では物体が存在しているワールド座標系の他に、視点の位置を基準とした視
点座標系が重要になります。
視点座標系とは、物体を見る視点を原点とした座標系で、奥行き方向を正とする軸を持
ちます(通常はこれをz軸にとります)
。各軸回りの回転は次のように解釈することができ
ます。
① Z軸回りの回転: 視点において観察者が首を左右に傾けることに相当
② X軸回りの回転:
同
前後に傾けることに相当
③ Y軸回りの回転:
同
左右に振ることに相当
画面奥行き方向
YV
ZV
画面に現れる図
視点
XV
Z 軸回りに回転し、
Y軸を左に傾けた場合
視点座標系と観察者からの見え方の関係
視点座標系では視点の位置を基準として対象物を見るため、ワールド座標系と視点座標系
との関係は下図のように、Z軸の正方向
ワールド座標系
が逆になります。
YW
YV
視点座標系
ZV
XW
ZW
ワールド座標系と視点座標系の関係
XV
102
CG プログラミング論 平成 27 年 6 月 24 日
10-3
視点座標系への座標変換
さて、ここから具体的な内容に入ります。ある図形を所定の視点から眺めたとき、それ
がどの様に見えるかを描画するにはどのようにしたら良いでしょうか。それを考えるため
に、下図のような状況を想定しましょう。これは、視点の位置が(ワールド座標系で)、
(Vx,Vy,Vz)の位置にあり、視線の方向がワールド座標の原点に向かっている事を表していま
す。このとき、ワールド座標系内に置かれた任意の図形がどのようにどのように見えるか、
という事がこれから考える問題です。
Y
視点
Vy
(Vx,Vy,Vz)
Vx
β
Vz
X
α
Z
さて、前節で説明した視点座標系とは視点を原点とし、その方向をz軸にとる座標系な
ので、今の場合は下図で表される座標系になります。我々が目にする立体図形とは、この
視点座標系の XY(図では XvYv)平面というスクリーンに、対象となる図形のイメージが
投影されたものと考えることができます。
Y
視点座標系
YV
ZV
XV
Z
β
X
α
103
CG プログラミング論 平成 27 年 6 月 24 日
視点座標系の XY 平面への投影図を描くためには大きく分けて、次の二つの操作が必要で
す。
Ⅰ.座標変換:対象となる図形のワールド座標系における座標(XW,YW,ZW)を視点座標系
からみた座標(XV,YV,ZV)に変換する。
Ⅱ.XY 平面への投影:視点座標系の座標で表された 3 次元図形を、
(視点座標系の)XY 平
面に投影する。
以下、順次説明して行きましょう。
Ⅰ.座標変換
さて、ワールド座標から視点座標へ変換するということは、ワールド座標系に平行移動
や回転移動を施して(それを)視点座標系に一致させる、ということを意味します。とこ
ろで、視点座標系を規定しているのは視線方向である ZV 軸ですから、ワールド座標の Z 軸
を視点座標系のそれに一致させれば良い事が分かります。そこで、今一度下図を見て下さ
い。
Y
YV
Vy
(Vx,Vy,Vz)
Vz
Z
β
Vz  Vx 2
2
Vx
sin  
ZV
XV
Vz
cos 
Vx
X
α
cos  
sin  
Vz 2  Vx 2
Vz 2  Vx 2
Vz 2  Vx 2  Vy 2
Vy
Vz  Vx 2  Vy 2
2
※ 後に必要になる sinαなどの表式も示しています。この表式は図より確認できるは
ずです。
この図より、ワールド座標系の Z 軸を視点座標系のそれ(ZV 軸)に一致させるためには、
次の4つの操作が必要になる事が分かります。
① ワールド座標の原点を視点の位置(VX,VY,VZ)に平行移動する。
② Y 軸の回りにαだけ回転する。
③ X 軸の回りに-βだけ回転する(Y 軸→Z 軸の方向が角度の正の方向なので)
。
④ Z 軸の向きを反転させる。
104
CG プログラミング論 平成 27 年 6 月 24 日
次ページ以降で各操作を確認して行きましょう。
① ワールド座標の原点を視点に移動する。このとき、変換後の座標を(x0,y0,z0)と表すと、
それは、下の式で表される。
Y0
※ (x,y,z)はワールド座標系における座標
x0  x  Vx
α
y 0  y  Vy
z 0  z  Vz
β
Z0
X0
ZV 軸
② Y 軸の回りに(Z0X0 平面を)αだけ回転させる(Z0 軸→Z1 軸、X0→X1 軸に移動)
。こ
のとき、変換後の座標を(x1,y1,z1)と表すと、それは、下の式で表される。
Y1(Y0)
x1  x0 cos  z 0 sin 
y1  y 0
α
X1
Z0
α
z1  x0 sin   z 0 cos
α
Z1
X0
③ X 軸の回りに(Z1Y1 平面を)-βだけ回転させる(Z1 軸→Z2 軸、Y1→Y2 軸に移動)
。こ
のとき、変換後の座標を(x2,y2,z2)と表すと、それは、下の式で表される。
Y2
Y1
Z2
x 2  x1
y 2  y1 cos   z1 sin 
β
z 2  y1 sin   z1 cos 
X2(X1)
β
Z1
105
CG プログラミング論 平成 27 年 6 月 24 日
④ z軸を反転させる(向きを逆にする)。このとき変換後の座標を(xv,yv,zv)と表すと、そ
れは、下の式で表される。
xV  x 2
yV  y 2
zV   z 2
Ⅱ.XY 平面への投影
Ⅰの座標変換が済むと、後は簡単です。変換後の各点の(x,y)座標をそのまま結んで図
形を描けば良いのです。これを平行投影法と言います。要領は下図の通りです。
YV
ZV
投影された図形
XV
これ以外に中心投影法と言って、視点からの距離によって遠近感をつける投影法がある
のですが、ここでは処理が簡単なこの平行投影法を用いる事にします。
以上の操作によって、任意の視点から眺めた図形を描画する事ができます。ここでは、
それを、ワイヤーフレーム図として描くことにします。次節で実際に描いてみましょう。
106
CG プログラミング論 平成 27 年 6 月 24 日
10-4
ワイヤーフレーム図の描画
「線」のみで描いた図形をワイヤーフレーム(wire frame)図と呼びます。もとの図形に含
まれる線分を単純に描いただけなので、容易に作図出来るのが利点です。しかし、視線方
向に対して隠れた部分の処理などを行っていないという問題があります。例えば、下図右
の例では、直方体の上面、下面のいずれからから見ているかが一意には解釈できません。
本来の直方体(左)とそのワイヤーフレーム図(右)
このように、実用には問題があるものの、より高度な描画方法の基本となるものなので、
まずはこのワイヤーフレーム図を実際に描きながら実践的に理解して行く事にしましょう。
さて、ワイヤ-フレーム図形を描くには、次の 2 種類のデータが必要です。
① 各点の座標
② 図形の点を結ぶ順番
具体例で説明しましょう。例えば次のような三角錐を描くときは、各点の座標に加えて、
どの 2 点を順に結んで行くか、という線を結ぶ順序の指定が必要です。
0
各点の座標
点0(x0,y0,z0,)
点1(x1,y1,z1)
1
3
点2(x2,y2,z2)
点3(x3,y3,z3)
2
頂点を結ぶ順番
番号
N1
N2
0
0
1
1
0
2
2
0
3
3
1
2
4
2
3
5
3
1
この右側の表に従って、順に N1-N2 で指定された頂点を結んで行けば三角錐のワイヤー
フレーム図が出来上がります(線を結ぶ順番は一通りではありません。上の表は一例です)。
107
CG プログラミング論 平成 27 年 6 月 24 日
【基礎課題 10-1】
次の様に配置された正三角錐を考えましょう。
Y
三角形の一辺をaとすると
0
1
Z
各点の座標
0(a/2,
3
X
2
2 / 3a ,
1(0,
0,
2(a/2,
0,
3(a,
0,
a
)
2 3
0)
a 3
)
2
0)
次のように、視点の位置(Vx,Vy,Vz)を入力して描画させると、その視点に対応した三角錐
がワイヤーフレーム図で表示されるプログラムを作成しましょう。
(Vx,Vy,Vz)=(1,0,0)
と入力して描画させ
たところ。つまり、
x軸上から上の三角
錐を眺めたところ。
この場合、頂点 0,2,3
からできる三角形し
か見えない。
※ ここに、三角形の一辺a=100 としています。
以下の手順にしたがって、このプログラムを作成して下さい。ここでは、ワイヤーフ
レーム図を作成するために新しいクラス「WireFrame」を定義して、それを用います。
108
CG プログラミング論 平成 27 年 6 月 24 日
<プログラム作成手順>
① いつも通り、この課題用のアプリケーションを新規作成して下さい。その後、そのプロ
ジェクトの中に新規クラスを「WireFrame」という名前で作成して下さい。
パッケージ名は各自によって異なる。
クラス名は「WireFrame」
スーパークラスは「java.lang.Object」のままでよい。
② すでに、NewJFrame と MyPanel クラスは作成しているので、次のような状態になる
はずです。この状態で、新規クラス「WireFrame」を記述します。package 名はそれぞ
れ各自のプロジェクトによって異なって結構です。適宜、自分のパッケージ名に読み替え
て下さい。
109
CG プログラミング論 平成 27 年 6 月 24 日
③ WireFrame クラスを次ページのように記述します。
package ouyou8_7_a;
import java.awt.*;
パッケージ名は各自のプログラムに応じて
異なる事に注意。
public class WireFrame {
double Vx,Vy,Vz; //視点の座標
double[] x; //ワールド座標系のx座標
double[] y; //ワールド座標系のy座標
double[] z; //ワールド座標系のz座標
double[] Xv; //視点座標系のx座標
double[] Yv; //視点座標系のy座標
double[] Zv; //視点座標系のz座標
int[] N1; //ワイヤーフレーム作成に用いる頂点の番号1
int[] N2; //ワイヤーフレーム作成に用いる頂点の番号2
int Np,NLine; //Np:頂点の数, NLine:頂点を結んでできる線分の数
public WireFrame() {
}
public void setData(double Vx,double Vy,double Vz,int Np
,int NLine,double[] x,double[] y
,double[] z,int[] N1,int[] N2) {
・・・
}
public void MyPaint(Graphics g) {
・・・
}
void zahyo_henkan() {
・・・
}
}
メソッド「setData」、
「MyPaint」そして「zahyo_henkan」の詳細は次ページ以降に示
しています。
110
CG プログラミング論 平成 27 年 6 月 24 日
<setData メソッド>
視点の座標(Vx,Vy,Vz)、頂点の数 Np、線分の数 NLine、図形のワールド座標(x,y,z)、頂
点を結ぶ順番を格納した N1,N2(p.107 の表参照)をメインプログラムから受け取るメソ
ッドです。
public void setData(double Vx,double Vy,double Vz,int Np
,int NLine,double[] x,double[] y
,double[] z,int[] N1,int[] N2) {
this.Vx=Vx;
this.Vy=Vy;
this.Vz=Vz;
this.Np=Np;
this.NLine=NLine;
this.x=x;
this.y=y;
this.z=z;
this.N1=N1;
this.N2=N2;
}
<MyPaint メソッド>
ここで、ワイヤーフレーム図を描画します。N1 および N2 で指定した頂点の座標を順次
結んで描画しています。
public void MyPaint(Graphics g) {
int xp1,yp1,xp2,yp2;
int xc=125; //描画中心のx座標
int yc=125; //描画中心のy座標
zahyo_henkan(); //視点座標系へ変換して(Xv,Yv,Zv)を求める
for (int i=0;i<NLine;i++) {
xp1=xc+(int) Xv[N1[i]];
xp2=xc+(int) Xv[N2[i]];
yp1=yc-(int) Yv[N1[i]];
yp2=yc-(int) Yv[N2[i]];
g.setColor(Color.blue);
g.drawLine(xp1,yp1,xp2,yp2);
}
}
111
CG プログラミング論 平成 27 年 6 月 24 日
<zahyo_henkan メソッド>
ここで、座標変換を行い、ワールド座標系の座標から、視点座標系の座標(Xv,Yv,Zv)を
求めています。手順は 10-3 節で説明した通りなので、そこの記述と読み比べれば理解でき
る筈です。
void zahyo_henkan() {
double root_val, cos_alfa, sin_alfa, cos_beta, sin_beta;
double x0,y0,z0,x1,y1,z1,x2,y2,z2;
Xv=new double[Np]; //座標変換後のx座標(視点座標系でのx座標)
Yv=new double[Np]; //座標変換後のy座標(視点座標系でのy座標)
Zv=new double[Np]; //座標変換後のz座標(視点座標系でのz座標)
//回転移動に必要な諸量の計算
root_val = Math.sqrt(Vx*Vx+Vz*Vz);
if(Vx!=0 || Vz!=0) {
cos_alfa = Vz/root_val;
sin_alfa = Vx/root_val;
}
else { //(Vx=0 かつ Vz=0)の場合α回転をしない(α=0 とおく)
cos_alfa = 1;
視点が Y 軸上にある場合
sin_alfa = 0;
}
root_val = Math.sqrt(Vx*Vx+Vy*Vy+Vz*Vz);
cos_beta = Math.sqrt(Vx*Vx+Vz*Vz) / root_val;
sin_beta = Vy / root_val;
for ( int i=0; i<Np; i++ ) {
// 原点を視点に平行移動
x0 = x[i] - Vx;
y0 = y[i] - Vy;
z0 = z[i] - Vz;
// y 軸の回りにαだけ回転
x1 = x0*cos_alfa - z0*sin_alfa;
y1 = y0;
z1 = x0*sin_alfa + z0*cos_alfa;
// x 軸の回りにβだけ回転
x2 = x1;
y2 = y1*cos_beta-z1*sin_beta;
z2 = y1*sin_beta + z1*cos_beta;
// z 軸の反転(向きを逆にする)
Xv[i]=x2;
Yv[i]=y2;
Zv[i]=-z2;
}
}
112
CG プログラミング論 平成 27 年 6 月 24 日
④ NewJFrame.java に戻って、DrawGraphics メソッドを次のように記述します。コメ
ントからプログラムの意味は分かるでしょう。なお、プログラムから分かるとおり、
(Vx,Vy,Vz)の値を受け取る各テキストフィールドの名前を jTextFieldVx 等としてい
ます。
void DrawGraphics(Graphics g) {
// 視点の座標の定義
double Vx=Double.parseDouble(jTextFieldVx.getText());
double Vy=Double.parseDouble(jTextFieldVy.getText());
double Vz=Double.parseDouble(jTextFieldVz.getText());
int Np=4; //頂点の数
int NLine=6; //線分の数
int a=100; //正三角錐の三角形の辺の長さ
//各頂点の座標を与える
double[] x={a/2,0,a/2,a};
double[] y={Math.sqrt(2.0/3)*a,0,0,0};
double[] z={a/2/Math.sqrt(3),0,a*Math.sqrt(3)/2,0};
//各頂点を結ぶ順番を定義する
int[] N1={0,0,0,1,2,3};
int[] N2={1,2,3,2,3,1};
WireFrame wframe= new WireFrame(); //WireFrame オブジェクトの生成
wframe.setData(Vx,Vy,Vz,Np,NLine,x,y,z,N1,N2); //描画に必要なデ
ータを設定する
wframe.MyPaint(g); //ワイヤーフレーム図を描画する
}
これで、プログラムは完成です。色々な(Vx,Vy,Vz)を入力して実行結果を確認してみ
て下さい。
<実行例>
(1,1,0)
(Vx,Vy,Vz)
(1,3,0)
(1,5,0)
(1,10,0)
Vy の値を増加
y 軸上方に上がるにつれて、最初は見えなかった(頂点 0-1-3 で作る)面が徐々に見え
るようになる様が確認できる(p.108 の図参照)
。
※この課題は、実行結果を確認後「実行結果を確認しました」と書いて送って下さい。
113
CG プログラミング論 平成 27 年 6 月 24 日
【応用課題 10-A】
【基礎課題 10-1】を改良して今度は次のような直方体のワイヤーフレーム図を描画する様
にして下さい。
Y
a
c
b
X
a=60, b=80,c=100
Z
このプログラムは、
【基礎課題 10-1】のプログラムで、下の点線枠部を直方体用に書き換
えるだけで実現できます(WireFrame クラスはあらゆる図形に対応できるように一般化し
て作っているのでこれが可能なのです)
。修正後の枠線部を記述して下さい。
void DrawGraphics(Graphics g) {
double Vx=Double.parseDouble(jTextFieldVx.getText());
double Vy=Double.parseDouble(jTextFieldVy.getText());
double Vz=Double.parseDouble(jTextFieldVz.getText());
int Np=4; //頂点の数
int NLine=6; //線分の数
int a=100; //正三角錐の三角形の辺の長さ
//各頂点の座標を与える
double[] x={a/2,0,a/2,a};
double[] y={Math.sqrt(2.0/3)*a,0,0,0};
double[] z={a/2/Math.sqrt(3),0,a*Math.sqrt(3)/2,0};
//各頂点を結ぶ順番を定義する
int[] N1={0,0,0,1,2,3};
int[] N2={1,2,3,2,3,1};
この部分を
修正する。
WireFrame wframe= new WireFrame(); //WireFrame オブジェクトの生成
wframe.setData(Vx,Vy,Vz,Np,NLine,x,y,z,N1,N2);
wframe.MyPaint(g); //ワイヤーフレーム図を描画する
}
<実行例> (Vx,Vy,Vz)
(1,1,1)
(1,1,2)
114
(1,1,4)