第13章.補間曲線-ベジェ曲線ext-web.edu.sgu.ac.jp/hiko/cg/print/print14_7.20_27.pdf ·...

25
CG プログラミング論 平成 28 7 20 143 【学習のねらい】 ① 前回までの学習では、球を題材にして、それに隠面消去さらにはシェーディングを施 すことによって立体的に描く方法を学習しました。これら隠面消去およびシェーディ ングについては、一般に任意の形状の図形についても適用できます。しかし、(関数で 表現できない様な)任意の形状を描くためには、表面上の各点の座標(x,y,z)を一つ ずつ指定しなければなりません。複雑な図形の場合は、膨大な点の座標を与えなけれ ばならず、大変な労力がかかります。そこで、その労力を減らすため、幾つかの代表 的な点を与え、それらを適当な関数で結ぶ(補間する)事で任意の形状(曲線)を表 現する方法があります。今回は、 CG の世界で最もよく用いられるベジェ曲線による一 般の曲線の近似方法を学習しましょう。 13-1 曲線の近似 これまで、円や楕円を描く方法は学習してきました。また、下の(c)の様に、円と楕円を 組み合わせて色々な曲線を描くことも可能です。 しかし、これらの2次曲線(楕円や放物線、双曲線の総称です)を組み合わせて複雑な曲 線を描こうとする場合には、かなり細かく分解しなければうまく近似できなくなります。 実際、下図のような場合には、曲線を分解することがやっかいになります。 (a) (b) (c)

Upload: others

Post on 02-Oct-2020

0 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: 第13章.補間曲線-ベジェ曲線ext-web.edu.sgu.ac.jp/HIKO/CG/print/print14_7.20_27.pdf · cg プログラミング論 平成28 年7 月20 日 143 第13章.補間曲線-ベジェ曲線

CG プログラミング論 平成 28 年 7 月 20 日

143

第第1133章章..補補間間曲曲線線--ベベジジェェ曲曲線線

【学習のねらい】

① 前回までの学習では、球を題材にして、それに隠面消去さらにはシェーディングを施

すことによって立体的に描く方法を学習しました。これら隠面消去およびシェーディ

ングについては、一般に任意の形状の図形についても適用できます。しかし、(関数で

表現できない様な)任意の形状を描くためには、表面上の各点の座標(x,y,z)を一つ

ずつ指定しなければなりません。複雑な図形の場合は、膨大な点の座標を与えなけれ

ばならず、大変な労力がかかります。そこで、その労力を減らすため、幾つかの代表

的な点を与え、それらを適当な関数で結ぶ(補間する)事で任意の形状(曲線)を表

現する方法があります。今回は、CG の世界で最もよく用いられるベジェ曲線による一

般の曲線の近似方法を学習しましょう。

13-1 曲線の近似

これまで、円や楕円を描く方法は学習してきました。また、下の(c)の様に、円と楕円を

組み合わせて色々な曲線を描くことも可能です。

しかし、これらの2次曲線(楕円や放物線、双曲線の総称です)を組み合わせて複雑な曲

線を描こうとする場合には、かなり細かく分解しなければうまく近似できなくなります。

実際、下図のような場合には、曲線を分解することがやっかいになります。

(a) (b)

(c)

Page 2: 第13章.補間曲線-ベジェ曲線ext-web.edu.sgu.ac.jp/HIKO/CG/print/print14_7.20_27.pdf · cg プログラミング論 平成28 年7 月20 日 143 第13章.補間曲線-ベジェ曲線

CG プログラミング論 平成 28 年 7 月 20 日

144

そこで、このような複雑な曲線をできる限り少ないデータで記述することができる方法

が色々と工夫されました。その中の一つがここで学習するベジェ曲線です。これは、もと

もとフランスの自動車メーカー「ルノー社」の技術者ピエール・E・ベジェ(Pierre E.Bezier)

が、自動車の設計のために考え出したものと言われています。その後、自動車のボディに

止まらず、様々な形状を表現するのに威力を発揮したため、CGの世界でも頻繁に用いられ

るようになったのです。

13-2 ベジェ曲線による補間①

ベジェ曲線では、制御点と呼ばれる複数の点によって、曲線の形が決まります。ここで

は、制御点の個数が4である場合のベジェ曲線について学習します。

今、下のように P1~P4の 4 つの点が与えられた時に、これを順次直線で結ぶと下のよう

な図が描けます。

一方、これら 4 点からベジェ曲線を定義して描いたものが、上の曲線です。このベジェ曲

線は次のようにして描けます。

P1 (x1,y1)

P2 (x2,y2) P3 (x3,y3)

P4 (x4,y4)

Page 3: 第13章.補間曲線-ベジェ曲線ext-web.edu.sgu.ac.jp/HIKO/CG/print/print14_7.20_27.pdf · cg プログラミング論 平成28 年7 月20 日 143 第13章.補間曲線-ベジェ曲線

CG プログラミング論 平成 28 年 7 月 20 日

145

平面上の4点 P1(x1,y1)、P2(x2,y2)、P3(x3,y3)、P4(x4,y4)が与えられたときに、これから定

義されるベジェ曲線は、tをパラメータとして次の様に表されます。

4

1

44332211

4

1

