前回お話しした通り、「論より証拠」、「百聞は一見に如かり」ですので、実際にやってみましょう。(そのプログラムは末尾の【TestGLUT001.cpp】を参照して下さい。)

 

先ずプログラムを起動します。

x-y軸の2D画面ですが、この時マウスを左クリックすると、

コンソールに"Viewpoint is set at (0.5, 0.5, 0.5)"と表示され、gluLookAt関数で視点を (0.5, 0.5, 0.5)、参照中心点を (0.0, 0.0, 0.0)とした表示になります。(ご注意いただきたいのは、まだこの段階では何ら投影(投射)設定を行っていないので、GLUTのデフォルトの状態であることです。)

次にマウス中ボタンを押すと、

コンソールに"Viewpoint is set at (-0.5, 0.5, 0.5)"と表示され、視点も(-0.5, 0.5, 0.5)に移動します。(青のx軸の先が切れていることに注意してください。視点が視野空間の中央にあるのか、x軸方向-0.5の位置からだと1.0まで見通せないようです。)

次にマウス右ボタンを押すと、

コンソールに"Viewpoint is set at (-0.5, -0.5, -0.5)"と表示され、最初の反対側から見る視点へ移動します。(今度は全部の軸がちょん切れました。矢張り見通せる範囲は視点から1.0に限られるようです。)

 

同様に'1', '2', '3'のキーを順に押してもらうと同じ表示(イメージは同じなので省略)になりますが、(↓のプログラムコードを見ていただくと分かりますが、プログラム冒頭のSetProjection関数を呼び出しており、投影方法を決定するOrtho_PerspectフラグがデフォルトでTRUEなので)今度は明示的にglOrtho関数で平行投影処理を行い、「xyz軸すべて-1.0~1.0までの平行投影」で表示しています。

従ってGLUTのデフォルトの表示設定だこの状態だと考えてよいと思われます

 

この段階で'm | M'(または'l | L'キー)を押してください。すると、'1'キーでは、

続いて'2'キーでは、

最後に'3'キーでは、

の様に表示されます。これはデフォルトのモデル空間(ローカル空間)がxyz軸共に-1.0~1.0であるところ('s'キーを押すとこの状態に戻りますが)、'm'では-1.5~1.5、'l'では-2.0~2.0と大きくなるからです。また視点位置は変わっていないのに、なぜか3軸が離れてゆくように見えるのは、ローカル空間の投射範囲を大きくしたのですが、この空間がワールド空間に配置される際に座標が-1.0~1.0に正規化されてしまうのでしょう。このことから「平行投影範囲を変化させると、視点位置を変化させるのと見え方は変わらない」相対性があることが分かります。

 

では、今度は'f'キーを押してから、'1', '2', '3'のキーを順に押してみてください。

コンソールに"Viewing volume is set 0.8 away from the view point."とでますが、視点からo.8離れた所からその5倍の4.0までの視界空間(viewing volume-形は四角錐の頭を切ったような形状です)で同様の視点の移動を行った光景が見られます。更にこの視点をo.4にするために'n'を押してください。("Viewing volume is set 0.4 away from the view point."とコンソールに出ます。)視点からo.4離れた所からその5倍の2.0までの視界空間となります。

XYZ軸がはみ出した画像が出ます。又'2'、'3'通すと、

とまた異なった透視投影画像が確認できます。

 

このように、OpenGL(+GLUT)で、3次元グラフィックを描画するには、

 

(1)ローカル座標空間(モデルビュー空間)で、定義、描画したプリミティブの画像を(好みで縮小・拡大、回転、移動下後に)、

(2)ワールド座標空間へ投影(投射-デフォルトは平行投影)し、

(3)特定の視点から眺める(デフォルトはxyz軸共に-1.0~1.0)

 

プロセスを踏むことが必要であることが分かりました。

 

次回は座標だけではなく、実際に

 

GLSamplerで描画した正三角錐と直方体を描画し、視点を変えて眺めて

 

みようと思います。楽しみにしてくださいね。

 

【TestGLUT001.cpp】

#include <stdio.h>                //C言語ベースなのでprintf等を使う場合必要
#include <GL\freeglut.h>        //Embarcadero C++ コンパイラーの"BCC102\include\windows\sdk\GL"に入れる

//外部変数
int g_w, g_h;                                    //クライアント領域幅、高さ
GLdouble eyex = 0.0, eyey = 0.0, eyez = 0.9;    //視点座標
GLdouble vsize = 1.0;                            //平行投射範囲の1/2
bool Ortho_Perspect = TRUE;                        //平行投影か否(透視投影)か
GLdouble fov = 60.0;                            //視野角
GLdouble nDist = 1.0;                            //投射投影の視野垂台への距離

