実習タイトル:CGプログラミング(モデリング)









1.実習概要
1-1:実習の目的
 コンピュータグラフィックス(以下,CGと書く)の技術は,デザイン分野,ビジュアリ
ゼーション(可視化)分野,そして,エンタテイメント分野など幅広い分野に応用されて
いる.CG技術を利用するには,それを実現するソフトウェアが必要であるが,CG関連のア
プリケーションソフトウェアは,2次元・3次元を問わず様々なものが作成されている.
 CGに関する技術には大別して,「コンピュータ内に形状を表現する技術:モデリング(
Modeling)」,および「画像として形状を表現する技術:レンダリング(Rendereing)」
の2つがある.一般のCGソフトには各々特徴とするところはあるが,ほとんどの場合,こ
れらの2つの機能は実装されている.
 本実習は,まず,モデリングをつかさどる部分(これを,モデラ(Modeler)と言う)
の作成を通してその構造を理解する.すなわち,いくつかの形状を作成し,形状をどのよ
うにコンピュータの内部で表現するか理解する.次いで,表面の反射モデルによる質感表
示を稼働化し3Dオブジェクトのレンダリング表示のメカニズムを理解する.
 なお,本実習は2年次配当科目「CGプログラミング(平面図形)」が履修済みであるこ
とが必須である.


1-2:実習日および実習場所
実習日:9月30日,10月7日,10月14日,10月21日

実習場所:御茶ノ水アネックス3F演習室2A

2-1:予備検討:モデルデータの構造について
 モデルとして,以下の構造体を考える.構造体の「構造」は,
・「形状=モデル」(Model)とは,「面」の集まりである.
・「面」(Surface)とは,「頂点」の集まり(並び)である.
・「頂点」(Vertex)とは,3次元空間の座標(x,y,z値)である.
と考える.括弧内は構造体名である.これを,C言語の構造体 -structure- で表現すると,
以下のようになる.

  ・モデルの型宣言
   typedef struct	//<−モデルの構造(面の集まり)
    { int num_s;		//立体を構成する面の数
     Surface *surf;	//面の配列
    }Model;

  ・面の型宣言
    typedef struct	//<−面の定義(点の並び)
    { int num_v;		//面を構成する頂点の数
    Vertex *vertex;	//頂点の配列
    }Surface;

  ・頂点の型宣言
    typedef struct	//頂点の定義
    { double x;		//座標値
      double y;
      double z;
    }Vertex;

このような構造体を用いると,Model型変数mdl内で
・モデルに含まれる面の数: mdl.num_s
・2番目の面の頂点数: mdl.surf[1].num_v
・3番目の面の1番目の頂点のX座標: mdl.surf[2].vertex[0].x
・3番目の面の1番目の頂点のY座標: mdl.surf[2].vertex[0].y
とCプログラムで記述できる(注:C言語では,配列要素は[0]からある).面の数と頂点の数は,
int型であり,座標値は,double型である.


2-2:実習1の実習概要と結果
(実習概要)
以後の実習に必要な実習環境を設定する.まず,基本部分を配布するので,各自のパソコンに
インストールする.配布したサンプルプログラムが正常に動作することを確認後,配布プログラム
を参考に新たな形状生成を行う.

(実習1-1)基本環境の構築:
(a)環境のダウンロード:実習HPから,使用するプログラム等をダウンロードする.
ファイル名はcgp_gm.tarである.
(b)解凍:拡張子から分かるように,unixのtar形式で圧縮されている.
 #tar xvf gm1.tar
と入力する.これにより,まず新しい「CGPGM」なるディレクトリが作成される.
ディレクトリ内に,「sample.c,incgm.h,libgm.h」なる3つのC言語プログラムが解凍される.
(c)ウインドウ名変更:
プログラム sample.cの終わりの部分にある関数main()内に
glutCreateWindow("00ki000 Tsuyoshi SAITOH");
なる部分があるが,括弧内の文字列を各自の名前等に変更する(英数字のみ).
(d)コンパイルと実行:gccによりコンパイルする.このためのコマンドは,以下の通りである.
ここで,「l」は,Lの小文字である.空白の位置に注意すること.
 #gcc sample.c -o sample -IGL -lglut
