まず,アニメーション GIF はパラパラマンガなので,コマ数だけ静止画ファ イルを用意する必要がある.前回のマンデルブロー集合を求めるプログラムの パラメータを変更してはコンパイル,実行を繰り返しても良いが,繰り返し部 分は人間がやるよりもコンピュータにやらせた方が良い.
この講義の範囲を超えるが,Java言語からファイルに出力するプログラムを 紹介する.ファイルに出力するには,まず,
java.io.PrintWriter ps=new java.io.PrintWriter(new java.io.FileWriter("mandel"+k+".ppm"));
のようにして,ファイル名("mandel"+k+".ppm")からFileWriterを作り,それ
から PrintWriterを作る.| 「作る」というのは「オブジェクトの生成」にあたり,演算子 new で実現さ れるが,この講義の範囲を超えるので詳しい説明は省略する.興味のある人は, あたりを参照すること. |
これまで,
System.out.println( 出力したい文字列 );としてきたのを,
ps.println( 出力したい文字列 );とするとファイルへの出力に変る.
| これは,変数 ps に入っているjava.io.PrintWriter型のオブジェクトのメソッド println を呼び出しているという動作だが,ここでは詳しい説明は省略する. |
出力を終えたら,ファイルを閉じる必要がある.
ps.close();を実行する.
class Mandel{
// 実数を引数(パラメタ)に持つメソッド mandel の定義
static int mandel(double x, double y){
double zr=0, zi=0,new_zr;
int i;
for(i=0;i<50;i=i+1){
if(zr*zr+zi*zi>4.0) { // |Zi|>2.0 の時は発散と判断
return i;
}
// 複素数の計算
// (zr+zi*i)*(zr+zi*i)+x+y*i=(zr*zr-zi*zi+x)+(2*zr*zi+y)*i
new_zr=zr*zr-zi*zi+x;
zi=2*zr*zi+y;
zr=new_zr;
}
return 50;
}
// 入出力関係でエラーが発生する可能性があるので,
// throws java.io.IOException をつける.
public static void main(String[] args) throws java.io.IOException{
int width=256, height=256;
int k;
for(k=0;k<10;k++){
// ファイルに出力する時は,ファイル名("mandel"+k+".ppm")から
// FileWriterを作り,それから PrintWriterを作る
java.io.PrintWriter ps=new java.io.PrintWriter(new java.io.FileWriter("mandel"+k+".ppm"));
// マンデルブロー集合を計算する範囲のスケール
// 3e-0.8k なので,kが0の時は3
double scale=3.0*Math.exp(-(double)k*0.8);
// ファイルへの出力
ps.println("P3 "+width+" "+height+" 255");
int i,j,r;
double x, y;
for(i=0;i<height;i=i+1){
for(j=0;j<width;j=j+1){
// -1.7 <= x < 0.7, -1.2 <= y < 1.2の範囲で計算
// 実数(double)から整数(int)への変換は「(double)整数式」
// のようにおこなう.
x= -1.2525 -scale*0.5+scale*((double)j/(double)width);
y= -0.3425 -scale*0.5+scale*((double)i/(double)height);
// 0 <= r <= 50の値が返る
r=mandel(x,y);
// r=50の時に青(0,0,255), r=0の時にほぼ白(250,250,250)とする.
// ファイルへの出力
ps.println((250-r*5)+" "+(250-r*5)+" 250");
}
}
// ファイルストリームを閉じる
ps.close();
}
}
}
これを実行すると,mandel0.ppm, mandel1.ppm, ..., mandel9.ppmというファ
イルができる.
convert -delay 40 mandel[0-9].ppm mandel-animation.gifのように実行すると,0.4秒間隔(-delay のパラメータは 1/100秒単位で指定)で, mandel[0-9].gif を次々に表示していく.以下のような画像が現れる.
レイトレーシング法(ray tracing algorithm)は,計算機内部の3次元世界か ら2次元画像を生成する方法(レンダリング法)の一つで,視線方向に光線を追 跡していく方法( 慶應SFC千代倉研究室によるCG教科書などを参照)である.
レイトレーシング法は本来,鏡面反射や屈折などを含む画像の生成に役に立ち, また複数の物体の描画で隠面消去が自然に定義できるという特徴があるが,こ こでは,その特徴は生かさずに,
class RayTrace{
public static void main(String[] args) throws java.io.IOException{
// 背景色
double backr=0.2, backg=0.2, backb=0.6;
// b=(10,10,100)に大きさ30のボールがある.
// ボールの色(黄色)
double ballred=1.0, ballgreen=1.0, ballblue=0.0;
// ボールの座標(10,10,100)と半径(30)
double bx=10.0, by=10.0, bz=100.0, br=30.0;
// 入力光の方向は(1,1,1)の方向
// 平方根を取る操作は Math.sqrt
// 単位ベクトル(長さ1)に正規化する
double lx=Math.sqrt(1.0/3.0), ly=Math.sqrt(1.0/3.0), lz=Math.sqrt(1.0/3.0);
// 画面の幅, 高さ
int width=128, height=128;
System.out.println("P3 "+width+" "+height+" 255");
int i,j,r;
double vx, vy, vz, vlen;
for(i=0;i<height;i=i+1){
for(j=0;j<width;j=j+1){
// 視線ベクトル.1.0離れた幅1.0, 高さ 1.0のスクリーンに投影
vz=1.0;
vx=(double)(i-height/2)/(double)height;
vy=(double)(j-width/2)/(double)width;
// (vx, vy, vz) を長さ1のベクトルにする.
vlen=Math.sqrt(vx*vx+vy*vy+vz*vz);
vx=vx/vlen; vz=vz/vlen; vz=vz/vlen;
// b-v t と v が直交する点を求める.
// (bx-t*vx)^2+(by-t*vy)^2+(bz-t*vz)^2+(bz-t*vz)^2=br^2
// はtの2次方程式になる.
// (vx^2+vy^2+vz^2)*t^2+(-2*bx*vx-2*by*vy-2*bz*vz)*t+
// bx^2+by^2+bz^2-br^2=0
double a=vx*vx+vy*vy+vz*vz, b=-2*vx*bx-2*vy*by-2*vz*bz;
double c=bx*bx+by*by+bz*bz-br*br;
// 解の公式より t=(-b±√(b*b-4*a*c))/(2*a)
double d=b*b-4*a*c;
if(d<0.0){ // 衝突しない時は背景色を描く.
System.out.println((int)(backr*255.0)+" "+(int)(backg*255.0)+" "+(int)(backb*255.0));
}
else{ // 始点(0,0,0)がボール内にないという前提で小さい方のtに決める
double t= (-b-Math.sqrt(d))/(2*a);
// ボールの法線ベクトル(Normal Vector)を求める
double nx= vx*t-bx, ny= vy*t-by, nz= vz*t-bz;
// 法線ベクトルを単位ベクトルにする.
double nlen=Math.sqrt(nx*nx+ny*ny+nz*nz);
nx=nx/nlen; ny=ny/nlen; nz=nz/nlen;
// 法線ベクトルと視線ベクトルの costhetaを求める
// 単位ベクトル同士なのでベクトルの内積で良い.
double costheta1= -vx*nx-vy*ny-vz*nz;
// 法線ベクトルと入力光の costheta を求める
double costheta2= -lx*nx-ly*ny-lz*nz;
// 拡散反射成分だけを求める
// 2つの数の最小値を取るMath.minの呼び出し
double red=Math.min(1.0,ballred*costheta1*costheta2);
double green=Math.min(1.0,ballgreen*costheta1*costheta2);
double blue=Math.min(1.0,ballblue*costheta1*costheta2);
System.out.println((int)(red*255.0)+" "+(int)(green*255.0)+" "+(int)(blue*255.0));
}
}
}
}
}
/home/ktanaka/bin/report712 1を実行しなさい.~/jousho00/report712.htmlは,以下のものを含んでいて, 他人から読める必要がある.
締切は,8/4(金)の21:00.8月中は採点のため他人から見えるようにしておくこと.