36

「Javaによる簡単実習3次元CG入門」 サンプルページ · できるようにプログラム例も示しています.本書は,「c++による簡単実習3次元cg入門

  • Upload
    others

  • View
    4

  • Download
    0

Embed Size (px)

Citation preview

Page 1: 「Javaによる簡単実習3次元CG入門」 サンプルページ · できるようにプログラム例も示しています.本書は,「c++による簡単実習3次元cg入門
Page 2: 「Javaによる簡単実習3次元CG入門」 サンプルページ · できるようにプログラム例も示しています.本書は,「c++による簡単実習3次元cg入門

「Java による簡単実習 3次元 CG 入門」

サンプルページ

この本の定価・判型などは,以下の URL からご覧いただけます.

http://www.morikita.co.jp/books/mid/084861

※このサンプルページの内容は,初版 1 刷発行時のものです.

Page 3: 「Javaによる簡単実習3次元CG入門」 サンプルページ · できるようにプログラム例も示しています.本書は,「c++による簡単実習3次元cg入門
Page 4: 「Javaによる簡単実習3次元CG入門」 サンプルページ · できるようにプログラム例も示しています.本書は,「c++による簡単実習3次元cg入門

i

はじめに

コンピュータグラフィックス(CG)は,CAD/CAMはもとより映画,コマーシャル,ゲームにまで利用されるようになり,ますます身近なものとなりました.最近では,コンピュータの性能向上により,パソコンでもフルカラー(約 1600万色)の 3次元 CGができるようになり,見るだけでなく自分で CG画像を作成できるようになっています.CG関係の書籍には,CGのアルゴリズムを学ぶためのものと,CGソフトの使用法を学ぶためのものがあります.本書は前者を目指したもので,理論やアルゴリズムの説明に加えて,実習ができるようにプログラム例も示しています.本書は,「C++による簡単実習 3次元 CG入門」をJava言語用に改訂したものです.内容として,メタボール,分散レイトレーシングを追加しました.また,C++言語にはクラス間の演算を定義する機能がありますが,Java言語にはその機能がないので,変換プログラムを用いて補っています.本書の特長としては,利用範囲が広いようにパソコンを使用して,プログラム例を介して容易に 3次元 CGの実習ができるようにしていることです.また,点,線分,座標変換などを数学の教科書の式のように記述できることです.すなわち,CGアルゴリズムの関係式を,そのままプログラムにもち込むことを目指しています.本書のプログラム例では,CGのアルゴリズムを主に記述しており,アルゴリズムに直接関係のない部分はできる限り簡略化して,簡潔でわかりやすいプログラミングを心掛けています.座標変換などを簡潔に記述するために,クラスどうしの演算を記述できるようにしています.また,プログラム例を容易に理解できるように,Java言語の簡単な機能を使用しています.付録 Cで Java言語について簡単に説明してありますので,プログラミングの経験があればとくにJava言語の書籍を用意する必要はありません.また,本書で使用しているベクトルや行列などの数学の基礎についても付録 Aで説明してあります.Java言語の実行環境はインターネットでダウンロードすることができ,無料で構築することができます.また,Java言語はWindows,Linux,Macなど,ほとんどのパソコンで利用可能です.以下に本書の特長を示します.

(1) テキストの利用範囲が広い・大学,短大の情報系の学科およびコンピュータ専門学校で,CGアルゴリズムを学ぶ学生を対象としている.

・学校で使用しやすいように,15回程度(90分/回)の授業で扱える内容としている.1章(1回,実行環境構築を含まない場合),2章(2回),3章(2回),4章(2回),5章(2回),6章(3回),7章(2回),8章(1回)

・ Java言語が使用できる環境で実習ができる(Windows,Linux,Macなど).(2) CGのアルゴリズム(原理)の説明がわかりやすい・できるだけ,アルゴリズムを前面に出している.・プログラム例を豊富に載せている.

Page 5: 「Javaによる簡単実習3次元CG入門」 サンプルページ · できるようにプログラム例も示しています.本書は,「c++による簡単実習3次元cg入門

ii はじめに

・ (3)および (4)により,簡潔でわかりやすいプログラムが実現されている.

(3) ウィンドウシステムの知識がいらない

・ウィンドウ操作を簡単にするために,単純なメソッドを用意している.・簡単に複数ウィンドウを使用できる.

(4) プログラムの記述が簡潔である(数学の教科書に近い記述が可能)

・ x, y, z の各座標ごとの記述は必要ない(プログラムがスッキリする).【例】ベクトル a(2,0,1)とベクトル b(1,1,1)を加算して,ベクトル cに代入する.

Vector a = new Vector(2,0,1);

Vector b = new Vector(1,1,1);

Vector c = a + b; // ベクトルの加算

【例】線分 a((1, 1, 1)− (3, 3, 2))をX 軸を中心に,30度回転して表示する.

Line a = new Line( new Point(1,1,1), new Point(3,3,2) );

TMatrix t = TMatrix.rot_x(30); // X軸を中心に 30度回転する変換行列Point b = t * a; // 座標変換の計算w.draw(b); // 線分 bの表示

(ベクトルや行列の演算についても,付録で解説しています.)

本書のプログラム例は,インターネットを介してダウンロードすることができます.なお,本書における演習以外に関する問い合わせはご遠慮願います.本書の執筆にあたり,御尽力いただいた森北出版の田中節男氏,加藤義之氏に感謝いたします.

  2009年 9月

みちのく盛岡にて 小笠原祐治 

Page 6: 「Javaによる簡単実習3次元CG入門」 サンプルページ · できるようにプログラム例も示しています.本書は,「c++による簡単実習3次元cg入門

iii

目  次

第 1章 実行環境構築と描画手順 1

1.1 実行環境の構築方法 1 1.2 描画方法 4

演習問題 10

第 2章 座標変換 11

2.1 座標変換の方法 11 2.2 平行移動 14

2.3 回転移動 16 2.4 スケーリング 19

2.5 座標変換の合成 21 演習問題 25

第 3章 ポリゴンモデル 26

3.1 形状モデル 26 3.2 ポリゴンモデルの記述方法 27

3.3 隠面処理 29 演習問題 36

第 4章 光学的モデルとシェーディング 37

4.1 光学的モデル 37 4.2 フラットシェーディング 39

4.3 グーローシェーディング 41 4.4 フォンシェーディング 44

4.5 陰付け 47 演習問題 49

第 5章 レイトレーシングの基礎 50

5.1 描画方法 50 5.2 鏡面反射の表現 57

5.3 透過光の表現 60 演習問題 63

第 6章 より自由な形状の表現 65

6.1 2次曲面 65 6.2 メタボール 68

6.3 立体の演算 71 6.4 パラメトリック曲面 74

6.5 高速化の手法 76 6.6 よりリアルな表現手法 78

演習問題 81

第 7章 マッピング 82

7.1 テクスチャマッピング 82 7.2 バンプマッピング 86

7.3 環境マッピング 88 演習問題 92

第 8章 ボリュームレンダリング 94

8.1 ボリュームデータ 94 8.2 ボリュームレンダリング 94

8.3 3次元テクスチャ 99 演習問題 101

Page 7: 「Javaによる簡単実習3次元CG入門」 サンプルページ · できるようにプログラム例も示しています.本書は,「c++による簡単実習3次元cg入門

iv 目  次

付録A ベクトルおよび行列の演算 102

A.1 2次元ベクトル 102 A.2 3次元ベクトル 102

A.3 ベクトルのかけ算(スカラー倍) 103

A.4 ベクトルの加算,減算 104 A.5 ベクトルの内積(スカラー積) 105

A.6 ベクトルの外積(ベクトル積) 106 A.7 行列の積 107

A.8 2次方程式の解 109

付録B 投影法 110

B.1 投影変換の種類と投影図形 110 B.2 平行投影 110

B.3 中心投影 112

付録C Java言語について 114

C.1 変数とクラス 114 C.2 メソッド 115

C.3 static型のメソッドとフィールド 116

C.4 mainメソッド 117 C.5 if文 118

C.6 for文 118 C.7 配列 119

C.8 演算子の拡張 120

付録D クラス,演算子一覧 122

D.1 クラス一覧 122 D.2 演算子一覧 122

付録E Sceneクラスの使用 124

E.1 モデリング(物体の登録) 124 E.2 レンダリング(描画) 126

E.3 マッピングの指定 127

付録 F プログラム例 129

F.1 2次元のプログラム例(ap1_∗.ox.java) 129

F.2 ポリゴンモデルのプログラム例(ap2_∗.ox.java) 130

F.3 メタボール・CSGモデルのプログラム例(ap3_∗.ox.java) 132