エラーがなければ
 #./sample
と入力する.サンプルプログラムが実行され,ウィンドウが開かれ,6角錐が現れる.
画面内の適当な点を左クリックで視点を変更できる.また,ウィンドウ上半分を右クリックすると拡大,
下半分をクリックすると縮小される.
(e)終了:ウィンドウ内で,中クリックする.「EXIT」と「SAVE」のメニュが現われる.
EXITはプログラムの停止である.SAVEは,そのウィンドウを,screne.pngなる画像ファイルとして格納する.
(常にファイル名が同じであることに注意すること)

結果



考察
プログラムが正常に機能したことを確認できた.

(実習1-2)プログラムの理解
本実習では,配布したsample.cプログラムを改良し,モデラ(表示を含む)を作成する.
したがって,このプログラム(および,glutパッケージ)を理解することから始めなければならない.

考察
 glutを用いて表示するため,面を単位として全体を描いているプログラムである.

(実習1-3)プログラムの複製
以下のファイルコピーコマンドを入力して,sample.cのコピー(week1.c)を作成する.
 #cp sample.c week1.c

考察
sample.cと同じ内容であるweek1.cが作成できた.

(実習1-4)新たな形状の生成
プログラムweek1.cの「図形を描く」部分を,高さ1の30角柱を生成するように作り直す.
これは,円柱の近似表現ともなる.

プログラム

結果



考察
 変数kakuを30にすることで底辺が30角の図形ができた.また,
6角錐を描く方法を参考にし,高さ1のところに,底辺の各角に対応する30角形を作るようにした.
結果,それぞれの点が対応し,30角柱を作ることができた.

2-3:実習2の実習概要と結果
(実習概要)
 実習1での描画は,立体の頂点座標を計算しながら表示した.これでは,例えば,始点変更などで,
再描画のたびに頂点の全てを計算し直す必要がある.簡単な図形ならまだしも,複雑な図形の場合,
計算のために多くの時間を必要とし描画速度も落ちる.そこで,「頂点計算のプロセス」と「描画のプロセス」
を分離することを考える.すなわち,
(1)様々な立体が表現できる共通の「データ構造」を設計する(これをモデルという).
(2)このデータ構造体に,頂点データ等を計算し必要な値を格納する.
(3)モデルに蓄えられた頂点等を参照しそれを描く.
の3つ分ける.
 この(1),(3)は,一つ作成すればよく,表現する立体毎に(2)を作成すればよいことになる.

(実習2-1)モデルの理解
配布資料の説明を理解し,配布した関数と対応させる.

考察
4.2節(配布資料内)の説明を理解し,関数と対応させることで,モデルの構造を理解することができた.

(実習2-2)プログラムの複製
実習2では,実習1で作成したプログラムを作り変える.そのために,
 #cp week1.c week2.c
とし複製を作る.

考察
week1.cと同じ内容であるweek2.cが作成できた.

(実習2-3)モデル描画プログラムの作成
第4.3節(配布資料内)で示した部分(関数mdrawの未完成版,week2.cの始めの部分にコメントとして
記述されている)を完成させる.4.3節で示したように,この関数は,2番目の面のみを表示するように
作られているので,「すべての面」を表示するように改良する.このプログラムのテストには,
データを格納する必要がある.libgm.hにある関数SMdlPyaramid()は,N角錐のモデルデータを格納する
関数である.この関数の利用方法は,4.5節で説明した.これらを用いて30角錐を表示しなさい.

プログラム

結果



