これまでテキストデータ(文字だけからなるデータ)を用いて、通常の文書データ(テキストファイル)や電子メールのデータ(メッセージ)、それにシェルの入力データ、すなわちシェルスクリプトなんかを作ってきました。特に、シェルスクリプトが表しているものは作業の手順であり、手続き (procedure) などと呼ばれたりします。このようにコンピュータは、対象をデータで表現し、それを入力し、加工し、出力することによって目的の仕事を達成する道具です。
コンピュータで画像や図形を扱う場合も、それらをコンピュータのデータとして表現する必要があります。したがってコンピュータグラフィックスを学ぶ場合には、画像データとか図形データなどと呼ばれるものがどういった構造をもっており、何を表そうとしているのかについて理解する必要があります。そこで今回は、ものの形をデータとして表すことについて考えてみます。
とは言っても、いわゆる CG 作成ソフトウェアが扱うデータは、そのソフトウェア自身で作成するか、形状作成専用のソフトウェアで作成するのが普通です。しかし、それでは形状がコンピュータのデータとしてどのように表現されているのかが隠されてしまいます。そこで、ここではテキストエディタを使って直接形状を記述する方法を採用します。そのための素材として、VRML (Virtual Reality Modelng Language) という言語を使用します。
VRML はインターネット上に3次元の仮想空間を構築するための言語で、仮想空間中に配置する物体の形状や振る舞い、そしてそこをさまよい歩くあなた自身を記述します。Web ページ上で3次元形状を表示することを目的に生まれた言語ですが、現在まであまり普及しているとは言えません。しかし、ここから始まった Web3D 技術は、これからのインターネットの応用方法として、高い可能性を持つもののひとつだと考えます(またはずす可能性もあるけど)。
簡単な VRML ファイルを作成してみましょう。
| テキストエディタで VRML ファイルを作成する |
|---|
% emacs data.wrl &[Enter] |
VRML ファイルの拡張子は .vrml とすることもありますが、ここでは .wrl としてください。wrl は world の意味だそうです。仮想世界のデータと言うわけですね。テキストエディタが起動したら、以下の内容をタイプしてください。
#VRML V2.0 utf8[Enter]
Shape {}[Enter]
|
タイプできたら C-x C-s でファイルを保存してください。このとき、emacs は動かしたままにしておいてください。それでは、このファイルによって記述された世界を見てみましょう。ターミナルのウィンドウで lookat あるいは gtklookat コマンドを実行してください。
| VRML ファイルを見る |
|---|
% lookat data.wrl[Enter] |
でも多分、開いたウィンドウは真っ黒で、何も見えないと思います。lookat コマンドを終了させるには、そのウィンドウを選択して q をタイプしてください。
| VRMLファイルのヘッダ |
|---|
#VRML V2.0 utf8 |
このファイルの1行目(#VRML V2.0 utf8)はヘッダと呼ばれ、このファイルが VRML ファイルであることを宣言します。VRML ファイルの先頭には、必ずこの1行を書きます。V2.0 は VRML の文法のバージョンを示し、utf8 は使用している文字コードを示します。
| VRMLファイルの書式 |
|---|
#VRML V2.0 utf8 ノード ノード … |
2行目以降にデータを記述します。Shape {} のようなものはノードと呼ばれ、これが VRML という言語の「単語」に相当します。VRML ファイルには、少なくとも1個の Shape ノードが含まれている必要があります。Shape ノードは「何か形がある」ことを示すノードです。
ノードは VRML ファイルを構成する基本単位です。VRML ファイルはいくつかのノードの集まりになっています。
| ノードの書式 |
|---|
ノード名 {
フィールド名 フィールド値
フィールド名 フィールド値
…
}
|
ノード名に続く { ... } 内には、そのノードを具体化する情報を記述します。この部分はフィールドと呼ばれます。以下は一つの Shape ノードだけからなる VRML ファイルですが、この Shape ノードには具体的な情報は何も記述されていません。
| 空の Shape ノードだけからなる VRML ファイル |
|---|
#VRML V2.0 utf8
Shape {}
|
したがってこの VRML ファイルは、何か形があることは示されていますが、それがどんな形をしているのか、どんな色をしているのかは決まっていません。この VRML ファイルを見ても何も見えないのは、このためです。では、Shape ノードの { ... } 内に geometry フィールドを置き、そこにこの Shape ノードの「形」として Sphere ノード(球)を指定してください(# より右側に書かれたものは無視されます)。
#VRML V2.0 utf8
Shape {[Enter]
geometry Sphere {} # 球[Enter]
}
|
タイプできたら C-x C-s でファイルを保存し、もう一度 lookat あるいは gtklookat コマンドを実行してください。何か見えましたか?
| VRML ファイルを見る |
|---|
% lookat data.wrl[Enter] |
この Sphere ノードも、大きさなどの具体的な情報が省略されています。Sphere ノードでは radius フィールドによってその球の半径を指定します。なお、VRML ファイルの改行は空白とほぼ同じに扱われるので、{ ... } 内で改行しても改行しなくても結果は変わりません。
#VRML V2.0 utf8
Shape {
geometry Sphere { radius 2 } # 球
}
|
タイプできたら C-x C-s でファイルを保存し、lookat あるいは gtklookat コマンドで見てください。何か変化はあったでしょうか?多分球のサイズはさっきの2倍になったと思います。つまり、Sphere ノードにおいて radius フィールドを省略した場合は、半径1の球になります。このように値の指定を省略したときに採用される値を、デフォルト値と呼びます。
この球には陰影が付いていないため、球と言ってもお月さまみたいに平たく見えてしまいます。物体が立体らしく見えるのは、物体の表面に光が当たって陰影が付くからです。陰影を表現するためには、物体表面における光の反射係数を指定する必要があります。Shape ノードには形を決める geometry フィールドのほかに、見かけを決める appearance フィールドを置くことができます。ここに Appearance ノードを置き、その material フィールドに指定する Material ノードによって光の反射係数を指定します。
#VRML V2.0 utf8
Shape {
geometry Sphere { radius 2 } # 球
appearance Appearance {[Enter]
material Material {}[Enter]
}[Enter]
}
|
タイプできたら C-x C-s でファイルを保存し、lookat あるいは gtklookat コマンドで見てください。ちゃんと球らしく見えるでしょうか?なお、この Material ノードもフィールドが省略されているので、デフォルトの反射係数が採用されます。
物体のノードです。 geometry フィールドに形状を指定します。 appearance フィールドには色などの属性情報を指定します。
| Shape ノードの書式 |
|---|
Shape {
geometry 形状
appearance 見かけ
}
|
Box(箱), Cone(円錐), Cylinder(円柱), Sphere(球), Text(文字), Extrusion(押し出し), ElevationGrid(地形等), IndexedFaceSet(ポリゴン集合), IndexedLineSet(線分集合), PointSet(点集合)
Shape ノードの geometry フィールドに指定する、球の形状のノードです。
| Sphere ノードの書式 |
|---|
Sphere {
radius 半径
}
|
コンピュータグラフィックスでは、物体の見え方も計算で求めなければなりません。物体の表面に光を当てたとき、そこがどんな色に見えるかを計算する処理を、シェーディング (shading、陰影付け) と言います。
![]() |
![]() |
| 面を単に色で塗り分けた場合 | シェーディングを行った場合 |
|---|
また、光の方向や視点の位置、物体表面の反射特性などの条件と陰影との関係を表したものをシェーディングモデル (shading model, 陰影付けモデル)と呼びます。この代表的なものに Phong のシェーディングモデルがあります。VRML の Material ノードで指定する反射係数は、Phong のシェーディングモデルに準じたものになっています。
![]() |
| 拡散反射と鏡面反射 |
|---|
Phong のシェーディングモデルでは反射光を、入射光が物体表面で鏡のように反射した鏡面反射光 (specular) と、一旦物体内に進入(屈折光)したあと物体内で散乱して再び外部に放出された拡散反射光 (diffuse) の2つの成分に分けて取り扱います。このような反射は金属以外の物質で起こります。鏡面反射光は一般にはハイライトと呼ばれ、通常は入射光の色がそのまま現れます。これに対して拡散反射光には物体の色が反映されます。つまり、拡散反射光が物体の見かけの色になります。このほか、物体表面には光源から直接届く光のほかに、壁などによる照り返しなどの間接光も届きます。間接光を正確に求めればかなりリアルな画像が得られますが、その分時間も(とても)かかります。そこで Phong のシェーディングモデルでは、間接光など光源から直接届く光以外のものをまとめて定数で表現しています。これを環境光 (ambient) と呼びます。
![]() |
+ | ![]() |
+ | ![]() |
| 拡散反射光による陰影 | 鏡面反射光による陰影 | 環境光による陰影 |
|---|
||
![]() |
| Phong のモデルによる陰影 |
|---|
余談ですが、間接光(相互反射光)を計算して陰影を求めるには、ラジオシティ (radiosity) 法という手段がよく用いられます。下の右側の図(ちょっと失敗しているけど)では、相互反射によって球の色が壁に映り込むカラーブリーディングという現象が発生しています。
![]() |
![]() |
| 環境光が定数 | 間接光を考慮 |
|---|
それでは、先ほど作成した球の色を変えてみましょう。色はどの色の光をどれだけ反射するかで決まります。例えば、赤い光だけ反射する物体は赤色に見えます。太陽光のような白色光はすべての色を含んでいるので反射率は色(=波長)に対する分布関数になってしまいますが、人間の目は赤、緑、青の光の3原色しか知覚できないので、この3色に対する反射係数を指定すれば任意の色を表現できます。
物体の色は拡散反射係数によって設定できます。拡散反射係数は、Material ノードの diffuseColor フィールドに赤、緑、青の順で指定します。これらは 0~1 の値をとります。
#VRML V2.0 utf8
Shape {
geometry Sphere { radius 2 } # 球
appearance Appearance {
material Material {[Enter]
diffuseColor 1 0 0[Enter]
}
}
}
|
タイプできたら C-x C-s でファイルを保存し、lookat あるいは gtklookat コマンドで見てください。上の例では赤成分の反射係数だけ1 (100%) になっているので、球は赤色に変わったと思います。赤、緑、青以外の色は、これらを混合して得られます。例えばオレンジ色は赤色と暗い緑色を合成すれば得られますから、diffuseColor フィールドに次のように変更します。
#VRML V2.0 utf8
Shape {
geometry Sphere { radius 2 } # 球
appearance Appearance {
material Material {
diffuseColor 1 0.5 0
}
}
}
|
それでは、次にハイライトを付けてみましょう。鏡面反射係数は、Material ノードの specularColor フィールドに赤、緑、青の順で指定します。これらも 0~1 の値をとります。ただし「拡散反射光+鏡面反射光+環境光≦入射光」という制限があるので、鏡面反射光の反射係数の分だけ拡散反射係数を下げなければ、不自然な陰影になってしまいます。また、非金属の材質ではハイライトに光源色がそのまま現れますから、鏡面反射係数の各成分を等しくしておきます。
#VRML V2.0 utf8
Shape {
geometry Sphere { radius 2 } # 球
appearance Appearance {
material Material {
diffuseColor 0.4 0.2 0
specularColor 0.6 0.6 0.6[Enter]
}
}
}
|
これに対して、金属のハイライトは材質の色に近い色になります(カラーシフトと言う現象が起きるので完全には一致しません)。従って、鏡面反射光の反射係数の「赤:緑:青」の比率は、拡散反射光の反射係数の比率と(だいたい)一致させます。
#VRML V2.0 utf8
Shape {
geometry Sphere { radius 2 } # 球
appearance Appearance {
material Material {
diffuseColor 0.4 0.2 0
specularColor 0.6 0.3 0
}
}
}
|
粗い表面ではハイライトが出ないか、淡く大きく広がります。逆に滑らかな表面では、ハイライトは鋭くはっきりと現れます。したがって、ハイライトの広がり方を制御することによって、物体表面の質感を制御することが可能になります。ハイライトの広がり具合は Material ノードの shininess フィールドに 0~1 の値で指定します。値が大きいほど輝きが鋭くなります。
#VRML V2.0 utf8
Shape {
geometry Sphere { radius 2 } # 球
appearance Appearance {
material Material {
diffuseColor 0.4 0.2 0
specularColor 0.6 0.3 0
shininess 1[Enter]
}
}
}
|
shininess の値の最大値は 0~1 のはずなんだけど、lookat だと 10 とかに設定しないとハイライトらしくなりません。
Shape ノードの appearance フィールドに指定する、物体の色などの属性情報を指定するノードです。
| Appearance ノードの書式 |
|---|
Appearance {
material 材質の指定
texture 貼り付けるテクスチャの指定
textureTransform テクスチャの張り付け位置
}
|
物体表面の材質パラメータを指定します。 Appearance ノードの material フィールドで使用します。
| Material ノードの書式 |
|---|
Material {
ambientIntensity 環境光の反射率
diffuseColor r g b
specularColor r g b
shininess 輝き
emissiveColor r g b
transparency 透明度
}
|
物体には一様な色ではなく、別の画像を貼り付けることもできます。物体の表面に画像を貼り付けて表面に模様を付けたりする処理を、テクスチャマッピングと言います。
![]() |
![]() |
| テクスチャマッピングなし | テクスチャマッピングあり |
|---|
先ほどの球に、下の画像を貼り付けて見ましょう。

貼り付ける画像は URL で指定します。したがって VRML では、インターネット上にある画像を貼り付けることができます。上の画像は http://www.wakayama-u.ac.jp/~tokoi/vrml/face.jpg という URL でアクセスできます。これを Appearance ノードの texture フィールドに ImageTexture ノードを指定し、その url フィールドに画像データの在り処を指定します。
#VRML V2.0 utf8
Shape {
geometry Sphere { radius 2 } # 球
appearance Appearance {
material Material {
diffuseColor 0.4 0.2 0
specularColor 0.6 0.3 0
shininess 1
}
texture ImageTexture {[Enter]
url "http://www.wakayama-u.ac.jp/~tokoi/vrml/face.jpg"[Enter]
}[Enter]
}
}
|
なお、画像を貼り付けた物体の色は画像と Material ノードで指定した色との合成になりますから、画像の色をそのまま使いたいときは Material ノードでグレー(灰色~白色)の反射係数(赤:緑:青の比が等しい)を設定してください。
#VRML V2.0 utf8
Shape {
geometry Sphere { radius 2 } # 球
appearance Appearance {
material Material {
diffuseColor 0.6 0.6 0.6
specularColor 0.4 0.4 0.4
shininess 1
}
texture ImageTexture {
url "http://www.wakayama-u.ac.jp/~tokoi/vrml/face.jpg"
}
}
}
|
テクスチャマッピングに使う画像を指定します。Appearance ノードの texture フィールドで使用します。
| ImageTexture ノードの書式 |
|---|
ImageTexture {
url 画像ファイルの URL
repeatS 画像空間のS軸(横)方向の繰り返し
repeatT 画像空間のT軸(縦)方向の繰り返し
}
|
それでは次に、この世界に他の部品を追加してみましょう。円柱を追加することにします。
#VRML V2.0 utf8
Shape {
geometry Sphere { radius 2 } # 球
appearance Appearance {
material Material {
diffuseColor 0.6 0.6 0.6
specularColor 0.4 0.4 0.4
shininess 1
}
texture ImageTexture {
url "http://www.wakayama-u.ac.jp/~tokoi/vrml/face.jpg"
}
}
}
[Enter]
Shape {[Enter]
geometry Cylinder {[Enter]
radius 1.5[Enter]
height 5[Enter]
}[Enter]
appearance Appearance {[Enter]
material Material {[Enter]
diffuseColor 1 1 0[Enter]
}[Enter]
}[Enter]
}[Enter]
|
Shape ノードの geometry フィールドに指定する、円筒の形状のノードです。
| Cylinder ノードの書式 |
|---|
Cylinder {
radius 半径
height 高さ
bottom 底面の有無
top 上面の有無
side 側面の有無
}
|
これを lookat か gtklookat で見ると、球と円柱が重なってしまっています。これは球も円柱も同じ位置に配置しているからです。
それでは、この円柱を平行移動してみましょう。物体を移動したり回転したりするためには、Transform ノードを使用します。移動したい Shape ノードを、Transform ノードの children フィールドに指定します。また、移動する量は translation フィールドに指定します。座標値は右手系で指定します。右図において、視点は最初Z軸上の正の方向にあります。
#VRML V2.0 utf8
Shape {
geometry Sphere { radius 2 } # 球
appearance Appearance {
material Material {
diffuseColor 0.6 0.6 0.6
specularColor 0.4 0.4 0.4
shininess 1
}
texture ImageTexture {
url "http://www.wakayama-u.ac.jp/~tokoi/vrml/face.jpg"
}
}
}
Transform {[Enter]
translation 0 -4 0[Enter]
children [[Enter]
Shape {
geometry Cylinder {
radius 1.5
height 5
}
appearance Appearance {
material Material {
diffuseColor 1 1 0
}
}
}
][Enter]
}[Enter] |
children フィールドの [ ... ] の中の Shape ノードは先ほどと変更ありませんが、ノードの階層関係(この Shape ノードは Transform ノードの子供にした)がぱっと見でわかるように、インデント(字下げ、行頭にスペースをあけること)しています。
Transform ノードの rotation フィールドを指定することで、物体を回転させることもできます。回転は回転軸の単位ベクトルと回転角(ラジアン)で指定します。1.5708 は約π/2(90°)です。
#VRML V2.0 utf8
Shape {
geometry Sphere { radius 2 } # 球
appearance Appearance {
material Material {
diffuseColor 0.6 0.6 0.6
specularColor 0.4 0.4 0.4
shininess 1
}
texture ImageTexture {
url "http://www.wakayama-u.ac.jp/~tokoi/vrml/face.jpg"
}
}
}
Transform {
translation 0 -4 0
rotation 0 0 1 1.5708[Enter]
children [
Shape {
geometry Cylinder {
radius 1.5
height 5
}
appearance Appearance {
material Material {
diffuseColor 1 1 0
}
}
}
]
}
|
children フィールドに列挙したノードに対して座標変換を行うグループ化ノードです。形状を空間中に配置するのに使います。
| Transform ノードの書式 |
|---|
Transform {
translation x y z
rotation x y z r
scale x y z
scaleOrientation x y z r
bboxCenter 外接箱の中心
bboxSize 外接箱サイズ
children [ノード ノード ... ]
}
|
球や箱のような、あらかじめ用意された単純な形状(プリミティブと呼びます)を空間中に配置して形状を表現する方法は簡単ですが、どんな形でもうまく表現できるわけではありません。物体形状を多面体で表現し、その個々の面(多角形)をデータに用いれば、どんな形でも正確に表現することができます。曲面は多面体で近似します。
下図の物体(ピラミッド型の5面体)を、VRML のデータにしてみましょう。

この形状は5つの頂点と5枚の面からなります。まず、個々の頂点の座標値を求めて、それぞれに番号を割り当てます。
| 頂点データ | |||
|---|---|---|---|
| 頂点番号 | x | y | z |
| 0 | 0 | 4 | 0 |
| 1 | -3 | 0 | -3 |
| 2 | -3 | 0 | 3 |
| 3 | 3 | 0 | 3 |
| 4 | 3 | 0 | -3 |
次に、各面がどの頂点で構成されているかを調べます。このとき頂点の順序は非常に重要であり、面を表から見たとき、頂点は必ず左回りにとる必要があります。例えば面4(底面)を下から見上げたとして頂点を左回りにたどれば、順序は4→3→2→1になります。
| 面データ | |||||
|---|---|---|---|---|---|
| 面番号 | 頂点番号 | ||||
| 0 | 0 | 1 | 2 | ||
| 1 | 0 | 2 | 3 | ||
| 2 | 0 | 3 | 4 | ||
| 3 | 0 | 4 | 1 | ||
| 4 | 4 | 3 | 2 | 1 | |
これが、この多面体の形状データです。この形状を VRML で使用するには、Coordinate ノードと IndexedFaceSet ノードを使います。emacs でこの形状の VRML ファイルを作成してみましょう。
| テキストエディタで VRML ファイルを作成する |
|---|
% emacs pyramid.wrl &[Enter] |
座標データは Coordinate ノードを使って表現します。point フィールドに [ ... ] でくくって座標値を列挙します。途中で改行しても構いません。
Coordinate {[Enter]
point [ 0 4 0 -3 0 -3 -3 0 3 3 0 3 3 0 -3 ][Enter]
}[Enter]
|
この Coordinate ノードを IndexedFaceSet ノードの coord フィールドに指定します。
IndexedFaceSet {[Enter]
coord Coordinate {
point [ 0 4 0 -3 0 -3 -3 0 3 3 0 3 3 0 -3 ]
}
}[Enter]
|
面データは、IndexedFaceSet ノードの coordIndex フィールドに [ ... ] でくくって頂点番号を列挙します。こちらも途中で改行することができます。ただし、面と面のデータを区切るために -1 を置いてください。
IndexedFaceSet {
coord Coordinate {
point [ 0 4 0 -3 0 -3 -3 0 3 3 0 3 3 0 -3 ]
}
coordIndex [ 0 1 2 -1 0 2 3 -1 0 3 4 -1 0 4 1 -1 4 3 2 1 -1 ][Enter]
}
|
それでは、このノードの形を見てみましょう。ファイルにヘッダを付け、このノードを Shape ノードの geometry フィールドに指定します。appearance ノードも設定しましょう。
#VRML V2.0 utf8[Enter]
Shape {[Enter]
geometry IndexedFaceSet {
coord Coordinate {
point [ 0 4 0 -3 0 -3 -3 0 3 3 0 3 3 0 -3 ]
}
coordIndex [ 0 1 2 -1 0 2 3 -1 0 3 4 -1 0 4 1 -1 4 3 2 1 -1 ]
}
appearance Appearance {[Enter]
material Material {}[Enter]
}[Enter]
}[Enter]
|
C-x C-s でこのファイルを保存し、lookat あるいは gtklookat コマンドで見てください。
Shape ノードの geometry フィールドに指定する、任意の多面体形状(ポリゴン)を表現するための形状のノードです。
| IndexedFaceSet ノードの書式 |
|---|
IndexedFaceSet {
creaseAngle スムーズシェーディングするときの限界角
ccw 頂点の順序が反時計回りか否か
convex 面がすべて凸多角形か否か
solid 閉じた形状か否か
colorPerVertex 頂点ごとに色を与えるか否か
normalPerVertex 頂点ごとに法線を与えるか否か
color 各面/頂点の色
normal 各面/頂点の法線ベクトル
texCoord テクスチャの座標
colorIndex 色データの指標
normalIndex 法線ベクトルデータの指標
texCoordIndex テクスチャ座標データの指標
coord 頂点座標データ
coordIndex 面データ(頂点座標データの指標)
}
|
IndexedFaceSet ノード、IndexedLineSet ノード、PointSet ノードの coord フィールドに指定する、頂点の座標値を指定するためのノードです。
| IndexedFaceSet ノードの書式 |
|---|
Coordinate {
point [ x y z x y z ... ]
}
|
(1)下図に示す図形(三角柱)を IndexedFaceSet ノード(あるいはExtrusion ノード)を使って表現しなさい。

(2)この授業で作成した data.wrl に(1)で作成した形状のノードを追加し、それを平行移動して下図のような図形を作成してください。

上の図形の VRML ファイルを電子メールで tokoi@sys.wakayama-u.ac.jp まで送ってください。Subject: は「第8回」にしてください。
VRML の仕様の一部をVRML の概要にまとめてあります(ごめんなさい、まだ途中)ので、 よかったら参考にしてください。PDF形式の資料もあります(いろいろ間違いがあります)。これはダウンロードした後 xpdf コマンドで読んでください。ただ、これらよりも about VRML97 の方がずっと良くできています。インターネット上では他にも多くの人が情報を提供しています。 これも Yahoo Japan の VRML のリンク集から探しはじめるといいでしょう。 安藤さんのページ にも豊富なリンクがあります。また、最新情報が知りたければ、大元の Web 3D Consortiumのページをあたってみてください。