F.4 自由曲面(B-スプライン)のプログラム例(ap4_∗.ox.java) 133

F.5 3次元テクスチャのプログラム例(ap5_∗.ox.java) 133

演習問題の解答例 134

索  引 144

Page 8: 「Javaによる簡単実習3次元CG入門」 サンプルページ · できるようにプログラム例も示しています.本書は,「c++による簡単実習3次元cg入門

1

2

3

4

5

6

7

8

付録

11

2 座標変換

 本章では,2次元(平面)および 3次元(空間)においての座標変換(移動,変形)について考えていきます.その際,数学の教科書の記述に見られるような表現方法(たとえば,点 a,線分 l,行列 tなど)を使って,座標変換の計算(行列の計算)を行います.数字に自信のない方は付録Aを参考にしてください.

2.1 座標変換の方法図形の幾何学的変換について,考えてみます.図形を別の位置に移動あるいは別の図形に変形

(拡大縮小を含む)することを,座標変換といいます.このとき,基本的な変換方法としてつぎのものがあります.① 平行移動② 回転移動③ 拡大縮小(スケーリング)④ ①~③の合成例として,三角形を原点を中心に 3倍に拡大する場合について考えてみましょう.図 2.1のように,三角形 pqrを拡大すると,三角形 p′q′r′ になります.

図 2.1 三角形を拡大する例

ここで,拡大した三角形の点 p′(x′, y′, z′)は,次式のようにもとの三角形の点 p(x, y, z)の各座標値を 3倍することによって計算することができます.

x′ = 3x

y′ = 3y

z′ = 3z

(2.1)

点 q′, r′ も同様に,点 q, rの各座標値を 3倍することによって計算することができます.平行移動や回転移動も考慮すると,座標変換の一般的な式はつぎのようになります.

Page 9: 「Javaによる簡単実習3次元CG入門」 サンプルページ · できるようにプログラム例も示しています.本書は,「c++による簡単実習3次元cg入門

1

2

3

4

5

6

7

8

付録

座標変換

12 第 2 章 座標変換

x′ = t0,0 · x+ t0,1 · y + t0,2 · z + t0,3

y′ = t1,0 · x+ t1,1 · y + t1,2 · z + t1,3

z′ = t2,0 · x+ t2,1 · y + t2,2 · z + t2,3

(2.2)

座標 (x, y, z)は図形を構成する点であり,座標 (x′, y′, z′)は変換後の図形を構成する点を示します.上記の変換は線形変換であるので,直線は直線に変換されます.すなわち,線分は線分に変換されることになり,図 2.2に示すように,線分の両端の点を式 (2.2)で変換することによって,変換後の線分を得ることができます.t0,0 ∼ t2,3 の各値は,変換の種類によって決定されます.

図 2.2 座標変換

式 (2.2)は,行列を用いて次式のように記述することができます(行列の演算については付録B.7を参照).このような変換を,数学では 3次元アフィン変換といいます.

x′

y′

z′

1

=

t0,0 t0,1 t0,2 t0,3

t1,0 t1,1 t1,2 t1,3

t2,0 t2,1 t2,2 t2,3

0 0 0 1

·x

y

z

1

(2.3)

p′ = t · p (2.4)

p = (x, y, z) :変換前の座標(Pointクラス)

p′ = (x′, y′, z′) :変換後の座標(Pointクラス)

t =

t0,0 t0,1 t0,2 t0,3

t1,0 t1,1 t1,2 t1,3

t2,0 t2,1 t2,2 t2,3

0 0 0 1

:変換行列(TMatrixクラス)

結局,座標変換は式 (2.4)で記述することができ,変換行列 t を用いた計算で行うことができます.プログラムでの変換行列の記述のために,TMatrixクラス(リスト 2.1参照)を用います.点 aや線分 lの座標変換は,つぎのように記述することができます.

TMatrix t = new TMatrix(1.0, 0.0, 0.0, 0.0 // 変換行列の作成0.0, 1.0, 0.0, 100.00.0, 0.0, 1.0, 0.00.0, 0.0, 0.0, 1.0);

Page 10: 「Javaによる簡単実習3次元CG入門」 サンプルページ · できるようにプログラム例も示しています.本書は,「c++による簡単実習3次元cg入門

1

2

3

4

5

6

7

8

付録

座標変換

132.1 座標変換の方法

Point a = new Point(10,10,10);

Point b = t * a; // 点の座標変換Line l = new Line(new Point(10,10,10), new Point(20,20,10));

Line m = t * l; // 線分の座標変換

線分との積の場合は,線分を構成するすべての頂点座標を変換します.なお,TMatrix クラスを用いた式 (2.4) の計算では,式 (2.2) の計算を行っており,式 (2.3) の 4 行目の計算(1 = 0 · x+ 0 · y + 0 · z + 1 · 1)は省略しています.2次元の場合は Z 座標がないので,Z 成分を省略した形式となり,変換行列は 3行 3列となるか,あるいは,Z 座標を 0とした形式となります.

リスト 2.1 TMatrix.ox.java

1 // 座標変換用行列2  3 public class TMatrix{

4  5 public static final double PIR = Math.PI/180.0;

6 public double[ ][ ] t = new double[4][4]; // 変換行列7  8 public TMatrix() {init();}

~~~~~~~~~~~~~~~~~~~~~22 public TMatrix(double m00, double m01, double m02, double m03,23 double m10, double m11, double m12, double m13,24 double m20, double m21, double m22, double m23,25 double m30, double m31, double m32, double m33){26 t[0][0] = m00; t[0][1] = m01; t[0][2] = m02; t[0][3] = m03;27 t[1][0] = m10; t[1][1] = m11; t[1][2] = m12; t[1][3] = m13;28 t[2][0] = m20; t[2][1] = m21; t[2][2] = m22; t[2][3] = m23;29 t[3][0] = m30; t[3][1] = m31; t[3][2] = m32; t[3][3] = m33;30 }31 void init(){32 for(int i=0; i<4; i++)33 for(int j=0; j<4; j++)t[i][j]=0.0;34 for(int i=0; i<4; i++) t[i][i]=1.0;35 }36  37 // 演算子の記述38 public static Vector operator*(TMatrix m, Vector b){39 return new Vector(m.t[0][0]*b.x+m.t[0][1]*b.y+m.t[0][2]*b.z+m.t[0][3],40 m.t[1][0]*b.x+m.t[1][1]*b.y+m.t[1][2]*b.z+m.t[1][3],41 m.t[2][0]*b.x+m.t[2][1]*b.y+m.t[2][2]*b.z+m.t[2][3]);42 }

~~~~~~~~~~~~~~~~~~~~~55 public static TMatrix operator*(TMatrix m1, TMatrix m2){56 TMatrix m = new TMatrix();57 for(int i=0; i<4; i++)58 for(int j=0; j<4; j++){59 m.t[i][j] = 0;60 for(int k=0; k<4; k++)m.t[i][j] += m1.t[i][k] * m2.t[k][j];61 }62 return m;63 }64  65 // 倍率変換66 public static TMatrix operator*(double a, TMatrix m){67 return TMatrix.scale(a,a,a)*m;68 }

Page 11: 「Javaによる簡単実習3次元CG入門」 サンプルページ · できるようにプログラム例も示しています.本書は,「c++による簡単実習3次元cg入門

1

2

3

4

5

6

7

8

付録

座標変換

14 第 2 章 座標変換

69 public static TMatrix scale(double x, double y, double z){70 TMatrix m = new TMatrix();71 m.t[0][0] = x; m.t[1][1] = y; m.t[2][2] = z;72 return m;73 }74 public static TMatrix scale(Vector a){return scale(a.x,a.y,a.z); }75 public static TMatrix scale(double a){return scale(a ,a ,a); }

76  77 // 回転変換78 static TMatrix rot_x(double sn, double cs){ // X軸を中心に回転79 TMatrix m = new TMatrix();80 m.t[1][1] = cs; m.t[1][2] = -sn;81 m.t[2][1] = sn; m.t[2][2] = cs;82 return m;83 }84 static TMatrix rot_y(double sn, double cs){ // Y軸を中心に回転85 TMatrix m = new TMatrix();86 m.t[0][0] = cs; m.t[0][2] = sn;87 m.t[2][0] = -sn; m.t[2][2] = cs;88 return m;89 }90 static TMatrix rot_z(double sn, double cs){ // Z軸を中心に回転91 TMatrix m = new TMatrix();92 m.t[0][0] = cs; m.t[0][1] = -sn;93 m.t[1][0] = sn; m.t[1][1] = cs;94 return m;95 }96 public static TMatrix rot_x(double r){return rot_x(Math.sin(r*PIR),Math.cos(r*PIR

));}97 public static TMatrix rot_y(double r){return rot_y(Math.sin(r*PIR),Math.cos(r*PIR

));}98 public static TMatrix rot_z(double r){return rot_z(Math.sin(r*PIR),Math.cos(r*PIR

));}

