コンピューターグラフィックス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 01 0 0 00 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 ワールド座標系→カメラ座標系 01 00 00 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 01 00 00 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 01 00 00 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 01 00 00 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 01 00 00 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 01 00 00 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 01 00 00 1 0 0 sin theta_cycle 2 1 1 1 cos theta_cycle 2 0 0 0 0 1 0 0 1 0 0 01 00 00 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 01 00 00 1 0 0 sin theta_cycle 2 1 1 1 cos theta_cycle 2 0 0 0 0 1 0 0 1 0 0 01 00 00 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(); } まとめ • ポリゴンモデルの描画 • 変換行列を使った視点操作 • 変換行列を使ったアニメーション
© Copyright 2024 ExpyDoc