9. 透視投影

【プログラミング序論D】 目次, 講義ページ, 授業科目, www.kameda-lab.org 2017/01/12a

正投影は概念としてはわかりやすいですが、描画されたものは往々にして直感と合いません。
ここでは、いよいよ透視投影を利用します。

参考リンク

OpenGL(ver.2.1)の各関数(小文字のglの2文字で始まる関数)の説明
OpenGLのProgramming Guide Book (ver1.1, 通称赤本)
GLUT - The OpenGL Utility Toolkit
The OpenGL Utility Toolkit (GLUT) Programming Interface API Version 3


09.01. 透視投影の導入(暫定)

ic2_SetUpCamera_Perspective()
ic2_SetUpCamera_Ortho()の代わりに、今度は透視投影を担当する関数を製作します。
新しい関数でかつ外部から使ってもらう予定の関数なので、ヘッダファイルにプロトタイプ宣言も追加します。
ic2_SetUpCamera_Ortho()内部で注意すべき点は、glMatrixMode(GL_PROJECTION); で操作する行列をGL_PROJECTIONに変更することと、その直後に行列の初期化を行うことです。
(GL_MODELVIEW行列と同様に、放っておくと、OpenGLでは行列操作として、現在の行列に今回の行列を右から乗算してしまいます。)

gluPerspective()
gluPerspective()は、透視投影行列を(比較的簡単に)定義するためのGLUライブラリ関数です。
まずは自分なりに適切な値を考えて、実装してみましょう。

【プログラム09-01】(灰色分は変更無し)

  1. ic2-CommonHeaders.h (08-01からの差分)
  2. 09-01-Projection.c (07-03からの差分)
  3. 09-01-Rendering.c (08-04からの差分)
  4. 07-03-EmbededObjects.c, 07-04-Initialization.c, 07-05-Callback.c, 07-05-MainFunction.c, 08-01-GLTools.c

GLUライブラリの参照
ここから gluPersPective() という、GLUライブラリ関数を初めて使います。
これはGLUライブラリに含まれているので、プロジェクトのリンク設定で「GLU」を加えます。
方法は06.01.節の06-01-Aを参照してください。
(今後はglutとGLUの2つを指定する形になります。)

「暫定」とついているところから予想がつくように、せっかく作製しても、何も描画されません。

演習

09-01-ex1: 表示できない理由
本節のプログラムで何も表示されない理由を述べなさい。

09-01-ex2: gluPerspective()関数の4引数の意味
gluPerspective()関数の4引数の意味を、透視投影の概念をYZ平面上に図示してそこに記入する形で説明しなさい。


09.02. 透視投影の導入(正式)

カメラと物体との位置関係
09-01-ex1でも出てくるでしょうが、OpenGLの透視投影は、焦点がカメラ座標系の原点にあるという前提で定式化されています。
一方で、08.章までのプログラムでは、描画は各軸 -1.0 から 1.0 までの範囲を想定していました。
つまり、想定している描画範囲とカメラ位置がずれてしまっています。
09.01.節の暫定プログラムでは、gluPerspective()でレンダリングされる最近距離と最遠距離との間(2.0から4.0までで2.0の幅を設けています)の範囲が想定描画範囲を含んでいません。
描画範囲とレンダリング範囲を合わせるためには、「カメラを後ろに下げる」必要があります。
ところが、OpenGLの構造上、投影の直前は常にカメラ中心は投影が実施されるカメラ座標系の原点にあることが仮定されているので、カメラ中心をカメラ座標系の中心から動かすことは認められていません。
(厳密には、自分でやりたければそういうことも可能ですが、その場合、投影行列を自分の手で与えなければいけません)
そこで、カメラを後ろに下げる代わりに、「(レンダリングする直前に)全ての対象(実質的にはカメラ以外の世界全て)をカメラから離す」ことを考えます。
全ての移動・回転・スケーリング操作は相対的なので、これは実現可能です。
実際には、OpenGLではカメラはZ軸負の方向を向いていますから、全ての対象のZ値を少しマイナス側に動かすことになります。

画角
Z軸に沿って描画範囲とレンダリング範囲が一致しても、それだけで思うようなCGになる保証はありません。
そこで、画角を調整して、X軸方向とY軸方向のみかけの大きさを調整します。
OpenGLのデフォルトでは、投影に用いられる投影面(画像平面)も、実際に表示に使われるウィンドウも正方形になります(アスペクト比1.0)。
想定描画範囲もX,Y各軸 -1.0 〜 1.0 ですから、カメラから見た想定描画範囲も正方形になります。
そのため、ここでは、垂直画角だけ考えて計算することにします。
- カメラ焦点(カメラ座標系原点)から対象物体(ロゴマークの描画面)までの距離を3.0に取ります。
- OpenGLのデフォルトでは投影面の大きさは縦横とも -1.0 〜 1.0 とします。
これらの拘束条件から、ロゴマークをウィンドウいっぱいに表示するのに必要な垂直直画角は 約36.87°になります。