~~~~~~~~~~~~~~~~~~~~~

127 // 平行移動128 public static TMatrix move(double x, double y, double z){129 TMatrix m = new TMatrix();130 m.t[0][3] = x; m.t[1][3] = y; m.t[2][3] = z;131 return m;132 }133 public static TMatrix move(Vector a){134 return move(a.x, a.y, a.z);135 }

~~~~~~~~~~~~~~~~~~~~~177 }

2.2 平行移動平行移動では,移動後の座標 p′ = (x′, y′, z′)は,移動前の座標 p = (x, y, z)に移動値 (ax, ay, az)

を加算することによって求められます.

x′ = x+ ax

y′ = y + ay

z′ = z + az

(2.5)

Page 12: 「Javaによる簡単実習3次元CG入門」 サンプルページ · できるようにプログラム例も示しています.本書は,「c++による簡単実習3次元cg入門

1

2

3

4

5

6

7

8

付録

座標変換

152.2 平行移動

したがって,変換行列は下記のようになります.

t =

1 0 0 ax

0 1 0 ay

0 0 1 az

0 0 0 1

(2.6)

平行移動を行うプログラム例をリスト 2.2(ex2_1.ox.java)に,実行結果を図 2.3 に示します.このプログラムを実行するとウィンドウが開き,変換前の図形(水色の Y字型図形)および平行移動した図形(赤:Z 方向に 100,紫色:X 方向に −150)が表示されます.

リスト 2.2 ex2_1.ox.java

1 // 3次元空間の図形の変換(平行移動)2  3 public class ex2_1{4 public static void main(String[ ] args){5

6 Line l = new Line(new Point[ ]{7 new Point( 0,50,0), new Point(23,27,0), // Y字形の頂点座標の設定8 new Point(23, 0,0), new Point(33, 0,0),9 new Point(33,27,0), new Point(50,50,0),

10 new Point(40,50,0), new Point(30,36,0),11 new Point(16,50,0), new Point( 0,50,0) });12  13 // Y字型図形の平行移動14 Win3D W1 = new Win3D("平行移動",400,350); // ウインドウ生成15 W1.drawAxis(); // 座標軸の表示16 W1.setColor(Color.CYAN); // 描画色の設定17 W1.draw(l); // Y字形の描画18  19 TMatrix t; // 変換行列20 t = new TMatrix(21 1, 0, 0, 0, // 変換行列の設定22 0, 1, 0, 0, // Z方向に10023 0, 0, 1, 100,24 0, 0, 0, 1);25 W1.setColor(Color.RED); // 描画色の設定26 W1.draw(t*l); // 変換して描画27  28 t = new TMatrix(

29 1, 0, 0, -150, // 変換行列の設定30 0, 1, 0, 0, // X方向に-15031 0, 0, 1, 0,32 0, 0, 0, 1);33 W1.setColor(Color.MAGENTA); // 描画色の設定34 W1.draw(t*l); // 変換して描画35 }36 }

Page 13: 「Javaによる簡単実習3次元CG入門」 サンプルページ · できるようにプログラム例も示しています.本書は,「c++による簡単実習3次元cg入門

1

2

3

4

5

6

7

8

付録

座標変換

16 第 2 章 座標変換

図 2.3 平行移動(ex2_1.ox.java)

プログラム例の処理はつぎのとおりです.

・ Y字型の線分 lを作成する. (6~11行目)・ 3次元座標用のウインドウ W1を開く. (14行目)・ 座標軸を描画する. (15行目)・ 描画色を設定する. (16,25,33行目)

・ 変換行列を作成する. (20,28行目)

繰り返す

・ 座標変換および描画をする. (26,34行目)

2.3 回転移動つぎに,X 軸,Y 軸,Z 軸での回転移動について考えてみます.各軸での回転方向を図 2.4に示します.

図 2.4 回転の方向

① X 軸を中心に θ度回転する場合X 軸を中心に回転する場合,Y 座標と Z座標は変化しますが,X 座標は変化しません.そのため,移動後の座標 p′ = (x′, y′, z′)は,次式によって求められます.

Page 14: 「Javaによる簡単実習3次元CG入門」 サンプルページ · できるようにプログラム例も示しています.本書は,「c++による簡単実習3次元cg入門

1

2

3

4

5

6

7

8

付録

座標変換

172.3 回転移動

x′ = x

y′ = y · cos θ − z · sin θz′ = y · sin θ + z · cos θ

(2.7)

変換行列は次式のようになります.

t =

1 0 0 0

0 cos θ − sin θ 0

0 sin θ cos θ 0

0 0 0 1

(2.8)

② Y 軸を中心に θ度回転する場合Y 座標は変化せず,変換行列は次式のようになります.

t =

cos θ 0 sin θ 0

0 1 0 0

− sin θ 0 cos θ 0

0 0 0 1

(2.9)

③ Z 軸を中心に θ度回転する場合Z 座標は変化せず,変換行列は次式のようになります.

t =

cos θ − sin θ 0 0

sin θ cos θ 0 0

0 0 1 0

0 0 0 1

(2.10)

回転移動を行うプログラム例をリスト 2.3(ex2_2.ox.java)に,実行結果を図 2.5 および図 2.6

に示します.このプログラムを実行するとウインドウが 2つ開き,X 軸回転(赤:−70度,紫:−140度)および Z 軸回転(赤:60度,紫:120 度)の結果が表示されます.Java言語では,算術関数を扱うためにMathクラスが用意されています.プログラム例でも,cos関数や sin関数の計算のために使用しています(詳しくは付録 C.3参照).

リスト 2.3 ex2_2.ox.java

1 // 3次元空間の図形の変換(回転移動)2  3 public class ex2_2{4 public static void main(String[ ] args){

5  6 Line l = new Line(new Point[ ]{7 new Point( 0,50,0), new Point(23,27,0), // Y字形の頂点座標の設定8 new Point(23, 0,0), new Point(33, 0,0),9 new Point(33,27,0), new Point(50,50,0),

Page 15: 「Javaによる簡単実習3次元CG入門」 サンプルページ · できるようにプログラム例も示しています.本書は,「c++による簡単実習3次元cg入門

1

2

3

4

5

6

7

8

付録

座標変換

18 第 2 章 座標変換

10 new Point(40,50,0), new Point(30,36,0),11 new Point(16,50,0), new Point( 0,50,0) });12  13 // Y字型図形の回転移動(X軸回転)14 Win3D W1 = new Win3D("X軸回転",400,350); // ウインドウ生成15 W1.drawAxis(); // 座標軸の表示16 W1.setColor(Color.CYAN); // 描画色の設定17 W1.draw(l); // Y字形の描画18  19 double a; // 回転する角度20 a = Math.toRadians(-70); // 度からラジアンに21 TMatrix t; // 変換行列22 t = new TMatrix(23 1, 0, 0, 0, // 変換行列の設定24 0, Math.cos(a), -Math.sin(a), 0, // X軸回転(-70度)25 0, Math.sin(a), Math.cos(a), 0,26 0, 0, 0, 1);27 W1.setColor(Color.RED); // 描画色の設定28 W1.draw(t*l); // 変換して描画29  30 a = Math.toRadians(-140); // 度からラジアンに31 t = new TMatrix(32 1, 0, 0, 0, // 変換行列の設定33 0, Math.cos(a), -Math.sin(a), 0, // X軸回転(-140度)34 0, Math.sin(a), Math.cos(a), 0,35 0, 0, 0, 1);

36 W1.setColor( Color.MAGENTA ); // 描画色の設定37 W1.draw(t*l); // 変換して描画38  39 // Y字型図形の回転移動(Z軸回転)40 Win3D W2 = new Win3D("Z軸回転",400,350); // ウインドウ生成41 W2.drawAxis(); // 座標軸の表示42 W2.setColor(Color.CYAN); // 描画色の設定43 W2.draw(l); // Y字形の描画44  45 a = Math.toRadians(60); // 度からラジアンに46 t = new TMatrix(47 Math.cos(a), -Math.sin(a), 0, 0, // 変換行列の設定48 Math.sin(a), Math.cos(a), 0, 0, // Z軸回転(60度)49 0, 0, 1, 0,50 0, 0, 0, 1);

