第2回に引き続き、OpenGLの勉強を続けていきます。
前回は2次元での描画をやりましたが、今回から3次元のCGの扱いをやっていきたいと思います。
目次
今回の環境
・Windows 10
・Visual Studio 2015 Community
3D描画処理
今回は、3Dの物体を描くところはメインではやらず、3Dの物体がある際に、視点の位置や視野、ビューポートを設定する部分をやってみたいと思います。
今回は以下のようなソースコードを書いてみました。ワイヤーフレームの立方体が表示されるプログラムとなります。
【描画結果】
立方体の表示自体はdisplay関数の中にあるglutWireCube()という関数で描いているに過ぎず、今回は図形を描く方は特にメインでは扱いません。
上記のような立方体が3次元のワールド座標に存在していることを考えた場合、これを見るためには以下の情報が必要です。
(1) ビューポート
(2) 視点位置
(3) 視野(描画領域)
聞きなれない言葉もあるかと思うので、一つずつ説明していきます。
(1) ビューポート
聞きなれない言葉なので難しそうに感じるかもしれませんが、とても単純な話で、ビューポートとはウィンドウの中で表示を行うエリアのことです。
今回、ビューポートはglViewport関数で描かれていて、
- glViewport(0, 0, width, height);
で描画されています。最初の二つの引数が始点(x,y)で、後は幅と高さです。ちなみに(0,0)は左下です。上記の書き方の場合には、ウィンドウ全体をビューポートにするということです。
なので、例えば
glViewport(0, 0, width/2, height/2);
とした場合には、以下のように描画されます(オレンジの背景の部分は後から図を作るときに付与しています)。
基本的に、ウインドウ全体を使うのであれば、機械的に「glViewport(0, 0, width, height);」と書いてしまっていいと思います。
(2) 視点位置
こちらも単純な話で、3Dの物体が置かれている座標系に対して、それを見るためには視点の位置を決定しなければなりません。
// ③ 視点位置の決定 glMatrixMode(GL_MODELVIEW); // モデルビュー行列の設定 glLoadIdentity(); // 行列を初期化 gluLookAt(7.0, 5.0, 3.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
OpenGLの中での3Dグラフィックの描画には行列が使われており、ある視点から3Dシーンを見るためには、ワールド座標系からカメラ座標系への変換行列が必要です。
この変換行列がモデルビュー行列です。
少しごちゃごちゃ書きましたが、視点位置から見た各物体の座標を知るためには行列が必要で、その行列がモデルビュー行列です。
OpenGLではいくつかの行列があり、使う前に、「これから計算する行列はモデルビュー行列だよ」ということを明示してやる必要があります。
それがglMatrixMode関数で、「GL_MODELVIEW」を引数で与えることで、これからモデルビュー行列を計算しますよということを伝えます。
次の「glLoadIdentity();」は行列を初期化する関数で、単にモデルビュー行列を4×4の単位行列にします。
最後のgluLookAt関数が重要で、この関数は9つの引数を取りますが、引数を3つずつに分けて、視点位置(E_x, E_y, E_z)、注視点(C_x, C_y, C_z)、視点の姿勢(U_x, U_y, U_z)を表します。
感覚的に分かりにくいとすれば姿勢ですが、例えばサッカーでシュートを決めるシーンの絵を考えたときに、視点位置と注視点が同じでも、普通に立った状態で決めるのと、オーバーヘッドで決めるのでは見えている絵が上下反対になります。
よって、上記の三つの情報があって初めて視点が一意に定まるということです。
これをgluLookAtに与えることで、この視点情報に応じた4×4のモデルビュー行列が計算されます。
(3) 視野(描画範囲)
最後に、視野を定めます。
視点位置とビューポートの他に、視野が何度なのかという情報と、描画する領域がどのくらい先までなのかという情報が必要です。
一般に3DCGでは上記のように透視投影で描画することが多いです。
透視投影とは、3次元の物体を見たとおりに2次元平面に描画するためのレンダリング手法なのですが、上の図のように視点位置から離れたところにスクリーンを置いて、錐体(視体積)を作ることで描画領域とします。
これにより、同じ大きさの物体でも、遠いところにある物体が小さく見えるなど、自然な描画を可能にします。
この描画領域を示す視体積は以下の関数で作られます。
// ② 視野領域の決定 glMatrixMode(GL_PROJECTION); // 射影行列を操作する glLoadIdentity(); // 行列を初期化 gluPerspective(60.0, (double)width / height, 1.0, 100.0);
ここではモデルビュー行列ではなく、射影行列という行列を計算する必要があるため、(2)と同じようにglMatrixModeに、今度はGL_PROJECTIONを引数として与え、射影行列を扱うことを教えます。
実際に視体積を定義している部分はgluPerspective関数の中となります。
引数は「垂直方向の画角, アスペクト比, 視体積の手前のz値, 視体積の奥側のz値」となります。
例えば「 gluPerspective(60.0, (double)width / height, 1.0, 100.0);」のようになりますが、これをから見ると、下図の右のようになります。
ただし、視点位置を原点として考えます。
これによって視野角と、前、後ろ側をどこまで描画するかを設定することができます。
まとめ
今回は3DCGの描画の上での基礎的な事項を扱いました。
次回は物体の描画の方について、少し掘り下げてプログラムを書いてみたいと思います。