画角と焦点距離
今回のプログラムでは画角から透視投影行列に必要なパラメータを決定しましたが、通常はカメラの概念として、焦点距離(カメラ中心と投影面との距離)が使われます。
そのため、gluPerspective()を使いこなすには、ある画像平面を想定して、それに対して焦点距離から垂直画角を求める式が必要になります。

【プログラム09-02】(灰色分は変更無し)

  1. 09-02-Projection.c (09-01からの差分)
  2. 09-02-Rendering.c (09-01からの差分)
  3. 07-03-EmbededObjects.c, 07-04-Initialization.c, 07-05-Callback.c, 07-05-MainFunction.c, 08-01-GLTools.c, ic2-CommonHeaders.h

演習

09-02-ex1: 移動関数の記述場所
上記説明中、「(レンダリングする直前に)全ての対象をカメラから離す」ためには、実際の描画関数(glVertex3f())に対して、プログラムソース上どのような相対位置に移動関数を加えるべきか説明しなさい。

09-02-ex2: 移動・回転・スケーリング操作
移動・回転・スケーリング操作において、「カメラを固定して対象を動かす」ことと「対象を固定してカメラを動かす」ことは全く同一の効果として実施可能である理由を、線形代数を用いて説明しなさい。

09-02-ex3: 垂直画角が36.87°になる根拠
本節の説明で、垂直画角が36.87°になる計算過程を説明しなさい。

09-02-ex4: 距離が10のとき
09-02-ex3において、カメラ中心から物体中心までの距離を3から10に変更したときの垂直画角を、計算過程を示しながら求めなさい。

09-02-ex5: 焦点距離と垂直画角
画像面が正方で縦横 -1.0 〜 1.0 の一定サイズであるときの、焦点距離fと垂直画角fovyとの関係を定式化しなさい。

09-02-ex6: 35mmフィルム換算
デジタルカメラの商品説明にある「35mmフィルム相当で焦点距離 z mm」の意味を説明しなさい。

09-02-ex7: 35mmフィルム換算値からの導出
画像面の縦横比が35mmフィルムと同一で、かつ垂直方向の大きさが+/- 1.0であるとしたときに、35mmフィルム換算値 z mmを与えることで、焦点距離fと垂直画角fovyを求める式を導出しなさい。


09.03. 透視投影下での回転と移動

透視投影では奥行き感がでます。
実際に、様々な演習をしてそのことを実感してみましょう。
1例だけ実際に示しますので、後の演習は各自で実装してください。

Y軸回転
08-04-ex3と同様に、glRotatef()を使って、ロゴがY軸回りに(右側が最初に手前に来る形で)ゆっくり回転するプログラムを作成してみましょう。
注意すべきなのは、レンダリング範囲と想定描画範囲を合わせるための移動行列の存在です。
このために、08-04-ex208-04-ex3で利用したような簡易な実装は不可能になります。
回転の度合いを表す変数 roty を用意して、MODELVIEW行列の操作に臨みます。

様子がわかってきたと思うので、ついでに描画速度を更新します。

【プログラム09-02】(灰色分は変更無し)

  1. 09-03-Rendering.c (09-02からの差分)
  2. 09-03-Callback.c (07-05からの差分)
  3. 07-03-EmbededObjects.c, 07-04-Initialization.c, 07-05-MainFunction.c, 08-01-GLTools.c, 09-02-Projection.c, ic2-CommonHeaders.h

演習

09-03-ex1: X軸方向のロゴマーク回転
X軸方向にロゴマークが回転し続けるようにプログラムを作製しなさい。上が手前に降りてくるように見えるようにすること。

09-03-ex2: Z軸方向のロゴマーク往復
Z軸方向(元の描画想定範囲で)-2.0から-4.0までをロゴマークが往復し続けるようにプログラムを作製しなさい。

09-03-ex3: Z指定値付近でのロゴマーク往復
Z軸方向(元の描画想定範囲で)引数で正値 z を与え、-z+1 から +z-1 までをロゴマークが往復し続けるようにプログラムを作製しなさい。

09-03-ex4: 正弦波での往復
09-03-ex2のプログラムにおいて、z軸往復の-2.0から-4.0までの変位量を、経過時間に対する正弦波とする表示に改造しなさい。

09-03-ex5: 移動と回転
移動と回転を伴ってロゴマークが周期運動をするようにプログラムを改造しなさい。移動と回転は異なる周期になるようにすること。


kameda[at]iit.tsukuba.ac.jp