51 W2.setColor(Color.RED); // 描画色の設定52 W2.draw(t*l); // 変換して描画53  54 a = Math.toRadians(120); // 度からラジアンに55 t = new TMatrix(56 Math.cos(a), -Math.sin(a), 0, 0, // 変換行列の設定57 Math.sin(a), Math.cos(a), 0, 0, // Z軸回転(120度)58 0, 0, 1, 0,59 0, 0, 0, 1);

60 W2.setColor(Color.MAGENTA); // 描画色の設定61 W2.draw(t*l); // 変換して描画62 }63 }

Page 16: 「Javaによる簡単実習3次元CG入門」 サンプルページ · できるようにプログラム例も示しています.本書は,「c++による簡単実習3次元cg入門

1

2

3

4

5

6

7

8

付録

座標変換

192.4 スケーリング

図 2.5 X 軸回転(ex2_2.ox.java) 図 2.6 Z 軸回転(ex2_2.ox.java)

2.4 スケーリングスケーリングは,原点を中心に拡大縮小することであり,X 成分を Sx倍,Y 成分を Sy 倍,Z

成分を Sz 倍する場合の移動後の座標 p′ = (x′, y′, z′)は,次式によって求めることができます.

x′ = Sx · xy′ = Sy · yz′ = Sz · z

(2.11)

したがって,変換行列は次式のようになります.

t =

Sx 0 0 0

0 Sy 0 0

0 0 Sz 0

0 0 0 1

(2.12)

また,パラメータ Sx, Sy, Sz の各値を 1あるいは−1とすることによって,表 2.1のように点,直線,平面に対して対称な変換を行うことができます.

表 2.1 パラメータ Sx, Sy, Sz の値と対称変換の種類

Sx = 1 Sx = −1

Sy = 1 Sy = −1 Sy = 1 Sy = −1

Sz = 1 無変換 XZ 平面 Y Z 平面 Z 軸

Sz = −1 XY 平面 X 軸 Y 軸 原点

スケーリング(赤:3倍,紫:X 方向に 0.5倍,Y 方向に 2倍)と対称変換(赤:Y Z 平面対称,紫:Z 軸対称)を行うプログラム例をリスト 2.4(ex2_3.ox.java)に,実行結果を図 2.7および図 2.8に示します.

Page 17: 「Javaによる簡単実習3次元CG入門」 サンプルページ · できるようにプログラム例も示しています.本書は,「c++による簡単実習3次元cg入門
Page 18: 「Javaによる簡単実習3次元CG入門」 サンプルページ · できるようにプログラム例も示しています.本書は,「c++による簡単実習3次元cg入門

1

2

3

4

5

6

7

8

付録

レイトレ|シングの基礎

515.1 描画方法

図 5.2 球体の描画 図 5.3 球体の描画(ex5_1.ox.java)(口絵 12)

式 (5.1)から tの 2次方程式

t2 + 2t · V • (V 0 −B0) + (V 0 −B0) • (V 0 −B0)− r2 = 0 (5.2)

が得られ,その判別式 dは次式 (5.3)になります(付録 A.8参照).

d = [V • (V 0 −B0)]2 − (V 0 −B0) • (V 0 −B0) + r2 (5.3)

判別式 dが 0以上の場合には球体と交わり,そのときの交点(視点に近い交点)の座標は次式で求められます.

t = −V • (V 0 −B0)−√d

P = V 0 + t · V

(5.4)

視線が球体と交わる場合,交点 P と光源 L0との位置関係から光源ベクトル Lを求めます.また,球体の中心点B0と交点 P との位置関係から法線ベクトルN を求めます.視線ベクトル V,光源ベクトル L,法線ベクトルN から,4章で説明したフォンモデルなどにより物体の色(描画色)を計算することができます.

L =P −L0

|P −L0| (5.5)

N =P −B0

|P −B0| (5.6)

レイトレーシング法を用いて球体を描画するプログラム例をリスト 5.1(ex5_1.ox.java)に,実行結果を図 5.3(口絵 12)に示します.プログラム例では,光線(始点と方向)を記述するための Rayクラス(Ray.ox.java,リスト 5.2)と,球体(中心点と半径)を記述するための Ballクラス(Ball.ox.java,リスト 5.3)を用意して使用しています.描画を行う画素のスクリーン座標(mainメソッド内の x, y)を,worldメソッドによって標準座標に変換して視線ベクトル V を

Page 19: 「Javaによる簡単実習3次元CG入門」 サンプルページ · できるようにプログラム例も示しています.本書は,「c++による簡単実習3次元cg入門

1

2

3

4

5

6

7

8

付録

レイトレ|シングの基礎

52 第 5 章 レイトレーシングの基礎

計算します.つぎに,Ballクラスの hitメソッドによって,視線 V Rが球体 Baと交わるかを判定します.hitメソッドは,視線が交差する場合には視点からの距離を,交差しない場合にはRay.INFINITY (無限遠方を表す大きな値)を返します.視線が球体と交わる場合には,Rayクラスの shadingメソッドによって球体上の交点 P の色を計算して描画します.

リスト 5.1 ex5_1.ox.java

1 // 球体を表示する(レイトレーシング法)2  3 public class ex5_1{ // 球体を表示する(レイトレーシング法)4 public static void main(String[ ] args){5

6 Ball Ba = new Ball(new Point(0,0,70),70); // 球体の記述7 Point L0 = new Point( 0,400,1000); // 照明光の記述8 Point V0 = new Point(500,500, 500); // 視点の記述9 Win3D W1 = new Win3D("球の描画",400,350, 0.25*Color.CYAN);10 W1.setView(V0);11  12 for(int x=0; x<W1.size_x; x++)13 for(int y=0; y<W1.size_y; y++){

14 Vector V = (W1.world(new Point(x,y,-W1.dv))-V0).unit(); // 視線ベクトルの計算15 Ray VR = new Ray(V0,V); // 視線の記述16 Point P = new Point(); // 交点座標17 Vector N = new Vector();18 if(Ba.hit(VR, P, N)<Ray.INFINITY ){ // 視線が球体と交わるかの判定19 Vector L = (P - L0) .unit(); // 照明光の方向ベクトル20 W1.setColor(VR.shading(L, N, Color.RED)); // 描画色の設定21 W1.w.point (x,y); // screen座標で描画22 }23 }24 }25 }

リスト 5.2 Ray.ox.java

1 // 視線,光線のクラス2  3 public class Ray{

4  5 public static double INFINITY = 1e12; // 無限遠方6 public static double DEL = 1e - 3; // 計算誤差7 public static int N_SHADE = 20; // 鏡面反射の係数8  9 public Point o; // 始点座標10 public Vector d; // 方向ベクトル11  12 public Ray(Point og, Vector di){13 o = og;14 d = di;15 }16 public Ray(Ray a){17 o = new Point(a.o);18 d = new Vector(a.d);19 }20  21 // 反射光を計算する L:照明方向ベクトル N:法線ベクトル22 public Color shading(Vector L, Vector N, Color c){23 if(0<N*L)return shading(L, N, c, 0);

Page 20: 「Javaによる簡単実習3次元CG入門」 サンプルページ · できるようにプログラム例も示しています.本書は,「c++による簡単実習3次元cg入門

1

2

3

4

5

6

7

8

付録

レイトレ|シングの基礎

535.1 描画方法

24 else return shading(L, N, c, 1);25 }26  27 // 反射光を計算する L:照明方向ベクトル N:法線ベクトル s:照明光強度28 public Color shading(Vector L, Vector N, Color c, double s){

29 double kd = 0.7, ks = 0.7, ke = 0.3; // 拡散反射,鏡面反射,環境光30 double NL = N * L;31 Vector R = L - 2 * NL * N; // 反射方向ベクトル32 Color Cd = kd * Math.max(-NL,0) * s * c; // 拡散反射光33 Color Cs = ks * Math.pow(Math.max(-R*d,0),N_SHADE) * s * Color.WHITE;// 鏡面反射光34 Color Ce = ke * c; // 環境光35 return Cd + Cs + Ce;36 }

~~~~~~~~~~~~~~~~~~~~~44 }

リスト 5.3 Ball.ox.java

1 // 球体のクラス2  3 public class Ball{

4  5 public Point o; // 中心座標6 public double r; // 半径7  8 public Ball(){9 o = new Point(0,0,0);

10 r = 0;11 }12 public Ball(Point og, double ra){13 o = og;14 r = ra;15 }16 public Ball(Ball a){17 o = new Vector(a.o);18 r = a.r;19 }