考察
mdraw内のn=1をn=mdl.num_sにすることによって,main()内で指定した面の数を反映できた.
libgm.hのvoid SMdlPyramidを関数mdarwから呼び出し,30角錐を表示することができた.

(実習2-4)モデルデータ生成プログラムの作成
 配布したvoid SMdlPyramid()および(1-4)を参考に,n角柱のモデルデータを生成する関数
void SMdlCylinder(&mdl,num,h,r)を作成する(ファイルlibgm.h内の関数SMdlPyramidの後に
新たに作り,関数SMdlPyramidそのものは残しておく).それらを用いて30角柱を表示しなさい.

プログラム

結果



考察
 側面は一つの面を円状に一周分表示させて作っていることがわかる.
libgm.hに表示したい図形の関数を定義することで,メインプログラムに図形描画を記述することなく表示できた.


2-4:実習3の実習概要と結果
(実習概要)
 実習4で作成したサーフェスモデルを表示する関数mdraw(Model mdl)を改良し,反射モデルに応じた
フラット・シェーディングを行う関数pdraw(Model mdl)を作成する.これにより,リアルな3次元形状の
表示が可能となる.
 先に作成した関数mdrawの改良であるが,mdrawも残しておくために別の名前で新たな関数として作成する.
 シェーディング表示を行うためには,以下の設定を行う必要がある.
・光源の設定
 display()関数のなかで,ライティングを可能とし,光源の属性を設定する.
・対象物体の表面特性の設定
 反射係数などを設定する.
・画の描画
 Model型構造体内の面頂点の座標値を利用して,各面の面法線ベクトルを算出する.
 各面ごとにglNormal3d(nx,ny,nz)を呼び出して,面の法線ベクトルを設定する.
 glPolygonModelの引数をGL_FILLにして,平面を塗りつぶし描画する.

(実習3-1)プログラムの複製
これまでと同様に,
 #cp week2.c week3.c
として,実習3で使用するプログラムを複製する.

考察
week2.cと同じ内容であるweek3.cが作成できた.

(実習3-2)関数pdrawの作成
 レンダリング表示を可能とするプログラムに改良する.本来なら,5.1節(配布資料内)で
示したような順番で改修すべきであるが,ここでは,量的な観点から,5.2節,5.3節,5.4節の順に行う.
5.2節に示した改修では,先ず関数mdrawの部分をコピーし,それを関数pdrawとした上で改修する.
先週作成した円柱をレンダリング表示してみる.

プログラム

結果



考察
 法線ベクトルを求めるために,最初の3点の差ベクトルを求め,そこから外積を求め,それを正規化することによって実現していることが分かる.

(実習3-3)光源や反射特性の効果と画像の検討
 5.4節および5.5節を参考に反射特性や光源属性と変え,その効果を試す.
・反射属性を変え,「青っぽい」色と「赤っぽい」色の立体画像を生成.
・拡散反射と鏡面反射を変化させ,「金属的な立体」と「素焼き的立体」画像の生成.

結果

青っぽい色
float mdl_color[4]={0.1,0.1,1.0,1.0};
float mdl_diffuse[4]={0.5,0.5,0.5,1.0};
float mdl_specular[4]={0.3,0.3,0.3,1.0};
float mdl_ambient[4]={0.1,0.1,0.1,1.0};


赤っぽい色
float mdl_color[4]={1.0,0.1,0.1,1.0};
float mdl_diffuse[4]={0.5,0.5,0.5,1.0};
float mdl_specular[4]={0.3,0.3,0.3,1.0};
float mdl_ambient[4]={0.1,0.1,0.1,1.0};


金属的な立体
float mdl_color[4]={0.8,0.8,0.8,1.0};
float mdl_diffuse[4]={0.2,0.2,0.2,1.0};
float mdl_specular[4]={1.0,1.0,1.0,1.0};
float mdl_ambient[4]={1.0,1.0,1.0,1.0};


