講義プリント

CG プログラミング論 平成 27 年 7 月 8 日
第12章.シェーディング
【学習のねらい】
① 先週の学習で、球面をポリゴンに分割して表現
し、さらに隠面消去を行いました。それに、光
源の方向や、表面の材質に応じた光の反射の仕
方等を考慮して色を塗ると、例えば右のような
CG を描くことができます。一般に、視線や光
源の方向、そして物体表面での光の反射のされ
方等を考慮して着色する際の色の塗り方、つま
り塗りつぶしのアルゴリズムをシェーディング
(Shading)と呼びます。今回はこのシェーデ
ィングを学習しましょう。
12-1
シェーディング(Shading)の基本的考え
端的に言えば、シェーディングとは、光の当たり具合によって(対象とする物体表面の)
濃淡が変化する状態を表現するアルゴリズム、という事になります。もちろん、光が物体
の表面でどのように散乱されて目に入ってくるかは物理学の法則に従っているので、それ
を忠実に再現するのは非常に手間がかかります。そこで、通常は、ある簡単化された物理
モデル(今の場合光学モデル)を用います。それらをシェーディングモデルと呼びます。
ここでは、最も簡単なシェーディングモデルによって、シェーディングを実現する事にし
ます。以下、順に基本的な考えを説明して行きましょう。
まず、物体表面に反射されてから目に入ってくる光は、①表面で反射された光が直接入
ってくるもの(スペキュラー:specular)、②光源からやってくる光が物体の表面や内部で
乱反射されて目に入るもの(ディフューズ:diffuse)、③周りの物体の照り返しや空気によ
る散乱光(アンビエント:ambient)、の 3 つの成分からなる事が分かっています。
★ スペキュラー(specular:鏡面反射光)
金属や、表面を磨いた物体などでは、光源からの光が表面から反射されて直接目に入っ
て来ます。これをスペキュラー(鏡面反射光)
、あるいはハイライトとも呼びます。スペキ
ュラー成分の強度は、光源と面の位置関係、および視点の位置によって決まります。
★ ディフューズ(diffuse:拡散反射光)
物体の内部に入った光はその中で反射を繰り返し、出てきたときには方向性が失われた
131
CG プログラミング論 平成 27 年 7 月 8 日
散乱光となります。これをディフューズ成分と呼びます。この強さは光源と面の位置関係
によってのみ決まり、視線の方向とは無関係になります。
★ アンビエント(ambient:環境光)
まわりの物体(空気も含む)からの照り返しを光源とした色の成分です。周囲に光を反
射する物体がなければ、この成分はほぼ 0 になります。通常は、アンビエント成分は物体
上のどこでも一定であるとして処理します。大きなアンビエント成分をつけることによっ
て発光体を表現することも出来ます。
ある物体を眺めた時、目に入って来る光は上の 3 つの成分からなるので、その(目に入
って来る)光の強度は次のような式で表す事ができます。
光の強度=Pa+Pd×d+Ps×s,
0≦d≦1,
0≦s≦1
(1)
Pa:アンビエント成分の最大強度、 Pd:ディフューズ成分の最大強度
Ps:スペキュラー成分の最大強度
ここに、Pa、Pd そして Ps は各成分の最大強度で、物体の材質や周りの環境で決まるもの
です。そして d とsが面上の各点で決まるディフューズ成分、スペキュラー成分の強度で、
これを具体的に計算するためには、次節で述べるシェーディングモデルが必要になります。
参考までに、下に、スペキュラー成分、そしてディフューズ成分のみを用いて描画した
例を示します。
<描画例>
スペキュラー成分のみの場合
ディフューズ成分のみの場合
132
CG プログラミング論 平成 27 年 7 月 8 日
12-2
シェーディングモデル
まず、ディフューズ成分について考えましょう。この成分の強度は入射光の強度に比例
すると考えられます。
光源

VN 法線ベクトル

VR
αd
光線ベクトル


入射光の強度は、上の図のように対象となる面の法線ベクトル VN と光線ベクトル V R との
間の角度αd が小さいほど強い、つまり真上から照らされるほど強度が強い、ということは
直感的に分かるでしょう。実際、入射光の強度は cosαd に比例します。そして cosαd が負
になると光源は面の裏側(反対側)になるので強度は 0 になります。そこで、ディフュー
ズ成分の各点における強度dは次のように表されます。
d=max(0,cosαd)
(2)
ここに、max(a,b)は(a,b)の内、大きな値の方をとる関数を意味します。
次に、スペキュラー成分については、もし平面が完全に滑らかであれば(光の反射の法
則により)入射角と反射角は等しいので、反射光の向きは下の様に一義的に決まります。
光源