20 public double hit(Ray VR, Point P, Vector N){ // 交点座標を返す21 Point Vo = VR.o - o;22 double ds = VR.d * Vo;

23 double d2 = ds * ds - Vo * Vo + r * r; // 判別式24 if(d2<0) return Ray.INFINITY;

25 double t = -ds - Math.sqrt(d2); // 距離26 if(t<Ray.DEL) t = -ds + Math.sqrt(d2);27 P.copy(VR.o+t*VR.d);

28 N.copy((P-o).unit()); // 交点座標,法線ベクトル29 return t;30 }

~~~~~~~~~~~~~~~~~~~~~45 }

( 2 ) 平面(ポリゴン)の描画

ポリゴン(平面上の多角形)の描画について考えてみます.図 5.4に示すように,視線がポリゴン(平面上の点 P 0,法線ベクトルN(面に垂直なベクトル))と交わる場合,光源 L0との位置関係からポリゴンの色を求めて描画します.

Page 21: 「Javaによる簡単実習3次元CG入門」 サンプルページ · できるようにプログラム例も示しています.本書は,「c++による簡単実習3次元cg入門

1

2

3

4

5

6

7

8

付録

レイトレ|シングの基礎

54 第 5 章 レイトレーシングの基礎

図 5.4 平面の描画

まず,視線とポリゴンを含む平面との交点 P を求めます.視線ベクトル V と交点 P との関係,および平面上の点 P 0の関係から次式が成り立ちます(tは視点から交点までの距離を意味します.P , P 0は各点の座標を表すとともに,原点から各点までのベクトルを表すものとします).

V 0 + t · V = P

(P 0 − P ) ·N = 0

(5.7)

式 (5.7)から交点 P を求めることができます.

t =(P 0 − V 0) •N

V •NP = V 0 + t · V

(5.8)

つぎに,求めた交点がポリゴン内部にあるかどうかを,次式の条件を満たすかどうかによって判定します.

[(P 1 − P 0)× (P − P 0)] •N ≥ 0

[(P 2 − P 1)× (P − P 1)] •N ≥ 0

[(P 3 − P 2)× (P − P 2)] •N ≥ 0

[(P 0 − P 3)× (P − P 3)] •N ≥ 0

(5.9)

図 5.5のようにポリゴンの各辺をベクトルとした場合,交点 P が各辺の左側に位置することを判定しています(ただし,この判定方法は,凹型ポリゴンには使用できません).“×”はベクトルの外積(付録 A参照)を表します.図 5.6に示す球体と床を描画するプログラム例をリスト 5.4(ex5_2.ox.java)に,実行結果を図

5.7(口絵 13)に示します(効率的な交差判定の方法として,Tomas Mollerや Arenbergのアルゴリズムなどがあります).

Page 22: 「Javaによる簡単実習3次元CG入門」 サンプルページ · できるようにプログラム例も示しています.本書は,「c++による簡単実習3次元cg入門

1

2

3

4

5

6

7

8

付録

レイトレ|シングの基礎

555.1 描画方法

図 5.5 交点の判定 図 5.6 球体と床の描画

図 5.7 球体と床の描画(ex5_2.ox.java)(口絵 13)

リスト 5.4 ex5_2.ox.java

1 // 物体(球体,床)を複数にする2  3 public class ex5_2{ // 球体を表示する(レイトレーシング法)4 public static void main(String[ ] args){

5  6 Ball Ba = new Ball(new Point(0,0,70),70); // 球体の記述7 Polygon Wa = TMatrix.scale(200,200,-50) * Polygon.square_xy(); //床の記述8 Color Cb = Color.RED;9 Color Cw = 0.3 * Color.WHITE + 0.4 * Color.BLUE;

10  11 Win3D W1 = new Win3D("陰の処理",400,350);12 Point V0 = new Point(500,500, 500); // 視点の記述13 Point L0 = new Point( 0,400,1000); // 照明光の記述14 W1.setView(V0);15  16 for(int x=0+0; x<W1.size_x+0; x++)17 for(int y=0; y<W1.size_y; y++){

18 Vector V = (W1.world(new Point(x, y, -W1.dv))-V0).unit(); // 視線ベクトルの計算19 Ray VR = new Ray(V0, V); // 視線の記述20 float s = 1; // 日向か日陰を表す21  22 Point P = new Point(); // 交点座標23 Vector N = new Vector(), D = new Vector();24 Color Cs = 0.25 * Color.CYAN;25 if(Ba.hit(VR, P, N)<Ray.INFINITY){ // 視線が球体と交わるかの判定26 Vector L = (P - L0).unit(); // 照明光の方向ベクトル27 Cs = VR.shading(L, N, Cb); // 球体の色

Page 23: 「Javaによる簡単実習3次元CG入門」 サンプルページ · できるようにプログラム例も示しています.本書は,「c++による簡単実習3次元cg入門

1

2

3

4

5

6

7

8

付録

レイトレ|シングの基礎

56 第 5 章 レイトレーシングの基礎

