コンピューターグラフィックスS

コンピューターグラフィックスS
第11回 OpenGL演習(2)
システム創成情報工学科 尾下 真樹
OpenGL演習(確認)
• OpenGLの使い方を講義で説明
• 資料に従って、各自、プログラムを拡張
– 講義で学習した内容を、実際に作成して確認
– 一部のプログラムは、穴埋めになっている
演習資料(3種類)(確認)
• 演習資料(OpenGL演習)
– この資料に従って、プログラムを拡張していく
– 次回以降の分の説明は、逐次追加する
• コンパイル方法の説明資料
– コンパイル方法の詳しい説明
– CL端末や自宅での方法も一応説明
• OpenGL関数 簡易リファレンス
– OpenGLの関数を簡単に説明した資料
プログラム拡張の流れ
• 簡単なアニメーションの追加
• ポリゴンモデルの描画
• 変換行列を使った視点操作
• 変換行列を使ったアニメーション
• 文字の描画
• テクスチャマッピング
前回の演習問題の解説
問題1
• サンプルプログラムを実行
• (a) 背面除去を無効に設定
した時の結果とその理由を
説明せよ
• (b) さらに、Zバッファを無効
に設定した時の結果とその
理由を説明せよ
解答1
• プログラムの実行結果
– もとの状態
• 片面が青、もう片面が赤の三角形が回転
– (a)背面除去を無効にした時
• 常に青い三角面が描画される
– (b) さらに、Zバッファを無効にした時
• 常に赤い三角面が描画される
解答1
• (a)両面が青 の、回転する三角形が描画される
– 理由:背面除去が 無効 であるため、青い三角形と赤い
三角形の両方 の描画が行われるようになる。
また、Zバッファが 有効 であり、先に 青い三角形 の描画
処理が行われた後、同じ位置に 赤い三角形 の描画処
理が行なわれるため、赤い三角形が描画されない ため。
• (b)両面が赤 の、回転する三角形が描画される
– 理由:Zバッファが 無効 であり、先に 青い三角形 の描画
処理が行われた後、同じ位置に 赤い三角形 の描画処
理が行なわれるため、青い三角形が上書きされる ため。
解答1
• プログラム内で描画している三角面の順番
がポイント
Step 1. +Zの方向を向いた青い三角面を描画
Step 2. -Zの方向を向いた赤い三角面を描画
• 最初の状態では、回転の状態によって、表(視点方
向)を向いている方の三角面が描画される
-Z
+Z
解答1
• 最初の状態での処理
– 青い三角形が視点の方を向い
ている場合
• まず、青い三角形が描画される
• 次の赤い三角形は、視点から見
て裏を向いているため背面除去
の対象となり、描画されない
– 赤い三角形が視点の方を向い
ている場合
• 青い三角形は裏向きのため、背
面除去されて描画されない
• 赤い三角面が描画される
解答1
• (a)背面除去が無効な状態での処理
– 最初に、青の三角形が表裏どちらを向いていて
も、最初に描画される。
その後、赤の三角形を描画しようとしても、すで
にZバッファには青の三角形のZ値が書き込ま
れているため、同じ位置にある赤い三角形は描
画されない。
解答1
(b)背面除去+Zテストが無効な状態での処理
– (a)の時と同様、まず青の三角形が描画される
– 今度はZテストは行われないので、後から描画す
る赤の三角形が常に描画され、青い三角形の上
に上書きされる
解答1(補足)
• 補足
– (a)や(b)で、青や赤の三角形が、裏を向いてい
る時に暗く描画される理由
– 光源処理が適用されているため
• 面(頂点)の法線が光源の方向を向いているほど明
るく描画される
• 法線が光源と反対方向を向いている時は、拡散光に
よる影響はなくなり、環境光のみが考慮されるので暗
く描画される
問題2
• 立方体を描画
• 透視投影の設定を変更
• 透視投影を平行投影に変更
?
?
解答2(準備)
• 静止した立方体を描画するように変更
glTranslatef( 0.0, 1.0, 0.0 );
glRotatef( angle, 0.0f, 1.0f, 0.0f );
glBegin( GL_TRIANGLES );
glColor3f( 0.0, 0.0, 1.0 );
glNormal3f( 0.0, 0.0, 1.0 );
glVertex3f(-1.0, 1.0, 0.0 );
glVertex3f( 0.0,-1.0, 0.0 );
glVertex3f( 1.0, 0.5, 0.0 );
glColor3f( 1.0, 0.0, 0.0 );
glNormal3f( 0.0, 0.0,-1.0 );
glVertex3f(-1.0, 1.0, 0.0 );
glVertex3f( 1.0, 0.5, 0.0 );
glVertex3f( 0.0,-1.0, 0.0 );
glEnd();
glTranslatef( 0.0, 1.0, 0.0 );
glDisable( GL_LIGHTING );
glColor3f( 1.0, 0.0, 0.0 );
glutWireCube( 1.8 );
glEnalbe( GL_LIGHTING );
解答2(a)
• 透視投影の設定を変更
– 射影変換の設定で、視界が広く設定されたため、
結果的に全体が縮小して表示された。
• 単に視点を遠くに離しただけの場合とは異なる
?
解答2(a)
最初の視界
(a)視界角を広げた状態
参考:視野角はそのま
まで距離を離した状態。
同じくサイズは小さくな
るが、透視による見え
方は異なることに注目。
解答2(b)
• 透視投影を平行投影に変更
– 視点に対して垂直な立方体の辺は、平行投影し
た時にはスクリーン座標系でもカメラに対して垂
直になってしまうため、線分の始点と終点が同じ
座標になり、線分として描画されない
?
解答2(補足)
• ワイヤーフレームで描画された
立方体の色について
– ワイヤーフレームにも光源処理が
適用されるので、もともとの立方
体の各面の向きによって辺の色
が異なっている
– これを避けたい場合は、ワイヤー
フレームで描画する前に、光源処
理をオフにする
• glDiable( GL_LIGHTING )
– 描画が終わったら、他のオブジェ
クト(今回の例では地面)に光源
処理が適用されるように、光源処
理をオンに戻しておく
• glEnable( GL_LIGHTING )
OpenGL演習
プログラム拡張の流れ
• 簡単なアニメーションの追加
• ポリゴンモデルの描画
• 変換行列を使った視点操作
• 変換行列を使ったアニメーション
• 文字の描画
• テクスチャマッピング
簡単なアニメーションの追加
前回の演習で完成
ポリゴンモデルの描画
四角すいの描画
• 四角すいを構成する頂点と三角面
三角面
y
法線
{ V0, V3, V1 } { 0.0, 0.53, 0.85 }
V0 (0.0, 0.8, 0.0)
{ V0, V2, V4 } { 0.0, 0.53, -0.85 }
{ V0, V1, V2 } { 0.85, 0.53, 0.0 }
V4
V3
x
V2
V1 (1.0, -0.8, 1.0)
z
{ V0, V4, V3 } { -0.85, 0.53, 0.0 }
{ V1, V3, V2 } { 0.0, -1.0, 0.0 }
{ V4, V2, V3 } { 0.0, -1.0, 0.0 }
三面図
y
V0 1
V4
1
V3(V4)
V2
V0
x
1
V1(V2)
1
V3
z
※ yz平面は省略
V1
x
面の法線の計算方法
• ポリゴンの2辺の外積から計算できる
V1
N
N=(V3 - V1)×( V2 - V1)
長さが 1 になるよう正規化
y
V3
V2
N
• 断面で考えれば、もっと
簡単に法線は求まる
x
1.8
1.0
ポリゴンモデルの描画方法
• いくつかの描画方法がある
– プログラムからOpenGLに頂点データを与える方
法として、いろいろなやり方がある
• 形状データの表現方法の違い
– 頂点データのみを使う方法と、頂点データ+面
インデックスデータを使う方法がある
– 後者の方が、データをコンパクトにできる
• OpenGLへのデータの渡し方の違い
– OpenGLの頂点配列の機能を使うことで、より高
速に描画できる
ポリゴンモデルの描画方法(続き)
• 主な描画方法
方法1: glVertex() 関数に直接頂点座標を記述
頂点データ(直接記述)、OpenGLに頂点ごとに渡す
方法2: 頂点データの配列を使用
頂点データ、OpenGLに頂点ごとに渡す
方法3: 頂点配列を使用
頂点データ、OpenGLにまとめて渡す
方法4: 頂点データと面インデックスの配列を使用
頂点データ+面インデックス、OpenGLに頂点ごとに渡す
方法5: 頂点配列と面インデックス配列を使用
頂点データ+面インデックス、 OpenGLにまとめて渡す
方法1 最も基本的な描画方法
• サンプルプログラムと同様の描画方法
– glVertex() 関数の引数に直接頂点座標を記述
– ポリゴン数×各ポリゴンの頂点数の数だけ
glVertex()関数を呼び出す
四角すいの描画(1)
• 四角すいを描画する新たな関数を追加
void renderPyramid()
{
glBegin( GL_TRIANGLES );
// +Z方向の面
glNormal3f( 0.0, 0.53, 0.85 );
glVertex3f( 0.0, 1.0, 0.0 );
glVertex3f(-1.0,-0.8, 1.0 );
glVertex3f( 1.0,-0.8, 1.0 );
・・・・・・
以下、残りの7枚分のデータを記述
・・・・・・
glEnd();
}
四角すいの描画(2)
• 描画関数から四角すいの描画関数を呼び出し
– 修正の場所を間違えないように注意
– renderPyramid()関数では色は指定されていない
ので、呼び出す前に色を設定している
void display( void )
{
・・・・・・
// 中心に四角すいを描画((0,1,0) に移動)
glTranslatef( 0.0, 1.0, 0.0 );
glColor3f( 1.0, 0.0, 0.0 );
renderPyramid();
・・・・・・
}
方法2 頂点データの配列を使用
• 配列を使う方法
– 頂点データを配列として定義しておく
– glVertex() 関数の引数として配列データを順番
に与える
• 利点
– モデルデータが配列になってるので扱いやすい
頂点データの配列を使用(1)
• 配列データの定義
// 全頂点数
const int num_full_vertices = 18;
// 全頂点の頂点座標
static float pyramid_full_vertices[][ 3 ] = {
{ 0.0, 1.0, 0.0 }, {-1.0,-0.8, 1.0 }, { 1.0,-0.8, 1.0 },
・・・・
{-1.0,-0.8,-1.0 }, { 1.0,-0.8,-1.0 }, {-1.0,-0.8, 1.0 } };
// 全頂点の法線ベクトル
static float pyramid_full_normals[][ 3 ] = {
{ 0.00, 0.53, 0.85 }, { 0.00, 0.53, 0.85 }, { 0.00, 0.53, 0.85 },
・・・・
{ 0.00,-1.00, 0.00 }, { 0.00,-1.00, 0.00 }, { 0.00,-1.00, 0.00 } };
頂点データの配列を使用(2)
• 各頂点の配列データを呼び出す
void renderPyramid()
{
int i;
glBegin( GL_TRIANGLES );
for ( i=0; i<num_full_vertices; i++ )
{
glNormal3f( pyramid_full_normals[i][0],
pyramid_full_normals[i][1],
pyramid_full_normals[i][2] );
glVertex3f( pyramid_full_vertices[i][0],
pyramid_full_vertices[i][1],
pyramid_full_vertices[i][2] );
}
glEnd();
}
ここまでの方法の問題点
• 問題点
– 頂点ごとに glVertex(), glNormal() 関数を呼び出
す必要がある
– 一般に関数呼び出しにはオーバーヘッドがかか
るので、なるべく関数呼び出しの回数は少なくし
たい
• OpenGLの頂点配列の機能を使えば、この
問題を解決できる
方法3 頂点配列を使った描画方法
• 頂点配列・・・配列データを一度に全部 OpenGL に
渡して描画する機能
• 頂点配列を使った描画の手順
1. 頂点の座標・法線などの配列データを用意
2. OpenGLに配列データを指定(配列の先頭アドレス)
• glVertexPointer()関数、 glNormalPointer()関数、等
3. どの配列データを使用するかを設定
• 頂点の座標、色、法線ベクトル、テクスチャ座標など
• glEnableClientState()関数
4. 配列の使用する範囲を指定して一気に描画
• 配列データをレンダリング・パイプラインに転送
• glDrawArrays()関数
頂点配列を使用
• 頂点配列の機能を利用して描画
void renderPyramid()
{
glVertexPointer( 3, GL_FLOAT, 0, pyramid_full_vertices );
glNormalPointer( GL_FLOAT, 0, pyramid_full_normals );
glEnableClientState( GL_VERTEX_ARRAY );
glEnableClientState( GL_NORMAL_ARRAY );
}
glDrawArrays( GL_TRIANGLES, 0, num_full_vertices );
ここまでの方法の問題点
• 別の問題点
– ある頂点を複数のポリゴンが共有している時、
各ポリゴンごとに何度も頂点のデータを記述す
る必要がある
• 例:四角すいの頂点数は5個だが、これまでの方法
では、三角面数6×3個=18個の頂点を記述する必
要がある
– OpenGLは、与えられた全ての頂点に座標変換
などの処理を適用するので、同じモデルを描画
する時でも、なるべく頂点数が少ない方が高速
方法4 三角面インデックスを使用
• 頂点とポリゴンの情報を別々の配列に格納
– 頂点データの数を最小限にできる
• 描画関数では、配列のデータを順に参照し
ながら描画
• 必要な配列(サンプルプログラムの例)
– 頂点座標(x,y,z)×頂点数
– 三角面を構成する頂点番号(v0,v1,v2)×三角
面数
– 三角面の法線(x,y,z)×三角面数
三角面インデックス
• 頂点データの配列と、三角面インデックスの
配列に分けて管理する
三角面インデックス
面1 面2 面3 面4 面5 面6
面1 面2 面3 面4 面5 面6
何番目の頂点データを
使うかという情報
頂点データ(座標, 法線, 色など)
※ 頂点の重複がある
※ 頂点の重複がなくなる
頂点データ(座標, 法線, 色など)
配列を使った四角すいの描画(1)
• 配列データの定義
const int num_pyramid_vertices = 5; // 頂点数
const int num_pyramid_triangles = 6; // 三角面数
// 角すいの頂点座標の配列
float pyramid_vertices[ num_pyramid_vertices ][ 3 ] = {
{ 0.0, 1.0, 0.0 }, { 1.0,-0.8, 1.0 }, { 1.0,-0.8,-1.0 }, ・・・・・・
};
// 三角面インデックス(各三角面を構成する頂点の頂点番号)の配列
int pyramid_tri_index[ num_pyramid_triangles ][ 3 ] = {
{ 0,3,1 }, { 0,2,4 }, { 0,1,2 }, { 0,4,3 }, { 1,3,2 }, { 4,2,3 }
};
// 三角面の法線ベクトルの配列(三角面を構成する頂点座標から計算)
float pyramid_tri_normals[ num_pyramid_triangles ][ 3 ] = {
{ 0.00, 0.53, 0.85 }, // +Z方向の面
・・・・・・
配列を使った四角すいの描画(2)
• 配列データを参照しながら三角面を描画
void renderPyramid()
{
int i, j, v_no;
glBegin( GL_TRIANGLES );
for ( i=0; i<num_pyramid_triangles; i++ )
{
glNormal3f( pyramid_tri_normals[i][0],・・[i][1],・・ [i][2] );
for ( j=0; j<3; j++ )
{
v_no = pyramid_tri_index[ i ][ j ];
glVertex3f( pyramid_vertices[ v_no ][0], ・・・[ v_no ][1], ・・・
}
}
glEnd();
}
方法5 頂点配列+インデックス配列
• 頂点配列に加えて三角面インデックスを指
定し、OpenGLに一度にデータを渡して描画
する方法
• 描画の手順
– 基本的には、先ほどの頂点配列を使用する場
合と同様に、配列データを設定する
– 最後の描画時に、三角面インデックスを指定し
て描画
• glDrawArrays()関数の代わりに glDrawElements()
関数を使用
この方法の問題点
• 頂点の法線の問題
– 隣接するポリゴンが共有する頂点を一つにまと
めることができる
• 頂点データの数を減らすことができる
– ただし、頂点の座標だけでなく、法線やテクス
チャ座標も一緒にする必要がある
• 同じ頂点で、面ごとに法線を変えるようなことはでき
ない
• どうしても別の法線にしたければ、頂点データを分け
る必要がある
2種類の法線による結果の例
同一頂点でも面ごとに
異なる法線を使用
同一頂点には全部の面で
同じ法線を使用
※ 面と面の境界がおなじ色になる
頂点の法線の使い分け
• 頂点を共有する面全部で共通の法線
– 人体などの一般的な物体では、シェーディングによって
表面をなめらかに見せるため、頂点を共有する面全部
で頂点の法線を共通にするのが普通
– 法線データは頂点の数だけ必要
• 面ごとに別々の法線
– 今回の例の四角すいのように、角のある物体について
は、面ごとに法線を分けるのが自然
– 法線データは面の数(×3)だけ必要になる
• 両者の混在は困難なのでどちらかに決めて適用
頂点の法線の計算方法
• 頂点の法線を自動的に計算する方法
– 頂点の法線を全て零ベクトルにする
– それぞれの面ごとに面の法線を計算
• 面の法線を、面を構成する全頂点の法線に加算
– 最後に、それぞれの頂点の法線を正規化する
ポリゴンモデルの描画方法(まとめ)
• 主な描画方法
方法1: glVertex() 関数に直接頂点座標を記述
頂点データ(直接記述)、OpenGLに頂点ごとに渡す
方法2: 頂点データの配列を使用
頂点データ、OpenGLに頂点ごとに渡す
方法3: 頂点配列を使用
頂点データ、OpenGLにまとめて渡す
方法4: 頂点データと面インデックスの配列を使用
頂点データ+面インデックス、OpenGLに頂点ごとに渡す
方法5: 頂点配列と面インデックス配列を使用
頂点データ+面インデックス、 OpenGLにまとめて渡す
補足: レンダリングの高速化
x
y
z
頂点座標
座標変換
x
y
z
ラスタライズ
描画
スクリーン座標
• レンダリング時にボトルネックとなりうる処理
– ラスタライズ
– 頂点データの座標変換、頂点データの転送
– 各描画関数の呼び出し
• 環境やプログラムによりボトルネックは異なる
レンダリングの高速化
• ラスタライズの問題
– 特に、ハードウェアがサポートしていない環境
– Zソートなどと併用することで、高速化できる
• 頂点データの座標変換・転送の問題
– 特に、ハードウェアがサポートしていない環境
– 同じ頂点は共有するなど、データ量を減らすこと
で高速化できる
• 関数呼び出しのオーバーヘッドの問題
– 頂点配列・インデックス配列を使うことで高速化
別のポリゴンモデルの描画
直方体の描画
• 別のポリゴンモデル(直方体)の描画
– 1枚は空欄にしているので、各自、適切な頂点番
号を考えて追加する
四角面
法線
(-0.4, 1.0, -0.2)
V7
V3
{ V2, V3, V1 , V0 } { 0.0, 0.0, 1.0 }
V6
{ V7, V6, V4 , V5 } { 0.0, 0.0, -1.0 }
y
V2 (0.4, 1.0, 0.2)
{ V2, V0, V4 , V6 } { 1.0, 0.0, 0.0 }
{ V3, V7, V5 , V1 } { -1.0, 0.0, 0.0 }
V5
V1
z
V4
x
V0 (0.4, 0.0, 0.2)
{ V3, V2?
, V6 , V7 } { 0.0, 1.0, 0.0 }
{ V0, V1, V5 , V4 } { 0.0, -1.0, 0.0 }
配列を使った直方体の描画(1)
• 配列データの定義
– 四角面を使うので、各面の頂点数が4個になる
const int num_cube_vertices = 8;
const int num_cube_quads = 6;
// 頂点数
// 四角面数
// 頂点座標の配列
float cube_vertices[ num_cube_vertices ][ 3 ] = {
{ 0.4, 0.0, 0.2 }, // 0
・・・・・・
{-0.4, 1.0,-0.2 }, // 7
};
// 四角面インデックス(各四角面を構成する頂点の頂点番号)の配列
int cube_index[ num_cube_quads ][ 4 ] = {
{ 2,3,1,0 }, { 7,6,4,5 }, { 2,0,4,6 }, { 3,7,5,1 },
{ ?, ?,
??, ? }, { 0,1,5,4 }
};
配列を使った直方体の描画(2)
• 配列データの定義(続き)
– 今回は各面の色も指定する
// 四角面の法線ベクトルの配列(四角面を構成する頂点座標から計算)
float cube_normals[ num_cube_quads ][ 3 ] = {
{ 0.00, 0.00, 1.00 },
{ 0.00, 0.00,-1.00 },
・・・・・・
{ 0.00,-1.00, 0.00 } };
// 四角面のカラーの配列
float cube_colors[ num_cube_quads ][ 3 ] = {
{ 0.00, 1.00, 0.00 },
{ 1.00, 0.00, 1.00 },
・・・・・・
{ 1.00, 1.00, 0.00 } };
配列を使った直方体の描画(3)
• 配列データを参照しながら四角面を描画
void renderCube()
{
int i, j, v_no;
glBegin( GL_QUADS );
for ( i=0; i<num_cube_quads; i++ )
{
glNormal3f( cube_normals[i][0],・・[i][1],・・ [i][2] );
glColor3f( cube_colors[i][0], ・・・[i][1], ・・・[i][2] );
for ( j=0; j<4; j++ )
{
v_no = cube_index[ i ][ j ];
glVertex3f( cube_vertices[v_no][0], ・・[v_no][1], ・・[v_no][2] );
}
}
glEnd();
}
配列を使った直方体の描画(4)
• 描画関数から直方体の描画関数を呼び出し
– 適切な位置(移動の変換行列)を設定
– 色の指定は不要
void display( void )
{
・・・・・・
// 中心に直方体を描画
glTranslatef( 0.0, 0.0,
? 0.0 );
glColor3f( 1.0, 0.0, 0.0 );
renderCube();
・・・・・・
}
変換行列を使った視点操作
視点操作の拡張
• 左ドラッグで距離を操作できるように拡張
1

0

0

0
0
0
1
0
0
1
0
0
1

0
0

 cam era_distance   0
 
1
0
0
0
0
cos   cam era_pitch 
 sin   cam era_pitch 
sin   cam era_pitch 
cos   cam era_pitch 
0
0
ワールド座標系→カメラ座標系
z
y
x
camera_pitch
z
x
0
0
1
0
0
1
0
0
0

1

0

1 
 x   x 
   
y
y
  
 z   z 
   
1  1 
モデル座標系→ワールド座標系
camera_distance
y
01

0 0

00

1   0
(0,1,0)
視点操作の拡張
• プログラムの修正箇所(多いので注意)
– 左ボタンの押下状態を記録する変数を追加
– カメラと原点の距離を記録する変数を追加
– mouse()関数に、左ボタンの押下状態を更新す
る処理を追加
– motion()関数に、左ドラッグに応じて
camera_distance を変更する処理を追加
• 一定値以上は近づかないように制限
– display()関数を、camera_distance に応じて変換
行列を設定するように変更
変換行列によるアニメーション
変換行列によるアニメーション
• 変換行列を組み合わせることで、さまざまな
運動を実現できる
• idle()関数
– 運動を表す媒介変数の変化を記述
• dysplay()関数
– 媒介変数の値に応じて、回転角度や移動距離
を設定
アニメーションに使用する変数
• 資料に従って、いくつかの媒介変数を追加
– theta_cycle
• 0~360 へ単調増加、360 になったら 0 に戻る
– theta_repeat
• 0~180 の間を往復、180 になったら減少を始める
• theta_cycle から計算できる
– move
• 0~1 の間を加速度つきで往復
• 1に近づくと速度が減少
– theta_cycle2, theta_repeat2
アニメーションの例
• 一定速度で回転運動
• 一定位置で回転運動
• 一定速度で回転運動(常に正面を向く)
• 一定速度で往復回転運動
• 一定速度で上下に往復移動運動
• 加速度つきで上下に往復移動運動
• 複数の物体の運動の組み合わせ
例1:一定速度で回転運動
• 移動→回転の順に適用
– 移動にも回転が適用されるので、半径1.5で回転





M
 cos   theta_cycle 

0

   sin  theta_cycle



 
0

0
sin   theta_cycle 
1
1
1
cos   theta_cycle 
0
0
ワールド座標系→カメラ座標系
01

00
00
 
1   0
0
0
1
0
0
1
0
0
0 

0

1.5 

1 
 x   x 
   
y
y
  
 z   z 
   
1  1 
モデル座標系→ワールド座標系
y
z
1.5
x
例1:一定速度で回転運動
• 行列と同じ順序で、回転・平行移動を適用





M
 cos   theta_cycle 

0

   sin  theta_cycle



 
0

0
sin   theta_cycle 
1
1
1
cos   theta_cycle 
0
0
01

00
00
 
1   0
0
0
1
0
0
1
0
0
0 

0

1.5 

1 
void display( void )
{
・・・・・・
// 例1:一定速度で回転運動
glRotatef( theta_cycle, 0.0f, 1.0f, 0.0f );
glTranslatef( 0.0f, 0.0f, 1.5f );
renderCube();
・・・・・・
}
 x   x 
   
y
y
  
 z   z 
   
1  1 
例2:一定位置で回転運動
• 回転→移動の順に適用(順序を逆)
– 常に同じ位置に移動するので、その場で回転





M
1

0
0

 
0
0
0
1
0
0
1
0
0
0   cos   theta_cycle 

0 
0

1.5    sin   theta_cycle 

1  
0
y
z
x
0
sin   theta_cycle 
1
1
1
cos   theta_cycle 
0
0
0

0
0

1 
 x   x 
   
y
y
  
 z   z 
   
1  1 
例2:一定位置で回転運動
• 回転→移動の順に適用(順序を逆)
– 常に同じ位置に移動するので、その場で回転





M
1

0
0

 
0
0
0
1
0
0
1
0
0
0   cos   theta_cycle 

0 
0

1.5    sin   theta_cycle 

1  
0
0
sin   theta_cycle 
1
1
1
cos   theta_cycle 
0
0
// 例2:一定位置で回転
y
glTranslatef( 0.0f, 0.0f, 1.5f );
glRotatef( theta_cycle, 0.0f, 1.0f, 0.0f );
renderCube();
z
x
0

0
0

1 
 x   x 
   
y
y
  
 z   z 
   
1  1 
例3:一定速度で回転運動2
• 常に正面を向くようにするためには?
– 最初に逆方向に回転しておくことで、次の回転を
キャンセル (移動にのみ回転がかかる)





M
 cos   theta_cycle 

0

   sin  theta_cycle



 
0

0
sin   theta_cycle 
1
1
1
cos   theta_cycle 
0
0
01

00
00
 
1   0
0
0
1
0
0
1
0
0
y
z
x
0   cos  theta_cycle 

0 
0

1.5    sin  theta_cycle 

1  
0
0
sin  theta_c ycle 
1
1
1
cos  theta_c ycle 
0
0
0

0
0

1 
 x   x 
   
y
y
  
 z   z 
   
1  1 
例3:一定速度で回転運動2
• 常に正面を向くようにするためには?
– 最初に逆方向に回転しておくことで、次の回転を
キャンセル (移動にのみ回転がかかる)





M
 cos   theta_cycle 

0

   sin  theta_cycle



 
0

0
sin   theta_cycle 
1
1
1
cos   theta_cycle 
0
0
01

00
00
 
1   0
0
0
1
0
0
1
0
0
0   cos  theta_cycle 

0 
0

1.5    sin  theta_cycle 

1  
0
// 例3:一定速度で回転運動(常に正面を向く)
y
glRotatef( theta_cycle, 0.0f, 1.0f, 0.0f );
glTranslatef( 0.0f, 0.0f, 1.5f );
glRotatef( - theta_cycle, 0.0f, 1.0f, 0.0f );
z
renderCube();
x
0
sin  theta_c ycle 
1
1
1
cos  theta_c ycle 
0
0
0

0
0

1 
 x   x 
   
y
y
  
 z   z 
   
1  1 
例4:一定速度で往復回転運動
• 変換行列は例1と同じ、異なる変数を使用
– 変数の変化(idle()関数) と 変換行列の設定
(display()関数)の組み合わせが重要





M
 cos   theta_repeat 

0

   sin  theta_repeat





0

0
sin   theta_repeat 
1
1
1
cos   theta_repeat 
0
0
y
z
x
01

00
00
 
1   0
0
0
1
0
0
1
0
0
0 

0

1.5 

1 
 x   x 
   
y
y
  
 z   z 
   
1  1 
例4:一定速度で往復回転運動
• 変換行列は例1と同じ、異なる変数を使用
– 変数の変化(idle()関数) と 変換行列の設定
(display()関数)の組み合わせが重要





M
 cos   theta_repeat 

0

   sin  theta_repeat





0

0
sin   theta_repeat 
1
1
1
cos   theta_repeat 
0
0
01

00
00
 
1   0
0
0
1
0
0
1
0
0
// 例4:一定速度で往復回転運動
y
glRotatef( theta_repeat - 90, 0.0f, 1.0f, 0.0f );
glTranslatef( 0.0f, 0.0f, 1.5f );
renderCube();
z
x
0 

0

1.5 

1 
 x   x 
   
y
y
  
 z   z 
   
1  1 
例4:一定速度で往復回転運動
• アニメーション用の変数を追加
void idle( void )
{
・・・・・・
// theta_repeat を 0~180 の間で反復変化させる
// (180まで増加したら0まで減少する)
if ( theta_cycle <= 180 )
theta_repeat = theta_cycle;
else
theta_repeat = 360 - theta_cycle;
・・・・・・
}
例5:一定速度で上下に往復移動
• 回転だけではなく、位置に変数を使用するこ
ともできる
– めり込みを避けるために y座標値を +1 している





M
1

0
0

 
0
0
0
1
0
0
1
0
0


theta_repeat / 180  1


0

1

0
y
z
x
 x   x 
   
y
y
  
 z   z 
   
1  1 
例5:一定速度で上下に往復移動
• 回転だけではなく、位置に変数を使用するこ
ともできる
– めり込みを避けるために y座標値を +1 している





M
1

0
0

 
0
0
0
1
0
0
1
0
0


theta_repeat / 180  1


0

1

0
 x   x 
   
y
y
  
 z   z 
   
1  1 
// 例5:一定速度で上下に往復移動運動
y
glTranslatef( 0.0f, theta_repeat / 180.0 + 1.0f, 0.0f );
renderCube();
z
x
例6:加速度つきで上下に往復運動
• 変数の変化を工夫することで、移動速度を
変化させるようなこともできる
– ここでは三角関数の絶対値を利用
– 0~360 を 0~2πに変換している
void idle( void )
{
// move を 0~1 の間で反復変化させる
//(三角関数を用いることで、一定速度でなはなく、
// 0 の近くで速度が小さく
// 180 の近くで速度が大きくなるように変化させる)
move = fabs( sin( theta_cycle * 3.1415926 / 180.0 ) );
}
例6:加速度つきで上下に往復運動
• 例5と同様の変換行列を使用





M
1

0
0

 
0
0
0
1
0
0
1
0
0


m ove  1


0

1

0
 x   x 
   
y
y
  
 z   z 
   
1  1 
// 例6:加速度つきで上下に放物往復移動運動
y
glTranslatef( 0.0f, move + 1.0f, 0.0f );
renderCube();
z
x
例7:複数の物体の運動
• それぞれ異なる変換行列を使用して描画















 x   x 
   
y
y
  
 z   z 
   
1  1 
M





M
 cos   theta_cycle 

0

   sin  theta_cycle



 
0

M
 cos   theta_cycle 2 

0

   sin  theta_cycle 2



 
0

0
sin   theta_cycle 
1
1
1
cos   theta_cycle 
0
0
01

00
00
 
1   0
0
sin   theta_cycle 2 
1
1
1
cos   theta_cycle 2 
0
0
0
0
1
0
0
1
0
0
01

00
00
 
1   0
0 

0

1.5 

1 
0
0
1
0
0
1
0
0
0

0

3

1 
 x   x 
   
y
y
  
 z   z 
   
1  1 
 x   x 
   
y
y
  
 z   z 
   
1  1 
変換行列の退避・復元
• 現在の変換行列を記録しておき、後から復
元することができる
– 記録した変換行列はスタックに記録される
• glPushMatrix()
– 現在の変換行列の退避
– スタックに積む
• glPopMatrix()
– 最後に退避した変換行列の回復
– スタックから取り出す
変換行列の退避・復元の例
• ワールド座標系からカメラ座標系への
変換行列を設定




 World →
 Camera 




• 地面を描画
• 行列を退避
• 物体1からワールド座標系への変換行列
• 物体1を描画




 World →
 Camera 




• 物体2を描画
Obj1 →
World










 World →
 Camera 




• 行列を回復
• 物体2からワールド座標系への変換行列










 World →
 Camera 






Obj2 →
 World








例7:複数の物体の運動
• それぞれ異なる変換行列を使用して描画















 x   x 
   
y
y
  
 z   z 
   
1  1 
M





M
 cos   theta_cycle 

0

   sin  theta_cycle



 
0

M
 cos   theta_cycle 2 

0

   sin  theta_cycle 2



 
0

0
sin   theta_cycle 
1
1
1
cos   theta_cycle 
0
0
01

00
00
 
1   0
0
sin   theta_cycle 2 
1
1
1
cos   theta_cycle 2 
0
0
0
0
1
0
0
1
0
0
01

00
00
 
1   0
0 

0

1.5 

1 
0
0
1
0
0
1
0
0
0

0

3

1 
 x   x 
   
y
y
  
 z   z 
   
1  1 
 x   x 
   
y
y
  
 z   z 
   
1  1 
変換行列の退避・復元の例
• プログラムの例
void display( void )
{
// ここまででワールド座標系からカメラ座標系への変換設定
// 地面を描画
// 例7:2つの物体を描画(異なる周期で往復回転運動)
glPushMatrix();
glRotatef( theta_cycle2, 0.0, 1.0, 0.0 );
glTranslatef( 0.0, 0.0, 3.0 );
renderCube();
glPopMatrix();
glRotatef( theta_cycle, 0.0f, 1.0f, 0.0f );
glTranslatef( 0.0f, 0.0f, 1.5f );
renderCube();
}
まとめ
• ポリゴンモデルの描画
• 変換行列を使った視点操作
• 変換行列を使ったアニメーション