44332211

)(

)()()()()(

)(

)()()()()(

i

ii

i

ii

tBy

tBytBytBytByty

tBx

tBxtBxtBxtBxtx

 

そして、ベジェ係数 Bi(t)は次のように与えられます。

3

4

2

3

2

2

3

1

)(

)1(3)(

)1(3)(

)1()(

ttB

tttB

tttB

ttB

パラメータtを、例えば、{0, 0.1, 0.2, 0.3, ・・・, 1.0}の様に適当な刻み幅で区切り、各tに

おける(x,y)を(1)式に従って計算して、それら各点を結べばベジェ曲線を描くことができま

す。なお、(1)、(2)式から分かるように、

t=0 の時: (x,y)=(x1,y1)

t=1 の時: (x,y)=(x4,y4)

となります。つまり、ベジェ曲線は 4 点からなる区間の両端の点は必ず通ります。

それでは、次の課題で p.144 のベジェ曲線を描いてみましょう。

【応用課題 13-A】

プログラムを起動して[描画]

ボタンをクリックすると、次のよ

うなベジェ曲線を、点を直線で結

んだものと同時に描画するプロ

グラムを作成しましょう。

このプログラムは次のように

なります。

0≦t≦1 (1)

(2)

Page 4: 第13章.補間曲線-ベジェ曲線ext-web.edu.sgu.ac.jp/HIKO/CG/print/print14_7.20_27.pdf · cg プログラミング論 平成28 年7 月20 日 143 第13章.補間曲線-ベジェ曲線

CG プログラミング論 平成 28 年 7 月 20 日

146

void DrawGraphics(Graphics g) {

double[] x={ 50, 0,250, 200 };//最初に与える点のx座標

double[] y={ 0, 150, 150,0 }; //最初に与える点のy座標

int x1,y1,x2,y2;

int Nb=50; //一区間のメッシュ数

int Np=4; //最初に与える点の数

int Kukan=1; //ベジェ曲線で近似する区間の数:今の場合1

double[] xb=new double[Kukan*(Nb+1)]; //ベジェ曲線上の各点のx座標

double[] yb=new double[Kukan*(Nb+1)]; //ベジェ曲線上の各点のy座標

int xc=10; //描画中心のx座標

int yc=200; //描画中心のy座標

//ベジェ曲線の描画

g.setColor(Color.blue);

Bezie(Kukan,Nb,x,y,xb,yb); //ベジェ曲線で近似した点 Xb,Ybの計算

x1=xc+(int) xb[0]; //描画開始点のx座標

y1=yc-(int) yb[0]; //描画開始点のy座標

for (int i=1;i<Kukan*(Nb+1);i++) {

x2=xc+(int) xb[i];

y2=yc-(int) yb[i];

g.drawLine(x1,y1,x2,y2);

x1=x2;

y1=y2;

}

// 最初に与えた点の(直線による)連結

x1=xc+(int) x[0];

y1=yc-(int) y[0];

g.setColor(Color.red);

for(int i=1;i<Np;i++) {

x2=xc+(int)x[i];

y2=yc-(int)y[i];

g.drawLine(x1,y1,x2,y2);

x1=x2;

y1=y2;

}

}

<解説>

① 4 点 P1~P4の座標を与えています。

② ベジェ曲線で用いるパラメータt{ 0≦t≦1 }の t の分割数(メッシュ数)を Nb とい

う変数で与えています。ここでは分割数(メッシュ数)を 50 点としています。

③ ベジェ曲線で近似する区間はここでは一つです。複数の区間をつなげる場合は、【応

用課題 13-C】で扱います。

④ ベジェ曲線では、P1~P4の区間内の任意の座標( x(t),y(t) )をパラメータt{ 0≦t≦1 }

で指定します(p.145 の(1)式参照)。そして、tを{t0,t1,t2,・・・,tNb}というように、刻

んでいます。そこで、パラメータ ti に対応する x(ti),y(ti)をそれぞれ、xb[i]、yb[i]と表

しています。

Page 5: 第13章.補間曲線-ベジェ曲線ext-web.edu.sgu.ac.jp/HIKO/CG/print/print14_7.20_27.pdf · cg プログラミング論 平成28 年7 月20 日 143 第13章.補間曲線-ベジェ曲線

CG プログラミング論 平成 28 年 7 月 20 日

147

⑤ ④で定義した xb[i]、yb[i]、(i=0~Nb)を求めるメソッド Bezie()を呼び出してい

ます。これは新たに(独自に)定義するメソッドです。このプログラム作成の後、下の

様に記述して下さい。この部分の記述時には(Bezie が未定義なので)警告が出ます。

上の記述が完成したら、ベジェ曲線上の座標 xb[i]、yb[i]、(i=0~Nb)を求めるメソッド

Bezie を次のように記述して下さい。場所は DrawGraphics()の前後いずれでも結構です。

void Bezie(int Kukan,int Nb,double[] x, double[] y

,double[] Xb,double[] Yb) {

double[][] B=new double[4][Nb+1]; //ベジェ係数 Bi(t)の宣言

double Db=1.0/Nb; //区間内の刻み幅

double t; //パラメータt

//ベジェ係数 Bi(t)の計算

for(int i=0; i<= Nb; i++) {

t=Db*i;

B[0][i]=(1-t)*(1-t)*(1-t);

B[1][i]=3*t*(1-t)*(1-t);

B[2][i]=3*t*t*(1-t);

B[3][i]=t*t*t;

}

//ベジェ曲線上の座標(x(tm),y(tm))の計算

int m=0;

for (int i=0;i<Kukan;i++) {

for (int j=0;j<=Nb; j++) {

Xb[m]=0;

Yb[m]=0;

for (int k=0;k<4;k++) {

Xb[m]=Xb[m]+x[i*3+k]*B[k][j];

Yb[m]=Yb[m]+y[i*3+k]*B[k][j];

}

m=m+1;

}

}

}

ベジェ係数 Bi(tj), ( i=0~3, j=0~Nb )を 2 次元配列 B[i][j]として表しています。それ

以外は、p.145 の(2)式および(1)式にしたがって、Bi(t)および ( x(t),y(t) )を計算しているだ

けなので、内容は理解できるでしょう。

※ この課題は、実行結果を確認後「実行結果を確認しました」と書いて送って下さい。

Page 6: 第13章.補間曲線-ベジェ曲線ext-web.edu.sgu.ac.jp/HIKO/CG/print/print14_7.20_27.pdf · cg プログラミング論 平成28 年7 月20 日 143 第13章.補間曲線-ベジェ曲線

CG プログラミング論 平成 28 年 7 月 20 日

148

【応用課題 13-B】

上のプログラムにおいて、P1~P4 の座標をそれぞれ次の①~③のように変更しました。

このとき描かれるベジェ曲線は、それぞれ下図のいずれでしょうか。①~③それぞれに該

当する図の記号を答えて下さい。

① P1(,x,y)=(50,0), P2(x,y)=(250,150), P3(x,y)=(0,150), P4(x,y)=(200,0)

② P1(,x,y)=(50,0), P2(x,y)=(0,150), P3(x,y)=(200,0), P4(x,y)=(250,150)

③ P1(,x,y)=(50,0), P2(x,y)=(200,0), P3(x,y)=(0,150), P4(x,y)=(250,150)

(A) (B)

(C)

Page 7: 第13章.補間曲線-ベジェ曲線ext-web.edu.sgu.ac.jp/HIKO/CG/print/print14_7.20_27.pdf · cg プログラミング論 平成28 年7 月20 日 143 第13章.補間曲線-ベジェ曲線

CG プログラミング論 平成 28 年 7 月 20 日

149

13-3 ベジェ曲線による補間① - 複数区間の場合

ベジェ曲線は4点で1つの曲線を近似するので、4点を超える場合には、図のように接

続を繰り返す事になります。その際、第3番目と第5番目の点の間に第4番目の点が位置

するようにしておくと、順次なめらかに接続されて行きます。

P1

P2 P3

P4

P5 P6

P7

ここで、点の個数と区間数の間には次の関係があることが分かります。

点の個数 = 3 × 区間数 + 1

【応用課題 13-C】

【応用課題 13-A】で作成したプログラムを用いて、次のような 7 点を補間するベジェ曲

線を描きましょう。

(0,0)

P2(0,100) P3(100,100)

P5(100,-100)

P6(175,-125)

P7(250,0) P1 P4(100,0)

Page 8: 第13章.補間曲線-ベジェ曲線ext-web.edu.sgu.ac.jp/HIKO/CG/print/print14_7.20_27.pdf · cg プログラミング論 平成28 年7 月20 日 143 第13章.補間曲線-ベジェ曲線

CG プログラミング論 平成 28 年 7 月 20 日

150

このプログラムは、【応用課題 13-A】のプログラムにおいて、下の下線部を変更し、空欄

①を埋めることで作成できます(メソッド Bezie はそのまま使えます)。任意の点の個数

Np の場合の区間数を与えるように①を埋めてプログラムを完成させて下さい。

void DrawGraphics(Graphics g) {

double[] x={ 0,0,100,100,100,175,250 };//最初に与える点のx座標

double[] y={ 0,100,100,0,-100,-125,0 };//最初に与える点のy座標

int x1,y1,x2,y2;

int Nb=50; //一区間のメッシュ数

int Np=7; //最初に与える点の数

int Kukan= ; //ベジェ曲線で近似する区間の数

double[] xb=new double[Kukan*(Nb+1)]; //ベジェ曲線上の各点のx座標

double[] yb=new double[Kukan*(Nb+1)]; //ベジェ曲線上の各点のy座標

int xc=10; //描画中心のx座標

int yc=200; //描画中心のy座標

//ベジェ曲線の描画

g.setColor(Color.blue);

Bezie(Kukan,Nb,x,y,xb,yb); //ベジェ曲線で近似した点 Xb,Ybの計算

x1=xc+(int) xb[0]; //描画開始点のx座標

y1=yc-(int) yb[0]; //描画開始点のy座標

for (int i=1;i<Kukan*(Nb+1);i++) {

x2=xc+(int) xb[i];

y2=yc-(int) yb[i];

g.drawLine(x1,y1,x2,y2);

x1=x2;

y1=y2;

}

// 最初に与えた点の(直線による)連結

x1=xc+(int) x[0];

y1=yc-(int) y[0];

g.setColor(Color.red);

for(int i=1;i<Np;i++) {

x2=xc+(int)x[i];

y2=yc-(int)y[i];

g.drawLine(x1,y1,x2,y2);

x1=x2;

y1=y2;

}

}

ヒント

前ページの式より

区間数=(点の個数-1)/3

となります。今の場合、点の個数は Np で与えられています。

Page 9: 第13章.補間曲線-ベジェ曲線ext-web.edu.sgu.ac.jp/HIKO/CG/print/print14_7.20_27.pdf · cg プログラミング論 平成28 年7 月20 日 143 第13章.補間曲線-ベジェ曲線

CG プログラミング論 平成 28 年 7 月 20 日

151

第第1144章章..回回転転体体のの描描画画

【学習のねらい】

① CG の世界では、前章で学習したベジェ曲線で表した曲線を(適当な軸を中心にして)

回転させることで立体的な図形を作成することがよく行われます。これは、大変有力

な方法なので、本講義の最後の単元として学習しておきましょう。

14-1 回転体の描画

回転体は、適当な回転軸の回りに外形となる曲線(輪郭線)を回転させて描きます。輪郭

線はベジェ曲線を用いて描きます。

例えば下の左図は曲線上の点群をy軸に関して回転させたときに描かれる円形断面を表し

ています。そしてこれを用いて隣接する円形断面間でポリゴン(四角形)を生成し、12 章

で学習したシェーディングを行なうと、右図が得られます。本章ではこういった立体図形

を描きましょう。

x

y

z

①ベジェ曲線を用いて輪郭線を描く。

②y軸を中心に回転させる。

Page 10: 第13章.補間曲線-ベジェ曲線ext-web.edu.sgu.ac.jp/HIKO/CG/print/print14_7.20_27.pdf · cg プログラミング論 平成28 年7 月20 日 143 第13章.補間曲線-ベジェ曲線

CG プログラミング論 平成 28 年 7 月 20 日

152

さて、回転体の輪郭を与えるベジェ曲線上の点の各々は、下図のように回転体断面の半径

と高さで指定できます。例えば断面を xy平面にとると、半径は x 座標の値、高さはy座標

の値となります。このベジェ曲線上の各点(x,y)を、y軸を中心に回転すれば、回転図形

が得られます。

【応用課題 14-A】

【応用課題 12-A】のプログラムを改良して、回転体を描くプログラムを作成しましょう。

【応用課題 12-A】のプロ

ジェクトをコピーしてそ

れを利用します。

ここで作成するのは、左

のような図形です。隠面

処理およびシェーディン

グは次の課題で行います。

まず、この描画図形に合

わせて、フレームを縦に

伸ばして下さい。

ベジェ曲線上の点

(2 次元)

回転角Φ

Page 11: 第13章.補間曲線-ベジェ曲線ext-web.edu.sgu.ac.jp/HIKO/CG/print/print14_7.20_27.pdf · cg プログラミング論 平成28 年7 月20 日 143 第13章.補間曲線-ベジェ曲線

CG プログラミング論 平成 28 年 7 月 20 日

153

続いて、NewJFrame.java 内の DrawGraphics()を次のように修正します。下線と点線枠

内が【応用課題 12-A】からの変更部分です。

void DrawGraphics(Graphics g) {

// 視点の座標の定義

double Vx=Double.parseDouble(jTextFieldVx.getText());

double Vy=Double.parseDouble(jTextFieldVy.getText());

double Vz=Double.parseDouble(jTextFieldVz.getText());

double[][][] faceX=new double[300][200][4];

double[][][] faceY=new double[300][200][4];

double[][][] faceZ=new double[300][200][4];

// ----( グラスの輪郭線データ )---------

double[ ] x =

{ 100, 50, 10, 10, 10, 40, 70, 100, 110, 70, 60, 56, 52 };

double[ ] y =

{ 0, 6, 30, 70, 120, 140, 160, 180, 220, 270, 280, 290, 310 };

int Np3=13; //最初に与える点の数

int Nb=20; //ベジェ補間の一区間のメッシュ数

int Kukan=(Np3-1)/3; //区間数

int Np1=Kukan*(Nb+1),Np2=50,Np=4;

KaitenTai(Np1,Np2,Kukan,Nb,x,y,faceX,faceY,faceZ); //回転体の座標の計算

//光線ベクトルの定義

double RayX=Double.parseDouble(jTextFieldRayX.getText());

double RayY=Double.parseDouble(jTextFieldRayY.getText());

double RayZ=Double.parseDouble(jTextFieldRayZ.getText());

double P_a=0.3,P_d=1.0,P_s=1.0; //各成分の最大強度の設定

Hidden3D h3D= new Hidden3D(); //hdden3Dオブジェクトの生成

h3D.setData(Vx,Vy,Vz,Np,Np1,Np2,faceX,faceY,faceZ); //描画に必要なデータを設定する

h3D.MyPaint(g); //画像の描画

}

ここに、①は、【応用課題 12-A】において Sphere()というメソッドを用いて球面上の(x,y,z)

座標を求めていた部分を、回転体の(x,y,z)座標を求める部分に置き換えたものです。そのた

め、Sphere()ではなく新たに Kaitentai()というメソッドを定義してこれを呼び出して

います。これは、xy 平面で与えた座標(x,y)を基に回転体の座標(faceX、faceY、faceZ)を

求めるメソッドです。これを次ページのように記述します。

なお、②では、まだシェーディングを行わないので、Shading クラスではなく、Hidden3D

クラスを用いています。

それでは、NewJFrame.java 内に新たなメソッド Kaitentai()を次ページのように記

述して下さい。

Page 12: 第13章.補間曲線-ベジェ曲線ext-web.edu.sgu.ac.jp/HIKO/CG/print/print14_7.20_27.pdf · cg プログラミング論 平成28 年7 月20 日 143 第13章.補間曲線-ベジェ曲線

CG プログラミング論 平成 28 年 7 月 20 日

154

void KaitenTai(int N_Y,int N_Fai,int Kukan,int Nb

,double[] x,double[] y,double faceX[][][]

,double faceY[][][],double faceZ[][][] ) {

double[][] B=new double[4][100];

double[] Xb=new double[N_Y];

double[] Yb=new double[N_Y];

double y1,y2,Fai1,Fai2;

double d_Fai=2*Math.PI/N_Fai;

double[] KaitenX=new double[4];//四角形ポリゴンのx座標

double[] KaitenY=new double[4];//四角形ポリゴンのy座標

double[] KaitenZ=new double[4];//四角形ポリゴンのz座標

Bezie(Kukan,Nb,x,y,Xb,Yb); //ベジェ曲線で補間する

//回転体の各ポリゴンの座標

for(int i=0;i<N_Y-1;i++) {

y1=Yb[i];

y2=Yb[i+1];

KaitenY[0]=y2;

KaitenY[1]=y2;

KaitenY[2]=y1;

KaitenY[3]=y1;

for(int j=0;j<N_Fai;j++) {

Fai1=j*d_Fai;

Fai2=(j+1)*d_Fai;

KaitenX[0]=Xb[i+1]*Math.cos(Fai2);

KaitenX[1]=Xb[i+1]*Math.cos(Fai1);

KaitenX[2]=Xb[i]*Math.cos(Fai1);

KaitenX[3]=Xb[i]*Math.cos(Fai2);

KaitenZ[0]=Xb[i+1]*Math.sin(Fai2);

KaitenZ[1]=Xb[i+1]*Math.sin(Fai1);

KaitenZ[2]=Xb[i]*Math.sin(Fai1);

KaitenZ[3]=Xb[i]*Math.sin(Fai2);

for (int k=0;k<4;k++) {

faceX[i][j][k]=KaitenX[k]; //(i,j)番目のポリゴンのx座標

faceY[i][j][k]=KaitenY[k]; //(i,j)番目のポリゴンのy座標

faceZ[i][j][k]=KaitenZ[k]; //(i,j)番目のポリゴンのz座標

}

}

}

}

p.152の図より、回転体表面の座標(x,y,z)は、xy平面上に描いたベジェ曲線の座標を(xb,yb)

とすると、次のように与えられます。

sin

cos

b

b

b

xz

yy

xx

Bezie()は次ページで定義する。

X

Z

xb

x

z

φ Φ

2/,

Page 13: 第13章.補間曲線-ベジェ曲線ext-web.edu.sgu.ac.jp/HIKO/CG/print/print14_7.20_27.pdf · cg プログラミング論 平成28 年7 月20 日 143 第13章.補間曲線-ベジェ曲線

CG プログラミング論 平成 28 年 7 月 20 日

155

xy 平面上に描いたベジェ曲線の座標(xb,yb)を求めるメソッド Bezie()は【応用課題 13-A】

(p.147)と同様に次のように記述します。

void Bezie(int Kukan,int Nb,double[] x, double[] y

,double[] Xb,double[] Yb) {

double[][] B=new double[4][Nb+1]; //ベジェ係数 Bi(t)の宣言

double Db=1.0/Nb; //区間内の刻み幅

double t; //パラメータt

//ベジェ係数 Bi(t)の計算

for(int i=0; i<= Nb; i++) {

t=Db*i;

B[0][i]=(1-t)*(1-t)*(1-t);

B[1][i]=3*t*(1-t)*(1-t);

B[2][i]=3*t*t*(1-t);

B[3][i]=t*t*t;

}

//ベジェ曲線上の座標(x(tm),y(tm))の計算

int m=0;

for (int i=0;i<Kukan;i++) {

for (int j=0;j<=Nb; j++) {

Xb[m]=0;

Yb[m]=0;

for (int k=0;k<4;k++) {

Xb[m]=Xb[m]+x[i*3+k]*B[k][j];

Yb[m]=Yb[m]+y[i*3+k]*B[k][j];

}

m=m+1;

}

}

}

Page 14: 第13章.補間曲線-ベジェ曲線ext-web.edu.sgu.ac.jp/HIKO/CG/print/print14_7.20_27.pdf · cg プログラミング論 平成28 年7 月20 日 143 第13章.補間曲線-ベジェ曲線

CG プログラミング論 平成 28 年 7 月 20 日

156

次に、Hidden3D.java に移って、MyPaint()メソッドを次のように変更します。今の場合、

隠面消去を行わないので、可視判定部分をコメントアウトしておきます。

public void MyPaint(Graphics g) {

int[] xp=new int[Np];

int[] yp=new int[Np];

x=new double[Np]; //ポリゴンの各頂点のx座標

y=new double[Np]; //ポリゴンの各頂点のy座標

z=new double[Np]; //ポリゴンの各頂点のz座標

int xc=150; //描画中心のx座標

int yc=300; //描画中心のy座標

g.setColor(Color.blue);

for(int i=0;i<Np1;i++) {

for(int j=0;j<Np2;j++) {

for(int k=0;k<Np;k++) {

x[k]=faceX[i][j][k]; //ポリゴンの各頂点のx座標

y[k]=faceY[i][j][k]; //ポリゴンの各頂点のx座標

z[k]=faceZ[i][j][k]; //ポリゴンの各頂点のx座標

}

// if(Kashi_hantei()) {

zahyo_henkan(); //ポリゴンの各頂点の座標を視点座標系へ変換

for(int k=0;k<Np;k++) {

xp[k]=xc+(int) xv[k];

yp[k]=yc-(int) yv[k];

}

g.drawPolygon(xp,yp,Np);

// }

}

}

}

プログラムを作成したら実行し、p.152 のように描画されるか確認して下さい。

※ この課題は、実行結果を確認後「実行結果を確認しました」書いて送ってください。

可視判定を行わないので、この部

分をコメントにして外しておく。

Page 15: 第13章.補間曲線-ベジェ曲線ext-web.edu.sgu.ac.jp/HIKO/CG/print/print14_7.20_27.pdf · cg プログラミング論 平成28 年7 月20 日 143 第13章.補間曲線-ベジェ曲線

CG プログラミング論 平成 28 年 7 月 20 日

157

14-2 シェーディングの改良

【応用課題 14-B】

次に、隠面消去を行ってみましょう。【応用課題 14-A】のプログラムで、Hidden3D.java

の MyPaint()メソッドでコメントアウトした部分を復活させて下さい。

public void MyPaint(Graphics g) {

・・・

for(int i=0;i<Np1;i++) {

for(int j=0;j<Np2;j++) {

for(int k=0;k<Np;k++) {

・・・

}

if(Kashi_hantei()) {

zahyo_henkan(); //ポリゴンの各頂点の座標を視点座標系へ変換

for(int k=0;k<Np;k++) {

xp[k]=xc+(int) xv[k];

yp[k]=yc-(int) yv[k];

}

g.drawPolygon(xp,yp,Np);

}

}

}

}

隠面処理を復活させた後、プログラムを実行すると、隠面消去が施されます。ところが、

この場合、下図のように描画されるはずです。ワイングラスのグラスの内側が表示されて

いませんね。

これは、隠面処理で消去されてしまったためです。

第 11 章で学習した隠面処理によると確かに該当す

る面の外側は見えません。しかし、本来は面の内側

が見えるはずです。事情は次の通りです。

第 11 章(p.116)で学習した通り、下の様な状況

では面 C は見えませんが、裏面の面 D は見えます。

球面のように閉じた図形と違って、ワイングラスの

ように開いた図形の場合は、裏面が見えることを考

慮しなければなり

ません。この点を改

良しましょう。

この 2 行を復活させる。

面 C

α

見えない

面 D

見える

法線ベクトル

Page 16: 第13章.補間曲線-ベジェ曲線ext-web.edu.sgu.ac.jp/HIKO/CG/print/print14_7.20_27.pdf · cg プログラミング論 平成28 年7 月20 日 143 第13章.補間曲線-ベジェ曲線

CG プログラミング論 平成 28 年 7 月 20 日

158

改良に進む前に、後の説明の都合上、これにシェーディングを施すようにしましょう。

DrawGraphics()メソッドの中で Hidden3D クラスを用いていた部分を、下の枠線部のよう

に、Shading クラスを用いるように修正して下さい。

void DrawGraphics(Graphics g) {

// 視点の座標の定義

・・・

double RayY=Double.parseDouble(jTextFieldRayY.getText());

double RayZ=Double.parseDouble(jTextFieldRayZ.getText());

double P_a=0.3,P_d=1.0,P_s=1.0; //各成分の最大強度の設定

Shading shade= new Shading(); //shadingオブジェクトの生成

shade.setData(Vx,Vy,Vz,Np,Np1,Np2,faceX,faceY,faceZ

,RayX,RayY,RayZ,P_a,P_d,P_s); //描画に必要なデータを設定する

shade.MyPaint(g); //画像の描画

}

もちろん、この段階ではシェーディングを行っても次のようにワイングラスの口の部分

が描画されないままです。

プログラムを作成したら実行し、上のように描画されることを確認して下さい。

※ この課題は、実行結果を確認後「実行結果を確認しました」書いて送ってください。

Page 17: 第13章.補間曲線-ベジェ曲線ext-web.edu.sgu.ac.jp/HIKO/CG/print/print14_7.20_27.pdf · cg プログラミング論 平成28 年7 月20 日 143 第13章.補間曲線-ベジェ曲線

CG プログラミング論 平成 28 年 7 月 20 日

159

さて、裏面が見えるようにするためには、次の様にします。

1. 可視判定で「見えない」と判定された場合、裏面について、第 12 章(p.133~134)で

説明した、ディフーズ成分の強度dおよびスペキュラー成分の強度sを計算する。

2. 裏面の法線ベクトルは表面のそれと逆向きになる。そこで、p.133 に示した法線ベクト

ルと光源との間の角度α’dは次のようになる。

3. このとき、 cosα’d=-cosαd となる。

4. 同様に、裏面に対する法線ベクトルと p.134 で示した VHベクトルとの角度をα’s とす

ると、cosα’s=-cosαs となる。

5. cosα’dと cosα’sを用いて、dおよびsを計算する。

この考えを用いて、次の【応用課題 14-C】で裏面を描けるように改良しましょう。

NV

RV

光源

法線ベクトル

光線ベクトル

αd

裏面の法線ベクトル

α’d

表面

裏面

Page 18: 第13章.補間曲線-ベジェ曲線ext-web.edu.sgu.ac.jp/HIKO/CG/print/print14_7.20_27.pdf · cg プログラミング論 平成28 年7 月20 日 143 第13章.補間曲線-ベジェ曲線

CG プログラミング論 平成 28 年 7 月 20 日

160

【応用課題 14-C】

【応用課題 14-B】のプログラムを改良しましょう。Shading.javaに戻って、MyPaint()

を次のように修正して下さい。点線枠の部分が修正部分です。

public void MyPaint(Graphics g) {

int[] xp=new int[Np];

int[] yp=new int[Np];

x=new double[Np]; //ポリゴンの各頂点のx座標

y=new double[Np]; //ポリゴンの各頂点のy座標

z=new double[Np]; //ポリゴンの各頂点のz座標

int xc=150; //描画中心のx座標

int yc=300; //描画中心のy座標

double intensity,Max_intensity;

int rgb;

Max_intensity=P_a+P_d+P_s;

for(int i=0;i<Np1;i++) {

for(int j=0;j<Np2;j++) {

for(int k=0;k<Np;k++) {

x[k]=faceX[i][j][k]; //ポリゴンの各頂点のx座標

y[k]=faceY[i][j][k]; //ポリゴンの各頂点のx座標

z[k]=faceZ[i][j][k]; //ポリゴンの各頂点のx座標

}

zahyo_henkan(); //ポリゴンの各頂点の座標を視点座標系へ変換

for (int k = 0; k < Np; k++) {

xp[k] = xc + (int) xv[k];

yp[k] = yc - (int) yv[k];

}

if(Kashi_hantei()) {

ShadeCalc();

intensity=P_a+P_d*D_strength+P_s*S_strength;//目に入って来

る光の強度

rgb=(int) (255*intensity/Max_intensity);

}

else {

ShadeCalc2();

intensity=P_a+P_d*D_strength+P_s*S_strength;//目に入って来

る光の強度

rgb=(int) (255*intensity/Max_intensity);

}

g.setColor(new Color(rgb, rgb, rgb ));

g.fillPolygon(xp, yp, Np);

}

}

}

ここで、裏面を描くために新たに ShadeCalc2()を用意しています。これを次ページの

ように記述して下さい。ShadeCalc()からの変更部分は、二つの枠で囲んだ部分のみです。

Page 19: 第13章.補間曲線-ベジェ曲線ext-web.edu.sgu.ac.jp/HIKO/CG/print/print14_7.20_27.pdf · cg プログラミング論 平成28 年7 月20 日 143 第13章.補間曲線-ベジェ曲線

CG プログラミング論 平成 28 年 7 月 20 日

161

void ShadeCalc2() {

double HousenV_x,HousenV_y,HousenV_z; //面の法線ベクトルの成分

double HousenV; //面の法線ベクトルの大きさ

double V1_x,V1_y,V1_z,V2_x,V2_y,V2_z;//面上のベクトル V1,V2の成分

double cos_alfad; //cosαd αd:法線ベクトルと光線ベクトルの間の角度

double HV_x,HV_y,HV_z; //Hベクトルの成分

double HV,RayV; //Hベクトル、光線ベクトルの大きさ

double cos_alfas; //cosαs αs:法線ベクトルと Hベクトルの間の角度

// V1 ベクトルの定義

V1_x=x[3]-x[1];

V1_y=y[3]-y[1];

V1_z=z[3]-z[1];

// V2 ベクトルの定義

V2_x=x[2]-x[0];

V2_y=y[2]-y[0];

V2_z=z[2]-z[0];

// 面(ポリゴン)の法線ベクトルの定義

HousenV_x=V1_y*V2_z-V1_z*V2_y;

HousenV_y=V1_z*V2_x-V1_x*V2_z;

HousenV_z=V1_x*V2_y-V1_y*V2_x;

//法線ベクトルの大きさ

HousenV=Math.sqrt( HousenV_x*HousenV_x+HousenV_y*HousenV_y

+HousenV_z*HousenV_z);

//光線ベクトルの大きさ

RayV=Math.sqrt(RayX*RayX+RayY*RayY+RayZ*RayZ);

// 光線ベクトルと面(ポリゴン)の法線ベクトルとの内積

cos_alfad=(HousenV_x*RayX+HousenV_y*RayY+HousenV_z*RayZ)

/HousenV/RayV;

cos_alfad=-cos_alfad;//裏面に対する修正部分

// ディフューズ(Diffuse)成分強度の計算

D_strength=Math.max(0,cos_alfad);

// スペキュラー(Specular)成分強度の計算

HV_x=Vx+RayX;

HV_y=Vy+RayY;

HV_z=Vz+RayZ;

HV=Math.sqrt(HV_x*HV_x+HV_y*HV_y+HV_z*HV_z);

cos_alfas=(HousenV_x*HV_x+HousenV_y*HV_y+HousenV_z*HV_z)

/HousenV/HV;

cos_alfas=-cos_alfas;//裏面に対する修正部分

S_strength=Math.pow(cos_alfas,40);

}

この修正の後に実行すると、次のようにワイングラスの口(裏面)部分がきちんと描かれ

ます。

Page 20: 第13章.補間曲線-ベジェ曲線ext-web.edu.sgu.ac.jp/HIKO/CG/print/print14_7.20_27.pdf · cg プログラミング論 平成28 年7 月20 日 143 第13章.補間曲線-ベジェ曲線

CG プログラミング論 平成 28 年 7 月 20 日

162

プログラムを作成したら実行し、上のように描画されることを確認して下さい。

※ この課題は、実行結果を確認後「実行結果を確認しました」書いて送ってください。

さて、実はまだもう一つ改良すべき点が残っています。上のプログラムで、グラスの下か

ら眺めるケースを試してみましょう。Vy=-1 に変更して描画して下さい。すると次のよう

になるはずです。

Page 21: 第13章.補間曲線-ベジェ曲線ext-web.edu.sgu.ac.jp/HIKO/CG/print/print14_7.20_27.pdf · cg プログラミング論 平成28 年7 月20 日 143 第13章.補間曲線-ベジェ曲線

CG プログラミング論 平成 28 年 7 月 20 日

163

これは少しおかしな描画になっています。本来であれば、次のように描画されなければな

りません。

これは、手前のグラス表面の描画の後に、

本来は当該視点からは隠れて見えない(向

こう側)の面の裏面を描いてしまったため

に起こった現象です。

Page 22: 第13章.補間曲線-ベジェ曲線ext-web.edu.sgu.ac.jp/HIKO/CG/print/print14_7.20_27.pdf · cg プログラミング論 平成28 年7 月20 日 143 第13章.補間曲線-ベジェ曲線

CG プログラミング論 平成 28 年 7 月 20 日

164

このような問題はどの様に解決できるでしょうか?具体的に下図のような場合を考えてみ

ましょう。この例の場合、面Cは、面Aに(全部、あるいは部分が)隠れて見えない面と

なります。また、面 B の裏面も同様です。

面A

面B

面C

視線ベクトル

法線ベクトル

このような場合、「面を塗る順番を工夫する」ことによって、正しく描画することができま

す。つまり、視点から見て奥の方にある面を先に描き、手前にある面を後から描くのです。

すると、遠方の面と手前の面が重なっていても、後から塗る(手前の)面が上書きされる

ので、自然に正しく描画されることになります。すると、面の奥行きに従って、描画順序

の並べ替え(ソート)を行うことがポイントになります。

この考えにしたがって、次の【応用課題 14-D】でシェーディングを改良しましょう。

【応用課題 14-D】

【応用課題 14-C】のプログラムにおいて、Shading.java を開いて下さい。そして、

MyPaint()メソッドを次のように修正して下さい。下線部が修正部分で、点線枠部分が新

たに加えた部分です。

Page 23: 第13章.補間曲線-ベジェ曲線ext-web.edu.sgu.ac.jp/HIKO/CG/print/print14_7.20_27.pdf · cg プログラミング論 平成28 年7 月20 日 143 第13章.補間曲線-ベジェ曲線

CG プログラミング論 平成 28 年 7 月 20 日

165

public void MyPaint(Graphics g) {

int[] xp=new int[Np];

int[] yp=new int[Np];

・・・

double intensity,Max_intensity;

int[] rgb=new int[Np1*Np2]; //光の強度を格納する配列

Max_intensity=P_a+P_d+P_s;

double[] dist=new double[Np1*Np2]; //視点から面までの距離

int[][] xp_sort=new int[Np1*Np2][Np];

int[][] yp_sort=new int[Np1*Np2][Np];

int No=0;

for(int i=0;i<Np1;i++) {

for(int j=0;j<Np2;j++) {

for(int k=0;k<Np;k++) {

x[k]=faceX[i][j][k]; //ポリゴンの各頂点のx座標

y[k]=faceY[i][j][k]; //ポリゴンの各頂点のx座標

z[k]=faceZ[i][j][k]; //ポリゴンの各頂点のx座標

}

zahyo_henkan(); //ポリゴンの各頂点の座標を視点座標系へ変換

for (int k = 0; k < Np; k++) {

xp[k] = xc + (int) xv[k];

yp[k] = yc - (int) yv[k];

xp_sort[No][k]=xp[k];

yp_sort[No][k]=yp[k];

}

dist[No]=zv[0]+zv[1]+zv[2]+zv[3]; //面から視点までの距離

if(Kashi_hantei()) {

ShadeCalc();

intensity=P_a+P_d*D_strength+P_s*S_strength;//目に入って来

る光の強度

rgb[No]=(int) (255*intensity/Max_intensity);

}

else {

ShadeCalc2();

intensity=P_a+P_d*D_strength+P_s*S_strength;//目に入って来

る光の強度

rgb[No]=(int) (255*intensity/Max_intensity);

}

No=No+1;

}

}

SortFace(Np1*Np2,dist,xp_sort,yp_sort,rgb); //面の順序をソート

for(int i=0;i<Np1*Np2;i++) { //視点から遠い順にシェーディングを行う

g.setColor(new Color(rgb[i], rgb[i], rgb[i] ));

g.fillPolygon(xp_sort[i], yp_sort[i], Np);

}

}

Page 24: 第13章.補間曲線-ベジェ曲線ext-web.edu.sgu.ac.jp/HIKO/CG/print/print14_7.20_27.pdf · cg プログラミング論 平成28 年7 月20 日 143 第13章.補間曲線-ベジェ曲線

CG プログラミング論 平成 28 年 7 月 20 日

166

【プログラムの解説】

① 視点から面までの距離 distは、各点の距離の合計としています。

② 視点からの距離が遠い順に描画面をソートするメソッドです。次のように記述して下

さい。選択ソートを使って、距離 distが大きい順にソートしています。

void SortFace(int NMax,double[] dist,int[][] xp,int[][] yp

,int[] rgb) {

int TempI,Pos;

double TempD;

int[] TempP=new int[Np];

for (int i=0;i<NMax-1;i++) {

Pos=i;

for (int j=i+1; j<NMax;j++) {

if(dist[j] > dist[Pos] ) {

Pos=j;

}

}

TempD=dist[i];

dist[i]=dist[Pos];

dist[Pos] = TempD;

TempP=xp[i];

xp[i]=xp[Pos];

xp[Pos] = TempP;

TempP=yp[i];

yp[i]=yp[Pos];

yp[Pos] = TempP;

TempI=rgb[i];

rgb[i]=rgb[Pos];

rgb[Pos] = TempI;

}

}

作成したら、プログラムを実行し、次ページの様に表示されることを確認して下さい。

XV

YV

ZV

0

1

2 3

zv[0]

zv[1]

zv[2]

zv[3]

Page 25: 第13章.補間曲線-ベジェ曲線ext-web.edu.sgu.ac.jp/HIKO/CG/print/print14_7.20_27.pdf · cg プログラミング論 平成28 年7 月20 日 143 第13章.補間曲線-ベジェ曲線

CG プログラミング論 平成 28 年 7 月 20 日

167

※ この課題は、実行結果を確認後「実行結果を確認しました」書いて送ってください。