28 }29 else if(Wa.hit(VR, P, N)<Ray.INFINITY){ // 視線が床と交わるかの判定30 Vector L = (P - L0).unit(); // 照明光の方向ベクトル31 Ray Rl = new Ray(L0, L); // 照明光線の記述32 if(Ba.hit(Rl, P, D)<Ray.INFINITY)s = 0; // 照明光が球体と交わるかの判定33 Cs = VR.shading(L, N, Cw, s); // 床の色34 }

35 W1.setColor(Cs); // 描画色の設定36 W1.w.point(x, y); // screen座標で描画37 }38 }39 }

プログラムでは,床(壁)を記述するために,Polygonクラス(Polygon.ox.java,リスト 5.5)を使用しています.Ballクラスと同様に,hitメソッドによって視線と交わるか判定します.スクリーンを構成する画素の描画手順を,図 5.8のフローチャートに示します.物体が複数になると,ほかの物体の日陰(照明光がほかの物体に遮られて,当たらない)になる場合があります.そのため,視線 V Rから見えるところ(視線との交点 P)はどこで,またそこには照明光が当たっているかどうかを考える必要があります.そこで,照明光の強度を変数 s(初期値 1)で記

図 5.8 球体と床の描画

Page 24: 「Javaによる簡単実習3次元CG入門」 サンプルページ · できるようにプログラム例も示しています.本書は,「c++による簡単実習3次元cg入門

1

2

3

4

5

6

7

8

付録

レイトレ|シングの基礎

575.2 鏡面反射の表現

述しており,交点 P の色 Csの計算に使用しています.一般的に物体が複数ある場合には,視線V Rと各物体との交点のうち視点 V 0にもっとも近いものを選び出して描画する必要があります.

リスト 5.5 Polygon.ox.java

1 // 多角形のクラス2  3 public class Polygon{

4  5 public Point[ ] p; // 頂点座標(Point)配列のポインタ6 public Vector nv; // 法線ベクトル

~~~~~~~~~~~~~~~~~~~~~11 public Polygon(Point a0, Point a1, Point a2, Point a3){12 p = new Point[5];13 p[0] = p[4] = a0; p[1] = a1;14 p[2] = a2; p[3] = a3;15 nv = ((p[0] - p[2]) % (p[1] - p[3])).unit();16 }17 public Polygon(Point a[ ]){18 int n = a.length;19 if(n>0) p = new Point[n+1];20 for(int i=0; i<n; i++)p[i] = a[i]; p[n] = a[0];21 nv = ((p[0] - p[2]) % (p[1] - p[3%n])).unit();22 }

~~~~~~~~~~~~~~~~~~~~~29 public static Polygon operator*(TMatrix m, Polygon b){30 Polygon c = new Polygon(b);31 for(int i=0; i<c.p.length; i++)c.p[i] = m * b.p[i];32 c.nv = ((c.p[0] - c.p[2]) % (c.p[1] - c.p[3%c.p.length])).unit();33 return c;34 }

~~~~~~~~~~~~~~~~~~~~~

43 public double hit(Ray VR, Point P, Vector N){ // 交点座標を返す44 double t = nv * (p[1] - VR.o) / (nv * VR.d); // 距離45 if(t<=0.) return Ray.INFINITY;

46 P.copy(VR.o+t*VR.d); // 交点,法線47 N.copy(nv);

48 for(int i=0; i<p.length-1; i++) // 交点座標Pがポリゴン内かの判定49 if(N*((p[i+1]-p[i])%(P-p[i]))<0)return Ray.INFINITY;50 return t;51 }

~~~~~~~~~~~~~~~~~~~~~65 }

5.2 鏡面反射の表現表面がつるつるしていて,周囲の物体が写り込むような物体の描画について考えてみます.図

5.9に示すように,視線の反射方向Rの物体が写り込んでいるものと考えることができます.したがって,視点を交点 P としてR方向(反射視線RR)を見る必要があります.交点 P の色 Cs

(球体の色)に,交点P から反射方向Rに見える色 Cr(床の色)を反射率 krを考慮して加える操作を行います.また,交点P から反射方向Rに見える物体にも写り込みがある場合には,その物体でも同様の操作をする必要があります.視線の反射方向Rは,次式で求めることができます.

Page 25: 「Javaによる簡単実習3次元CG入門」 サンプルページ · できるようにプログラム例も示しています.本書は,「c++による簡単実習3次元cg入門

1

2

3

4

5

6

7

8

付録

レイトレ|シングの基礎

58 第 5 章 レイトレーシングの基礎

図 5.9 球体と床の描画 図 5.10 反射する物体(ex5_3.ox.java)(口絵 14)

R = V − 2N · (V •N)

C = Cs + kr · Cr

(5.10)

球体と床を描画するプログラム例をリスト 5.6(ex5_3.ox.java)に,実行結果を図 5.10(口絵14)に示します.球体に床が写っているのがわかります.スクリーンを構成する画素の描画手順を,図 5.11のフローチャートに示します.視線が球体と交わる場合,反射視線のR方向の色を考慮しているところ(点線部分)が追加されています.

リスト 5.6 ex5_3.ox.java

1 // 床と映り込みのある球体を表示する2  3 public class ex5_3{ // 球体を表示する(レイトレーシング法)4 public static void main(String[ ] args){

5  6 Ball Ba = new Ball(new Point(0,0,70),70); // 球体の記述7 Polygon Wa = TMatrix.scale(200,200,-50) * Polygon.square_xy(); //床の記述8 Color Cb = Color.RED;9 Color Cw = 0.3 * Color.WHITE + 0.4 * Color.BLUE;10 double kr = 0.3; // 反射率11  12 Win3D W0 = new Win3D("反射の処理",400,350);13 Point V0 = new Point(500,500, 500); // 視点の記述14 Point L0 = new Point( 0,400,1000); // 照明光の記述15 W0.setView(V0);16  17 for(int x=0+0; x<W0.size_x+0; x++)18 for(int y=0; y<W0.size_y; y++){

19 Vector V = (W0.world(new Point(x,y,-W0.dv))-V0).unit(); // 視線ベクトルの計算20 Ray VR = new Ray(V0, V); // 視線の記述21 double s = 1; // 日向か日陰を表す22  23 Point Ps = new Point(),Pr = new Point(); // 交点座標24 Vector N = new Vector(),D = new Vector();25 Color Cs = 0.25 * Color.CYAN, Cr = Color.BLACK;26 if(Ba.hit(VR, Ps, N)<Ray.INFINITY){ // 視線が球体と交わるかの判定27 Vector L = (Ps - L0).unit(); // 照明光の方向ベクトル

Page 26: 「Javaによる簡単実習3次元CG入門」 サンプルページ · できるようにプログラム例も示しています.本書は,「c++による簡単実習3次元cg入門

1

2

3

4

5

6

7

8

付録

レイトレ|シングの基礎

595.2 鏡面反射の表現

28 Cs = VR.shading(L, N, Cb); // 球体の色29 Ray RR = new Ray(Ps, V-2*(V*N)*N); // 反射視線の記述30 if(Wa.hit(RR, Pr, N)<Ray.INFINITY){ // 視線が床と交わるかの判定31 Vector Lr = (Pr - L0).unit(); // 照明光の方向ベクトル32 Ray LR = new Ray(L0, Lr); // 照明光線の記述33 if(Ba.hit(LR, Pr, D)<Ray.INFINITY)s = 0; // 照明光が床に当たるかの判定34 Cr = RR.shading(Lr, N, Cw, s); // 球体に反射している色35 }36 }37 else if(Wa.hit(VR,Ps,N)<Ray.INFINITY){ // 視線が床と交わるかの判定38 Vector L = (Ps - L0).unit(); // 照明光の方向ベクトル39 Ray LR = new Ray(L0, L); // 照明光線の記述40 if(Ba.hit(LR, Ps, D)<Ray.INFINITY)s = 0; // 照明光が床に当たるかの判定41 Cs = VR.shading(L, N, Cw, s); // 床の色42 }43 W0.setColor(Cs+kr*Cr);44 W0.w.point(x, y); // screen座標で描画45 }46 }47 }

図 5.11 球と床の描画

Page 27: 「Javaによる簡単実習3次元CG入門」 サンプルページ · できるようにプログラム例も示しています.本書は,「c++による簡単実習3次元cg入門

1

2

3

4

5

6

7

8

付録

94

8 ボリュームレンダリング

 これまでは,表面の特徴に着目して描画する物体を扱ってきましたので,光をまったく通さない物体,あるいはガラスなどのように光を透過する物体の描画を行いました. 本章では,雲,煙,湯気などのような半透明な物体,あるいは内部構造を透かして見ているようなボリュームレンダリングという描画方法について考えていきます.

8.1 ボリュームデータ雲,煙,半透明な材質を含んでいる物体などを描画する場合,その物体の情報をどのように表現すればよいのでしょうか.雲は微小な水滴や氷からできており,雲の中心付近ではその密度が大きく,光を通しにくくなっています.また,雲の端の方では水滴や氷の密度が小さく,光を通しやすくなっています.すなわち,場所によって密度が異なっており,その密度を表現する必要があります.そのため,図 8.1に示すように,空間を格子状に分割して,物体を格子(voxel:ボクセル)ごとの密度で表現します.これを,ボリュームデータといいます.

図 8.1 ボリュームデータ

ボリュームレンダリングでは,物体の内部構造を透かして見ているように描画でき,ボリュームデータとして数値シミュレーションの結果や CT(Computed Tomography:断層撮影法),MRI

(Magnetic Resonance Imaging:磁気共鳴映像法)などの測定によって得られた結果を用いることができます.

8.2 ボリュームレンダリングボリュームデータを描画する方法として,ボリュームレンダリングを用います.その様子を,図 8.2に示します.基本的な考え方を以下に示します.

・視点に達する光の強さBは,視線に沿って各ボクセル(①の範囲)からの反射光および背景

Page 28: 「Javaによる簡単実習3次元CG入門」 サンプルページ · できるようにプログラム例も示しています.本書は,「c++による簡単実習3次元cg入門

1

2

3

4

5

6

7

8

付録

ボリュ|ムレンダリング

958.2 ボリュームレンダリング

図 8.2 ボリューム・レンダリング

からの光の累積となる.・各ボクセルからの反射光は,視点方向にあるボクセル(②の範囲)によって弱くなる(減衰する).

・各ボクセルからの反射光は,照明光の強さに比例する.照明光の強さは,照明方向のボクセル(③の範囲)によって弱くなる(減衰する).視点に達する光の強さB は次式で計算することができます.

B =

t1∑t=t0

{exp

[−γ

t∑s=t0

ρ(xs, ys, zs)

]· It(xt, yt, zt)ϕ(xt, yt, zt)ρ(xt, yt, zt)

}(8.1)

←−−−−−−−−②−−−−−−−−→←−−−−−−−−−−−−−−−−−−−−−−−−−①−−−−−−−−−−−−−−−−−−−−−−−−−→

γ : 密度を光減衰係数に変換する係数ρ : 密度It : 光源からの照度ϕ : シェーディング関数

光源(強さ I)からの視線上のボクセルへの到達光の照度 It は,次式で計算することができます.

It = I · exp[−γ

Lin∑r=Lt

ρ(xr, yr, zr)

](8.2)

←−−−−−−−−−−③−−−−−−−−−−→

ここで,視点から図 8.2に示したような直方体(バウンディングボリューム)との交点までの距離を t0, t1としています.また,照明から直方体との交点までの距離を Lin,着目している点までの距離を Lt としています.メタン分子(CH4)のボリュームデータを作成するプログラム例をリスト 8.1(ex8_1.ox.java)に示します.ballメソッド(リスト 8.1の 66行目参照)では各原子のデータとして,配列 vol(密

Page 29: 「Javaによる簡単実習3次元CG入門」 サンプルページ · できるようにプログラム例も示しています.本書は,「c++による簡単実習3次元cg入門

1

2

3

4

5

6

7

8

付録

ボリュ|ムレンダリング

96 第 8 章 ボリュームレンダリング

度)と配列 col(色)に球状に書き込みます. ボリュームデータを用いる場合,使用する配列は 3

次元となり,必要メモリ量は一辺ボクセル数の 3乗に比例します.そのため,一辺のボクセル数をあまり大きくできません.

リスト 8.1 ex8_1.ox.java

1 // ボリュームデータファイルの作成2 // メタン分子(CH4)3 import java.io.*;

4  5 public class ex8_1{

6  7 final static int BSIZE = 64; // 一辺のボクセル数8 final static int BSX = BSIZE*2; // 作業配列の大きさ9 final static int BSY = BSIZE*2; // 作業配列の大きさ10 final static int BSZ = BSIZE*2; // 作業配列の大きさ11  12 static double[ ][ ][ ] vol = new double[BSX][BSY][BSZ]; // 作業配列(密度)13 static Color [ ][ ][ ] col = new Color [BSX][BSY][BSZ]; // 作業配列(色)14  15 public static void main(String[ ] args){

16  17 System.out.printf("ボリュームデータを作成中です! Y=n");

18 for(int x=0, y, z; x<BSX; x++) // ボリュームデータの作成19 for(y=0; y<BSY; y++)20 for(z=0; z<BSZ; z++){21 vol[x][y][z] = 0;22 col[x][y][z] = Color.BLACK;23 }24  25 // 炭素原子26 Point C = new Point(0,0,-0.3);27 ball(C, 0.60, Color.WHITE, .10);28 ball(C, 0.20, Color.YELLOW, 1.0);29  30 // 水素原子31 Point[] H = new Point[]{Point.unit(0,0), Point.unit(120,0),32 Point.unit(240,0), Point.unit(0,90)*Math.sqrt(2.)};33 Point H0 = (H[0] + H[1] + H[2] + H[3]) / 4;34 for(int i=0; i<4; i++){35 H[i] = 0.55 * (H[i] - H0) + C;36 ball(H[i], .45, Color.WHITE, .10);37 ball(H[i], .15, Color.RED, 1.0);38 }39  40 // ボリュームデータの書き込み41 try {42 String fn = "data/metan.vol";43 FileWriter writer = new FileWriter(fn, false);44 writer.write(String.format("%d %d %dY=n", BSIZE, BSIZE, BSIZE));

45  46 for(int x=0; x<BSX; x+=2)47 for(int y=0; y<BSY; y+=2)

48 for(int z=0; z<BSZ; z+=2){ // 平均を書き込む49 double vo = 0;50 Color at = Color.BLACK;51 for(int i=0; i<8; i++){52 vo = vo + vol[x+i%2][y+(i/2)%2][z+i/4];53 at = at + col[x+i%2][y+(i/2)%2][z+i/4]*vol[x+i%2][y+(i/2)%2][z+i/4];54 }55 if(vo>0)at = at / vo;

Page 30: 「Javaによる簡単実習3次元CG入門」 サンプルページ · できるようにプログラム例も示しています.本書は,「c++による簡単実習3次元cg入門

1

2

3

4

5

6

7

8

付録

ボリュ|ムレンダリング

978.2 ボリュームレンダリング

56 writer.write(String.format("%5.3f %5.3f %5.3f %5.3fY=n",vo/8, at.r, at.g,at.b));

57 }58 writer.close();59 }60 catch(IOException e){61 System.out.println(e);62 }63 }64  65 // 球体のボリュームデータを作業配列に書き込み66 static void ball(Point p, double r, Color c, double v){67 for(int x=0; x<BSX; x++)68 for(int y=0; y<BSY; y++)69 for(int z=0; z<BSZ; z++){70 Vector pp = (new Point(x,y,z) + new Point(1,1,1) * (.5-BSX/2.)) * 2 / BSX-p;71 if(pp.len()>r || v<vol[x][y][z])continue;