//投影(視野空間)設定
void SetProjection(void) {

    //変換行列の初期化(投影(プロジェクション)座標変換行列に単位行列を設定-注1
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();

    //透視投射範囲の設定
    if(Ortho_Perspect)
        glOrtho(-vsize, vsize, -vsize, vsize, -vsize, vsize);                        //平行投射範囲の設定
    else {
        if(!g_h)    g_h = 1;    //高さが0の場合は1に設定(解説:Division by 0エラー回避策です。)
        gluPerspective(fov, (double)g_w /(double)g_h, nDist, nDist * 5);    //透視投射範囲の設定
    }
    //モデルビュー変換行列を指定-注2
    glMatrixMode(GL_MODELVIEW);
}

/*

注1:投影(プロジェクション)座標変換行列とはモデルビュー座標をワールド座標に変換する為に乗じる行列です。「単位行列」とは「座標A行列 x 単位行列 = 座標A行列」となるような行列で、GLUTでは

    1,0,0,0

    0,1,0,0

    0,0,1,0

    0,0,0,1

の様になります。

注2:モデル座標変換行列とは、回転、縮小拡大、平行移動をするためにプリミティブのローカル座標に乗じる行列です。

*/


void DrawAxes(void) {    //解説:RGBのxyz軸を描く関数です。

    glPushMatrix();
        glBegin(GL_LINES);
            //X軸(青
            glColor3d(0.0, 0.0, 1.0);
            glVertex3d(0.0, 0.0, 0.0);
            glVertex3d(0.95, 0.0, 0.0);
            glVertex3d(0.95, 0.0, 0.0);
            glVertex3d(0.90, 0.03, 0.0);
            glVertex3d(0.95, 0.0, 0.0);
            glVertex3d(0.90, -0.03, 0.0);
            //Y軸(緑)
            glColor3d(0.0, 1.0, 0.0);
            glVertex3d(0.0, 0.0, 0.0);
            glVertex3d(0.0, 0.95, 0.0);
            glVertex3d(0.0, 0.95, 0.0);
            glVertex3d(0.03, 0.90, 0.0);
            glVertex3d(0.0, 0.95, 0.0);
            glVertex3d(-0.03, 0.90, 0.0);
            //Z軸(赤
            glColor3d(1.0, 0.0, 0.0);
            glVertex3d(0.0, 0.0, 0.0);
            glVertex3d(0.0, 0.0, 0.95);
            glVertex3d(0.0, 0.0, 0.95);
            glVertex3d(0.0, 0.03, 0.90);
            glVertex3d(0.0, 0.0, 0.95);
            glVertex3d(0.0, -0.03, 0.90);
        glEnd();
    glPopMatrix();
}

//アイドル時処理関数
void idle(void) {

    glutPostRedisplay();    //再描画関数(解説:これを読むと、InvalidateRectの様に再描画要求を出すだけですね。)
}

//描画関数
void display(void) {    //GLUTのコールバック関数です。描画命令を書き込みます。

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);    //色とZ(深度)バッファを初期化
    glLoadIdentity();                 //モデルビュー変換行列を初期化
    gluLookAt(    eyex, eyey, eyez,    //eyex, eyey, eyez-解説:視点の位置になります。
                0.0, 0.0, 0.0,         //targetx, targety, targetz-解説:表示されるウィンドウの中心点になります。
                0.0, 1.0, 0.0);        //アップベクター(上方向)-解説:画面の上がxyz軸いずれかを選びます。
    DrawAxes();                        //x, y, z軸を描画(サンプル)
    glFlush();                        //glutSwapBuffersで行われるので不要かも
    glutSwapBuffers();    //glutInitDisplayMode(GLUT_DOUBLE)でダブルバッファリングを利用可
}

//ウィンドウサイズ変更時関数
static void resize(int width, int height)
{
    //クライアントエリアサイズを記録
    g_w = width; g_h = height;
    //クライアントエリア全体に表示-解説:表示されるビューポートを設定します。
    glViewport(0, 0, g_w, g_h);
    //投影・視点再設定
    SetProjection();
    gluLookAt(    eyex, eyey, eyez,    //eyex, eyey, eyez
                0.0, 0.0, 0.0,         //targetx, targety, targetz
                0.0, 1.0, 0.0);        //アップベクター(上方向)
}