素焼き的立体
float mdl_color[4]={0.8,0.5,0.4,1.0};
float mdl_diffuse[4]={1.0,1.0,1.0,1.0};
float mdl_specular[4]={0.3,0.3,0.3,1.0};
float mdl_ambient[4]={0.1,0.1,0.1,1.0};


考察
 物体の表面の反射や屈折の数値を変更することで,様々な質感の物体が表現できた.
また,視点を変えることにより,表示された物体の光が当たらないところには影がつくことがわかる.
mdl_specularとmdl_specularの各値を高く設定することによって,材質に光沢が出て,金属的な立体と
非金属的な立体の差を表現できることがわかる.

(実習3-4)モデルデータ生成関数の作成
 5.6節で示したモデルデータの作成関数を作成する.

プログラム

(円錐台のモデルデータを生成する関数)

(球のモデルデータを生成する関数)

(トーラスのモデルデータを生成する関数)

考察
3つのモデルデータの作成関数ができたことにより,様々な複合図形を生成することが可能になった.

2-5:実習4の実習概要と結果
(実習概要)
 実習3までで,3次元形状をサーフェスモデルとして計算機内に表現し,それをフラットシェーディング
により表示することができた.また,各種のモデルデータを生成する関数を作成した.
 実習4では,様々な形状をアフィン変換を施して表示する関数modeler()を作成する.これにより,
様々な形を組み合わせた形状が表現可能となる.最後に,各自好みの形のCG画像を作成する.

(実習4-1)プログラムの複製
これまでと同様に,
 #cp week3.c week4.c
により,プログラムを複製する.

考察
week3.cと同じ内容であるweek4.cが作成できた.

(実習4-2)関数modelerの作成
6.3節で示した関数modeler()を作成し,関連部分を改修する.

プログラム

結果



考察
 アフィン変換を用いることによって,モデルを一つのウィンドウでいくつも表示できる.
glTranslatedにより,原点に表示したモデルを移動して位置を決めている.
また片方のトーラスのみ,glRotatedで図形の中心から90度を角度つけて,上下左右に交わっているようにしている.

(実習4-3)複合形状の作成
関数modeler()を作り変え,幾つかの形状を用いた複合形状のCGイメージを生成する.
どのような複合形状であってもよい.

プログラム

結果



考察
 サンプルにあった土台を元にピースマークを作ってみた.
対象のモデルに角度を付ける際,glRotated(角度,x,y,z);となっている.
3等分に円柱を付けたかったので,それぞれに120の倍数の値を角度に付け
Y軸を中心に回転させることによって完成できた.

2-6:作成したモデルデータ生成関数の詳細
・作成する形状:トーラス
プログラム

・関数の名前:IMDLtorus(Model &mdl, int num, double r1, double r2);

・関数のパラメータの説明
  num:円周上の分割数 
  r1:トーラスの半径 
  r2:トーラス断面の半径

・関数の利用方法
 libgm.hに関数IMDLtorus(Model *m,int num,double r1,doubler2)のプログラムを追加する.
 メインプログラムにIMDLtorus(&mdl,分割数,トーラスの半径,トーラス断面の半径)を記述する.

・モデル生成の方法
 z軸を中心としてその周りに円を回転させて生成できる物体をトーラスとする.
 関数IMDLtorus()に以下の式を記述する.
 x=(r2*cos(2.0*M_PAI/num*i)+r1)*cos(2.0*M_PAI/num*j);
  y=(r2*cos(2.0*M_PAI/num*i)+r1)*sin(2.0*M_PAI/num*j);
  z=r2*sin(i*2.0*M_PAI/num);
 これらの式を二重のfor文で上記の式を0からnum回繰り返すようにして実行した.

・表示例-1 ワイヤフレームの例



・表示例-2 レンダリングを施した例





考察
 トーラスは円の外側に回転軸を置き得られる回転体であるとわかる.
円周上での分割面を多くすることで滑らかなトーラスを描画することが可能である.