72 col[x][y][z]=c; // 色73 vol[x][y][z]=v; // 密度74 }75 }76 }

また,ボリュームレンダリングのプログラム例をリスト 8.2(ex8_2.ox.java)に,実行結果の画像を図 8.3に示します.プログラム例では,ボリュームデータを 2倍(変数 scaで設定)に拡大して表示しています.最初に,データ(V o)を読み込んだ後にシェーディング関数(VR.shading)で使用する法線ベクトル(nor)を計算します.つぎに,ボリュームデータが存在する範囲のバウンディングボリューム(ボックス,ポリゴン配列 bb)を設定して,計算範囲を明確にしています(hitメソッドを使用).その後,式 (8.1),(8.2)の計算を行い描画しています.式 (8.1), (8.2)

には∑(繰り返し)が多く,描画には時間を要してしまいます.

リスト 8.2 ex8_2.ox.java

1 // ボリュームデータファイルの表示2 // メタン分子(CH4)3  4 public class ex8_2{

5  6 static int bsx, bsy, bsz; // 一辺のボクセル数7  8 public static void main(String[ ] args){

9  10 double GAMM = 0.5; // 密度を光減衰量に変換する係数11 double BETA = 1.2; // 反射量の係数12 double sca = 2; // 1ボクセルの大きさ13

14 Volume Vo = new Volume("data/metan.vol", 0); // データ読み込み15 double[ ][ ][ ] vol = Vo.val; // 密度16 Color [ ][ ][ ] col = Vo.vat; // 色17 bsx = Vo.vsx; bsy = Vo.vsy; bsz = Vo.vsz; // 配列のサイズ18  19 Vector[ ][ ][ ] nor = new Vector[bsx][bsy][bsz]; // 法線ベクトル20 double dx, dy, dz;21 for(int x=0; x<bsx; x++)22 for(int y=0; y<bsy; y++)23 for(int z=0; z<bsz; z++){24 if(x==0) dx=(vol[x+1][y][z]- 0 )/2;

Page 31: 「Javaによる簡単実習3次元CG入門」 サンプルページ · できるようにプログラム例も示しています.本書は,「c++による簡単実習3次元cg入門

1

2

3

4

5

6

7

8

付録

ボリュ|ムレンダリング

98 第 8 章 ボリュームレンダリング

25 else if(x==bsx-1) dx=(0 -vol[x-1][y][z])/2;26 else dx=(vol[x+1][y][z]-vol[x-1][y][z])/2;27 if(y==0) dy=(vol[x][y+1][z]- 0 )/2;28 else if(y==bsy-1) dy=(0 -vol[x][y-1][z])/2;29 else dy=(vol[x][y+1][z]-vol[x][y-1][z])/2;30 if(z==0) dz=(vol[x][y][z+1]- 0 )/2;31 else if(z==bsz-1) dz=(0 -vol[x][y][z-1])/2;32 else dz=(vol[x][y][z+1]-vol[x][y][z-1])/2;33 nor[x][y][z]=-(new Vector(dx,dy,dz)).unit();34 }35  36 // バウンディングボックス37 Point bl = new Point(bsx-1, bsy-1, bsz-1) * (sca/2);38 Polygon[ ] bb = TMatrix.scale(bl) * Polygon.box();

39 Point pos = -bl; // 配置位置40 double ll = (bl).len() * 2;

41  42 Color BC = 0.25 * Color.CYAN; // 背景色43 int wsiz = (int)ll / 2 * 2;

44 Win3DE W1 = new Win3DE("ボリュームレンダリング", wsiz, wsiz, BC);45 Point L0 = 1000 * Point.unit(-10,60); // 照明光の記述46 Point V0 = 1000 * Point.unit( 20,30); // 視点の記述47 W1.setView(V0);48  49 for(int xx=0; xx<W1.size_x; xx++)50 for(int yy=0; yy<W1.size_y; yy++){

51 Vector V = (W1.world(new Point(xx,yy,-W1.dv))-V0).unit(); // 視線ベクトル52 Ray VR = new Ray(V0, V); // 視線の記述53  54 Point Pos = new Point();55 double t, va = 0;56 Color B = Color.BLACK;57 if(hit(Pos, VR, bb)<Ray.INFINITY)58 for(t=1; t<ll; t++){59 Point S = Pos + t * V;60 Point SX = new Point();

61  62 if(index(SX, S, pos, sca)==0)break;63 Optics At = new Optics(col[(int)SX.x][(int)SX.y][(int)SX.z],0.7,0.3,0.3);

64 Vector N = nor[(int)SX.x][(int)SX.y][(int)SX.z]; // 法線ベクトル計算65 double a = vol[(int)SX.x][(int)SX.y][(int)SX.z]/sca; // ボクセル値の計算66 if(a==0) continue;67  68 // 照明光の強度Iの計算69 Vector L = (S - L0).unit(); // 照明光の方向ベクトル70 double la = 0; // 照明光に沿ったボクセル値の和71 for(double r=1; r<ll; r++) {72 Point LP = S - r * L;73 Point LX = new Point();74 if(index(LX, LP, pos, sca)==0)break;