//マウス入力処理関数-解説:マウス関連のウィンドウメッセージ処理を簡単にしたものです。
void mouse(int button, int state, int x, int y) {

    switch(button) {
    case GLUT_LEFT_BUTTON:    //解説:マウス左ボタンが押された時
        eyex = eyey = eyez = 0.5;
        //透視変換(視野角90度、ビューポートの縦横比で、視点から1~4迄の視野空間を設定)
        printf("Viewpoint is set at (0.5, 0.5, 0.5)\r\n");
        break;
    case GLUT_MIDDLE_BUTTON: //解説:マウス中央ボタンが押された時
        eyex = -0.5;
        eyey = eyez = 0.5;

        printf("Viewpoint is set at (-0.5, -0.5, 0.5)\r\n");
        break;
    case GLUT_RIGHT_BUTTON: //解説:マウス右ボタンが押された時
        eyex = eyey = eyez = -0.5;
        printf("Viewpoint is set at (-0.5, 0.5, 0.5)\r\n");
        break;
    default:
        break;
    }
    display();
}

//キーボード入力処理関数-解説:キー入力関連のウィンドウメッセージ処理を簡単にしたものです。
void keyboard(unsigned char key, int x, int y) {

    switch(key) {
    case '\033':        //ESC
    case 'q':            //ESC か q をタイプしたら終了
        exit(0);
    case '1':    //解説:'1'キ-が押された時
        eyex = eyey = eyez = 0.5;
        printf("Viewpoint is set at (0.5, 0.5, 0.5)\r\n");
        break;
    case '2':    //解説:'2'キ-が押された時
        eyex = -0.5;
        eyey = eyez = 0.5;

        printf("Viewpoint is set at (-0.5, 0.5, 0.5)\r\n");
        break;
    case '3':    //解説:'3'キ-が押された時
        eyex = eyey = eyez = -0.5;
        printf("Viewpoint is set at (-0.5, -0.5, -0.5)\r\n");
        break;
    case 'S':    //解説:'S | s'キ-が押された時
    case 's':
        Ortho_Perspect = TRUE;    //平行投影変換
        vsize = 1.0;
        printf("Viewable space is set in the demention of -1.0 to 1.0 for both X, Y and Z.\r\n");
        break;
    case 'M':    //解説:'M | m'キ-が押された時
    case 'm':
        Ortho_Perspect = TRUE;    //平行投影変換
        vsize = 1.5;
        printf("Viewable space is set in the demention of -1.5 to 1.5 for both X, Y and Z.\r\n");
        break;
    case 'L':    //解説:'L | l'キ-が押された時
    case 'l':
        Ortho_Perspect = TRUE;    //平行投影変換
        vsize = 2.0;
        printf("Viewable space is set in the demention of -2.0 to 2.0 for both X, Y and Z.\r\n");
        break;
    case 'F':    //解説:'F | f'キ-が押された時
    case 'f':
        Ortho_Perspect = FALSE;    //透視投影変換
        nDist = 0.8;
        printf("Viewing volume is set 0.8 away from the view point.\r\n");
        break;
    case 'N':    //解説:'N | n'キ-が押された時
    case 'n':
        Ortho_Perspect = FALSE;    //透視投影変換
        nDist = 0.4;
        printf("Viewing volume is set 0.4 away from the view point.\r\n");
        break;
    default:
        break;
    }
    SetProjection();
    display();
}

//メイン(エントリーポイント)関数
int main(int argc, char *argv[]) {

    glutInit(&argc, argv);                //GLUTの初期化
    glutInitDisplayMode(GLUT_RGBA |        //ディスプレーの初期化
                        GLUT_DOUBLE |    //ダブルバッファの場合
                        GLUT_DEPTH);    //3Dグラフィックには必要
    glutInitWindowPosition(100, 100);    //ウィンドウ位置指定
    glutInitWindowSize(640, 640);        //ウィンドウサイズ指定
    //ファイル名だけを表示する
    char* fn = argv[0];
    for(char* pt = argv[0]; *pt; pt++) {
        if(*pt == '\\')
            fn = ++pt;
    }
    glutCreateWindow(fn);                //タイトル付ウィンドウ生成
    glShadeModel(GL_SMOOTH);            //既定値の滑らかな網かけ
    glEnable(GL_DEPTH_TEST);            //深度テストを有効にする
    glClearColor(0.0, 0.0, 0.0, 1.0);    //画面消去色(黒)
    glutDisplayFunc(display);            //描画関数の指定
    glutReshapeFunc(resize);            //ウィンドウサイズ変更時関数の指定
    glutIdleFunc(idle);                    //アイドル時処理関数の指定
    glutMouseFunc(mouse);                //マウス入力処理関数
    glutKeyboardFunc(keyboard);            //キーボード入力処理関数
    glutMainLoop();                        //メインループ関数
    return 0;
}