VR
αd

VN 法線ベクトル

αd
VS
反射光ベクトル
光線ベクトル

しかし、実際には物体の表面がざらついているので、つまり凹凸があるので上の V S 以外

の方向にも散乱されます。しかし、その光は本来の V S の方向から離れるほど弱くなると考
えられます。そこで、本来の反射方向からのずれを表すために、次のようなベクトルを導
入します。
133
CG プログラミング論 平成 27 年 7 月 8 日



VH  VR  VE
そしてこのベクトルと面の法線ベクトルとの間の角度を下図のようにαs とすると、この
角度は本来の反射角からのずれを表します。実際、視線ベクトルが反射ベクトルの方向に
一致する場合はαs が 0 になります。
光源
法線ベクトル

VN
光線ベクトル

VR

VH
αs

V E 視線ベクトル
そこで、スペキュラー成分の強度sについては、次の式で表されることが多いです。
s=(cosαs)n
(3)
もっと精密なモデルもあるのですが、簡単のため、ここではこの式を用いる事にします。n
については磨かれた金属の様に物体の面が滑らかな程、値を大きくします。そしてざらつ
いた表面の場合は、nが小さな値に対応します。下の描画例を参考にして下さい。
<スペキュラー成分のみの描画>
n=10 の場合
n=50 の場合
さて、以上より(2)式と(3)式を p.132 の(1)式に代入すると、面上の各点から目に入ってい
る光の強度が計算できるので、明るさの濃淡をリアルに表現できる事になります。次節で
プログラムを作成し、実際に描画してみましょう。
134
CG プログラミング論 平成 27 年 7 月 8 日
12-3
球面のシェーディング
【応用課題 12-A】
上の考えに従って、次のように球面にシェーディングを施して描画するプログラムを作
成しましょう。これは、
【応用課題 11-B】を改良して作成します。
視線ベクトルの成分
jTextFieldRayX
jTextFieldRayY
jTextFieldRayZ
光線ベクトルの成分
基本となるプログラムは【基礎課題 11-B】です。このプログラムをコピーして本課題用
プロジェクトとし、プログラムを開いた状態にして下さい。
135
CG プログラミング論 平成 27 年 7 月 8 日
<プログラム作成手順>
①
まず、フレームに上のように光線ベクトルの成分を入れる欄を加えて下さい。Name
プロパティは上図の通りです。
② 次に、シェーディング処理を行うクラス「Shading」を作成しましょう。プロジェクト
の中に、次の様に新規クラスを「Shading」という名前で作成(追加)して下さい。こ
こにスーパークラスを、以前作成した Hidden3D としている事に注意して下さい。つ
まり Hidden3D を継承して Shading クラスを作成する訳です。
パッケージ名は各自によって異なる。
クラス名は「Shading」
スーパークラスは「ouyou8_7_a.Hidden3D」とする。
なお、スーパークラスは、
「パッケージ名.スーパークラス名」と指定するので、もしパ
ッケージ名が「ouyou8_7_a」と異なる場合は、パッケージ名部分を各自の名前に合わせ
て変更して下さい。
136
CG プログラミング論 平成 27 年 7 月 8 日
③ すでに、NewJFrame、MyPanel、WireFrame および Hidden3D クラスは作成してい
るので、次のような状態になるはずです。この状態で、新規クラス「Shading」を記述
します。package 名はそれぞれ各自のプロジェクトによって異なって結構です。適宜、
自分のパッケージ名に読み替えて下さい。
※
表示幅の関係で NewJFrame.java は隠れている。
④ Shading クラスを次ページのように記述します。
package ouyou8_7_a;
import java.awt.*;
パッケージ名は各自のプログラムに応じて
異なる事に注意。
public class Shading extends Hidden3D {
double P_a; //Pa(アンビエント成分の最大強度)
double P_d,D_strength; //Pd(ディフューズ成分の最大強度),d
double P_s,S_strength; //Ps(スペキュラー成分の最大強度),s
double RayX,RayY,RayZ; //光線ベクトルの成分
public void setData(double Vx,double Vy,double Vz,int Np
,int Np1,int Np2,double[][][] faceX
,double[][][] faceY,double[][][] faceZ
,double RayX,double RayY,double RayZ
,double P_a,double P_d,double P_s) {
・・・・
}
public void MyPaint(Graphics g) {
・・・
}
void ShadeCalc() {
・・・
}
}
メソッド「setData」、
「MyPaint」そして「ShadeCalc」の詳細は次ページ以降に示す通
りです。
137
CG プログラミング論 平成 27 年 7 月 8 日
<setData メソッド>
スーパークラスである Hidden3D の setData メソッドに、光の各成分の最大強度および
光線ベクトルの各成分を受け取る部分を加えています。
public void setData(double Vx,double Vy,double Vz,int Np
,int Np1,int Np2,double[][][] faceX
Hidden3D ク ラ ス の
,double[][][] faceY,double[][][] faceZ
,double RayX,double RayY,double RayZ
setData()メソッドの処
,double P_a,double P_d,double P_s) {
理を実行。
super.setData(Vx,Vy,Vz,Np,Np1,Np2,faceX,faceY,faceZ);
this.P_a=P_a; //アンビエント成分の最大強度
this.P_d=P_d; //ディフューズ成分の最大強度
新たな処理として
this.P_s=P_s; //スペキュラー成分の最大強度
this.RayX=RayX; //光線ベクトルのx成分
加えた部分
this.RayY=RayY; //光線ベクトルのy成分
this.RayZ=RayZ; //光線ベクトルのz成分
}
138
CG プログラミング論 平成 27 年 7 月 8 日
<ShadeCalc メソッド>
ここで、ある面
(ポリゴン)
から目に入って来る光のディフューズ成分の強度 D_strength、
スペキュラー成分の強度 S_strength を計算します。
void ShadeCalc() {
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;
// ディフューズ(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;
S_strength=Math.pow(cos_alfas,40);
}
}
※ Math.pow(x,n)は xn を意味します。
139
CG プログラミング論 平成 27 年 7 月 8 日
<MyPaint メソッド>
ここで、画像を描画します。Hidden3D クラスの 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=125; //描画中心のx座標
int yc=125; //描画中心の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座標
}
if(Kashi_hantei()) {
zahyo_henkan(); //ポリゴンの各頂点の座標を視点座標系へ変換
for (int k = 0; k < Np; k++) {
xp[k] = xc + (int) Xv[k];
yp[k] = yc - (int) Yv[k];
}
ShadeCalc(); //光の各成分の強度を計算
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);
g.drawPolygon(xp, yp, Np);
}
}
}
}
140
CG プログラミング論 平成 27 年 7 月 8 日
⑤ NewJFrame.java に戻って、DrawGraphics メソッドを次のように修正して下さい。
下線部と点線枠内が修正部分です。コメントからプログラムの意味は分かるでしょう。
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[200][200][4];
double[][][] faceY=new double[200][200][4];
double[][][] faceZ=new double[200][200][4];
int Np1=40,Np2=40,Np=4,r=100;
Sphere(Np1,Np2,faceX,faceY,faceZ,r);//球を描くためのポリゴンの座
標の計算
//光線ベクトルの定義
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; //光の各成分の最大強度の設定
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); //画像の描画
}
完成したら、プログラムを実行し、p.135 の様に表示される事を確認して下さい。
※ この課題は、実行結果を確認後「実行結果を確認しました」と書いて送って下さい。
141
CG プログラミング論 平成 27 年 7 月 8 日
【応用課題 12-B】
上 の 課 題 (【 応 用 課 題
12-A】)を用いて、今度
は右のように、θ、φ方
向のメッシュ数を 200 に
して描きましょう。その
た
め
に
は
、
DrawGraphics メソッド
の中のどの部分を修正す
れば良いですか?修正し
た部分のみを記述して下
さい。
【応用課題 12-C】
今度は、メッシュ数は 200 としたままで、次の様にスペキュラー成分のみを入れて描い
て下さい。そのためには、DrawGraphics メソッドの中のどの部分を修正すれば良いでしょ
うか?修正した部分のみ
を記述して下さい。
<ヒント>
アンビエント成分およ
び、ディフューズ成分を
0 とします。
142