75 double a0 = vol[(int)LX.x][(int)LX.y][(int)LX.z]/sca; // ボクセル値76 la = la + a0;77 }78 double I = Math.exp(-GAMM*la); // 照明光の強度③79 B = Math.exp(-GAMM*va) * I * VR.shading(L, N, At, 1) * a * BETA+B;

     // 視線に沿った反射光の和①80 va = va + a; // 視線に沿ったボクセル値の和②81 }82 double tr = Math.exp(-GAMM*va); // 透明度83 W1.setColor(B+tr*BC);84 W1.point(xx, yy);85 }86 }

Page 32: 「Javaによる簡単実習3次元CG入門」 サンプルページ · できるようにプログラム例も示しています.本書は,「c++による簡単実習3次元cg入門

1

2

3

4

5

6

7

8

付録

ボリュ|ムレンダリング

998.3 3 次元テクスチャ

87  88 // 座標をボクセル位置(配列の添え字)に変換する89 static int index(Point ix, Point P, Point pos, double sca){90 ix.copy((P-pos)/sca);91 if(ix.x<0 || bsx-1<=ix.x)return 0;92 if(ix.y<0 || bsy-1<=ix.y)return 0;93 if(ix.z<0 || bsz-1<=ix.z)return 0;94 return 1;95 }96  97 // バウンディングボリュームの交差判定98 public static double hit(Point Pos, Ray V, Polygon[] bb){99 double d = Ray.INFINITY;

100 Point P = new Point(), N = new Point();101 for(int i=0; i<6; i++) d = Math.min(d, bb[i].hit(V, P, N));102 if(d >= Ray.INFINITY) return Ray.INFINITY;103 Pos.copy(V.o+d*V.d);104 return d;105 }106 }

図 8.3 メタン分子(CH4)(ex8_2.ox.java)

8.3 3次元テクスチャ森林などを描画する場合,樹木などの緻密な形状を小さく描画することになってしまいます.すると,エイリアシングが発生しやすくなり,高品質な画像を生成することは難しくなってきます.エイリアシングの発生を抑える方法として,遠方の樹木などをボリュームデータで表現する方法を 3次元テクスチャといいます.葉や枝の茂りぐあいをボリュームデータの密度とします.3次元テクスチャを用いたプログラム例をリスト 8.3(ex8_3.ox.java),実行結果の画像を図 8.4

(口絵 31)に示します.6章では,2次曲面やメタボール,B-スプライン曲面を Sceneクラスに登録(addメソッド)して描画(renderメソッド)していました.ボリュームデータも同様の手順で,登録(11行目)して描画(18行目)することができます.ボリュームデータは 64× 64× 64

ボクセルで,横と奥行き方向を 2倍,縦方向を 3倍に拡大して描画しています.また,コケとして 3次元テクスチャを用いた例を図 8.5(口絵 43),および遠方の樹木に 3次元テクスチャを用いた例を図 8.6(口絵 44)に示します.

Page 33: 「Javaによる簡単実習3次元CG入門」 サンプルページ · できるようにプログラム例も示しています.本書は,「c++による簡単実習3次元cg入門

1

2

3

4

5

6

7

8

付録

ボリュ|ムレンダリング

100 第 8 章 ボリュームレンダリング

リスト 8.3 ex8_3.ox.java

1 // 3次元テクスチャ(ボリュームファイル)を読み込み,描画を行う2  3 public class ex8_3{4 public static void main(String[ ] args){

5  6 Color Cb = 0.1 * Color.WHITE + 0.1 * Color.CYAN; // 背景色7 Scene Sc = new Scene(Cb);8  9 Volume Vt = new Volume("data/tree.vol", 0);10 Optics At = new Optics(Color.WHITE, .7, .1, .2, 0.8, 0.4,0);11 Sc.add(TMatrix.move(0,0,-100)*TMatrix.scale(2,2,3)*Vt,At);12  13 Win3DE W1 = new Win3DE("3次元テクスチャ",300,300, Cb);14 Point L0 = 1000 * Vector.unit(60,60); // 照明光の記述15 Point V0 = 1000 * Vector.unit(30,10); // 視点の記述16 W1.setView(V0);17 Sc.setLight(L0);18 W1.render(Sc, Win3DE.RAY,10);19 }20 }

図 8.4 3次元テクスチャ(ex8_3.ox.java)(口絵 31)

図 8.5 灯籠とコケ(ap5_1.ox.java)(口絵 43) 図 8.6 灯籠と樹木(ap5_2.ox.java)(口絵 44)

Page 34: 「Javaによる簡単実習3次元CG入門」 サンプルページ · できるようにプログラム例も示しています.本書は,「c++による簡単実習3次元cg入門
Page 35: 「Javaによる簡単実習3次元CG入門」 サンプルページ · できるようにプログラム例も示しています.本書は,「c++による簡単実習3次元cg入門

144

索 引

■英数字

2次曲面 65

2次元標準座標 6

3次元テクスチャ 99

3次元標準座標 6

Ballクラス 51

B-スプライン曲面 74

Colorクラス 4

Csgmクラス 67

CSGモデル 71

Lineクラス 7

Metaクラス 67

Opticsクラス 62

Pointクラス 7

Polygonクラス 27

Quadクラス 66

Rayクラス 51

Scene2クラス 77

Sceneクラス 63

Surfaceクラス 27

TMatrixクラス 12

Vectorクラス 24

Win2Dクラス 6

Win3DEクラス 6

Win3Dクラス 6

Z ソート法 32

Z バッファ法 32

■あ 行

アンチエイリアシング 78

アンビエント 38

一葉双曲面 65

隠面処理 30

演算子 5

■か 行

回転移動 16

拡散反射光 37

拡大縮小 19

陰付け 47

画素 4

環境光 38

環境マッピング 88

間接光 80

鏡面反射光 38

曲面パッチ 74

空間分割法 77

屈折率 60

グーローシェーディング 41

クーンズ曲面 74

光学的モデル 37

交差判定 54

格子 94

光線追跡法 50

コンスタントシェーディング39

■さ 行

座標変換 11

サーフェイスモデル 26, 27

三原色 4

シェーディング 39

シャドウボリューム法 48

シャドウマップ法 47

スクリーン 4

スクリーン座標 6

スケーリング 19

スネルの法則 60

スーパーサンプリング 78

スペキュラー 38

スムーズシェーディング 41

双曲柱面 65

双曲放物面 65

相互反射 80

ソリッドテクスチャリング 84

ソリッドモデル 26

■た 行

楕円錐面 65

楕円柱面 65

楕円放物面 65

楕円面 65

ディフューズ 37

テクスチャマッピング 82

透過率 60

トーラス 28

■な 行

二葉双曲面 65

■は 行

ハイライト効果 38

バウンディングボリューム法77

パラメトリック曲面 74

反射率 60

バンプマッピング 86

ピクセル 4

フォンシェーディング 44

フォンモデル 39

フラットシェーディング 39

ブレゼンハム 8

フレーネルの法則 60

分散レイトレーシング 79

平行移動 14

ベジェ曲面 74

変換行列 12

法線ベクトル法 29

放物柱面 65

ボクセル 94

ポリゴン 26

ポリゴンモデル 26, 27

ボリュームデータ 94

ボリュームレンダリング 94

■ま 行

マッピング 82

メタボール 68

モデリング 26

■ら 行

ラジオシティ法 80

ラスタスキャン 4

ランバーモデル 39

立体の演算 71

リフラクションマッピング 91

リフレクションマッピング 89

レイトレーシング 26, 50

レンダリング 26

■わ 行

ワイヤーフレーム 26

ワイヤーフレームモデル 26

Page 36: 「Javaによる簡単実習3次元CG入門」 サンプルページ · できるようにプログラム例も示しています.本書は,「c++による簡単実習3次元cg入門

Java による簡単実習 3次元 CG入門     © 小笠原祐治 2009

2009 年 10 月 20 日 第 1 版第 1 刷発行 【本書の無断転載を禁ず】

著  者 小笠原祐治発 行 者 森北博巳発 行 所 森北出版株式会社

東京都千代田区富士見 1-4-11(〒 102-0071)電話 03-3265-8341 / FAX 03-3264-8709http://www.morikita.co. jp/日本書籍出版協会・自然科学書協会・工学書協会 会員

<(社)出版者著作権管理機構 委託出版物>

落丁・乱丁本はお取替えいたします 印刷/シナノ・製本/ブックアート

Printed in Japan/ISBN978-4-627-84861-0

   著 者 略 歴小笠原 祐治(おがさわら・ゆうじ)1981 年 岩手大学工学部情報工学科卒業1983 年 岩手大学大学院情報工学専攻修了1983 年 富士通株式会社入社1991 年 岩手大学工学部情報工学科助手1997 年 岩手産業技術短期大学校講師2001 年 岩手県立高度技術専門学院2005 年 岩手産業技術短期大学校講師 現在に至る 博士(工学)