<「GLUTでOpenGL 3Dプログラミング(PDF版)」公開中(無料)>
GLUTでOpenGL 3Dプログラミング(PDF版)
>> 「GLUTでOpenGL 3Dプログラミング(PDF版)」はこちらよりダウンロードできます

GLUTのダウンロードと設定

0
    最初に、GLUTOpenGL Utility Toolkit)のダウンロードとインストールの仕方を解説します。

    GLUTOpenGLのウィンドウ表示を簡単にプログラミングするためのライブラリです。

    また、GLUTを使用するとプラットフォーム非依存のOpenGLプログラムを書くことができます。
    OpenGLが使える環境で、それぞれのプラットフォーム用のGLUTがインストールされていれば、同じプログラムをコンパイルして実行することができます。

    私も、昔、仕事で一度、GLUTを使ったことがあります。
    簡単な、自動車の運動計算のライブラリのデモプログラムで、計算の設定と実際の計算はWindowsのアプリケーションで行い、表示プログラムは、WindowsパソコンとSGIのワークステーションで動かすことができます。
    その表示プログラムを作るのに、GLUTを使用しました。同じプログラムを、Windows環境と、SGIのUNIX環境で、コンパイルして作成しました。

    GLUTのダウンロードですが、まず、GLUTのホームページは以下のURLにあります。
    GLUT and OpenGL Utility Libraries」
     http://www.opengl.org/resources/libraries/

    ここに、GLUTに関する説明とダウンロードの情報が掲載されています。
    すべて、英語です。

    次に、Windows版のGLUTをダウンロードしてみましょう。
    Windows版のGLUTは以下のURLからダウンロードできます。
    「Nate Robins - OpenGL - GLUT for Win32」
     http://www.xmission.com/~nate/glut.html

    このサイトの、「glut-3.7.6-bin.zip」をダウンロードします。

    GLUT for Win32のZIPファイルをダウンロード後、解凍すると以下のファイルが入っています。

    ・README-win32.txt
    ・glut32.dll
    ・glut32.lib
    ・glut.h

    まずは、READMEの説明を読んでください。
    そこに、インストール方法が書かれています。
    DLLはシステムフォルダに、libとヘッダーは、自分の使うVisualC++(VisualStudio)のライブラリファイルのフォルダとインクルードファイルのフォルダにコピーします。
    VisualStudio2005の場合は、例えば、以下の2つのフォルダになります。

    ・C:¥Program Files¥Microsoft Visual Studio 8¥VC¥PlatformSDK¥Lib
    ・C:¥Program Files¥Microsoft Visual Studio 8¥VC¥PlatformSDK¥Include¥gl

    以上で、GLUTのダウンロードとインストールは終わりです。

    VisualStudioでの具体的な設定などは、また次回に解説する予定です。


    <関連ページ>
    VisualC++でGLUTとOpenGLでプログラミングをするための設定

    VisualC++ 2008 Express EditionでWin32とOpenGLで画面をクリアするプログラムを作成する

    GLUTとOpenGLで画面をクリアするプログラム



    VisualC++でGLUTとOpenGLでプログラミングをするための設定

    0
      まず、お詫びしなければならないことがあります。

      すでに直してありますが、前回の記事で、glut32.libとglut.hの2つのファイルのインストール先を以下のように書きました。

      ・C:¥Program Files¥Microsoft Visual Studio 8¥VC¥lib
      ・C:¥Program Files¥Microsoft Visual Studio 8¥VC¥include

      しかし、これは誤りで、正しくは、以下の2つのフォルダとなります。

      ・C:¥Program Files¥Microsoft Visual Studio 8¥VC¥PlatformSDK¥Lib
      ・C:¥Program Files¥Microsoft Visual Studio 8¥VC¥PlatformSDK¥Include¥gl

      OpenGLは、Win32 APIの関数という位置づけになります。
      VisualStudio2005では、Win32 APIのインクルードファイルやライブラリファイルは、「VC」フォルダの中の「PlatformSDK」というフォルダの中にあります。
      したがって、GLUTのインクルードファイルやライブラリファイルも、このフォルダの中の、「Include」や「Lib」というフォルダの中に、入れることになります。
      さらに、インクルードファイルは、「Include」フォルダの中の、「gl」というフォルダの中に入れます。
      このフォルダの中には、gl.hやglu.hというOpenGLのインクルードファイルも入っています。


      さて、VisualStudio(VisualC++)でGLUTOpenGLを使えるようにするための設定ですが、実は、私がダウンロードしたものでは、GLUTのインクルードファイルglut.hの中で、gl.h、glu.hなどの必要なインクルードファイルがインクルードされています。
      また、必要なライブラリファイルについても、自動的にリンクされるように記述されていました。

      そのため、GLUTOpenGLを使ったプログラムの中で、

      #include  <GL/glut.h>

      と、GLUTのインクルードファイルをインクルードするだけで、ビルドすることができました。

      試しに、前回ご紹介したGLUTのホームページからダウンロードしてきたcube.cというサンプルプログラムをビルドして実行してみました。

      以下のようになります。

      cube.c実行画面


      サンプルプログラムをビルドする手順は次のようになります(ここではVisualStudio2005で解説します。VisualC++ 2008でも基本的操作はほぼ同じです)。

      1)VisualStudio2005のメニューの、[ファイル] → [新規作成] → [プロジェクト...]を選択します。
      2)一番左の「プロジェクトの種類」というところで、[VisualC++] → [全般]を選択します。
      3)右側の「テンプレート」のなかで、「空のプロジェクト」を選択します。
      4)プロジェクト名を入力し、プロジェクトを作成するフォルダを指定し、空のプロジェクトを作成します。
      5)ビルドしたいサンプルプログラムのソースファイルを作成したプロジェクトのフォルダの中にコピーします。
      6)プロジェクトにコピーしたサンプルプログラムのソースファイルを追加します。
      7)あとはビルドして、[デバッグ] → [デバッグなしで開始]を選択して実行するだけです。


      次に、念のため、GLUTOpenGLを使ってプログラミングするために必要な設定を説明します。

      まず、インクルードファイルですが、必要なインクルードファイルは、OpenGLのものがgl.h、glu.hの2つになります。
      また、GLUTのものは、前回にインストールしたglut.hとなります。
      これらは、VisualStudioで使う、PlatformSDKのインクルードファイルの中の、「gl」フォルダの中にあります。
      これらを、以下のように、インクルードします。

      #include  <GL/glut.h>
      #include  <GL/gl.h>
      #include  <GLgl/glu.h>

      glut.hの中で、gl.hとglu.hがインクルードされているため、この順番にしてください。

      もちろん、前述のように、以下の1行だけでも大丈夫です。

      #include  <GL/glut.h>


      次に、ライブラリファイルを設定する方法を説明します。

      ライブラリファイルの設定をするには、まず、左側のソリューションエクスプローラで、プロジェクト名を選択し、メニューの、[プロジェクト] → [プロパティ]を選択します。

      開いたダイアログ(プロパティ ページ)の左側で、[構成プロパティ] → [リンカ] → [入力]を選択します。
      次に、右側の一番上、「追加の依存ファイル」の欄に以下のように入力します。

      opengl32.lib glu32.lib glut32.lib


      これで、設定は終わりです。もちろん、前述のように、GLUTのインクルードファイルをインクルードするだけでも大丈夫です。

      それでもうまくいかない場合は、上のように設定してみてください。


      次からは、いよいよ、GLUTOpenGLを使ってプログラミングをしていく方法を解説していきます。


      <関連ページ>
      GLUTのダウンロードと設定

      VisualC++ 2008 Express EditionでWin32とOpenGLで画面をクリアするプログラムを作成する

      GLUTとOpenGLで画面をクリアするプログラム



      GLUTとOpenGLで画面をクリアするプログラム

      0
        今回は、GLUTOpenGLを使ったプログラムの一番基本的なものとして、画面を赤くクリアするプログラムを作ってみたいと思います。


        ●VisualStudio2005で雛形を作る

        最初に、VisualStudio2005で雛形を作ります。

        雛形は、以下の手順で作成します。

        VisualStudioの[ファイル] → [新規作成] → [プロジェクト]を選択します。
        次に、左側の「プロジェクトの種類」で[VisualC++] → [Win32]を選択します。
        右側のテンプレートに、「Win32 コンソール アプリケーション」と「Win32 プロジェクト」が表示されるはずですが、そこで、「Win32 コンソール アプリケーション」を選択します。
        プロジェクトを作る「場所」を選択し、「プロジェクト名」を入力し、「OK」ボタンを選択します。

        次の画面で、「完了」ボタンを選択します。

        これで、コンソールアプリケーションの雛形ができます。
        以下のような、プログラムが作成されると思います。


        #include "stdafx.h"


        int _tmain(int argc, _TCHAR* argv[])
        {
        return 0;
        }



        しかし、このままだと、プロジェクトがUnicodeに設定されていて、GLUTを使ったプログラムを作るには、何かと不便です。
        GLUTには、C言語のmain関数の引数、argcとargvを受け取る初期化関数がありますが、この関数は、引数に、argvとして1バイト文字列の配列を受け取ります。

        そこで、この雛形のプロジェクトをUnicode文字セットを使用するものから、マルチバイト文字セットを使用するものに変更します。

        まず、左側のソリューションエクスプローラでプロジェクト名を選択し、メニューから、[プロジェクト] → [プロパティ]を選択します。

        表示された画面の左側で、[構成プロパティ] → [全般]を選択します。
        右側の画面の上から5つ目の「文字セット」の内容を、「Unicode文字セットを使用する」から「マルチバイト文字セットを使用する」に変更します。

        これで、先ほどのサンプルのままで使用することができます。
        この場合、_tmainはmainに、_TCHARはcharに変換されます。

        このままだと気になるときは、_tmainと_TCHARをmainとcharに変更し、C言語の標準のmain関数に書き換えてしまっても大丈夫です。

        なお、stdafx.hはVisualC++のプリコンパイルヘッダで、ここで説明した方法でプロジェクトを作成した場合、必要となります。


        #include "stdafx.h"


        int main(int argc, char* argv[])
        {
        return 0;
        }



        ●OpenGLで画面をクリアする

        OpenGLで画面をクリアするには、glClearColorとglClearを使います。

        glClearColerは画面をクリアする色を指定します。
        この関数は、R(赤)、G(緑)、B(青)、A(不透明度)の色の4つの要素を、この順番で引数にとります。値は、0.0から1.0の間で指定します。
        例えば、1.0、0.0、0.0、1.0の4つの値を引数に渡すと、画面をクリアする色は赤に設定されます。

        glColorは各種バッファをクリアする関数です。
        この関数の引数に、GL_COLOR_BUFFER_BITを渡すと、カラーバッファがクリアされ、画面がクリアされます。

        これらの関数を使って画面をクリアする処理は、GLUTでウィンドウが表示されたときに呼び出されるコールバック関数に記述します。
        以下が、そのサンプルコードです。
        このサンプルでは、displayという関数の中で、OpenGLのglClearColorとglClearを使って、画面をクリアしています。
        今回は、GLUTの設定でシングルバッファを使用しているため、最後にglFlushを使って、OpenGLのコマンドをバッファから強制的に送り出して、描画させます。


        void display()
        {
        glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);
        glFlush();
        }



        ●GLUTの設定

        次に、GLUTの設定ですが、これは、先にサンプルコードを示した方がいいかもしれません。
        以下のようになります。


        int main(int argc, char* argv[])
        {
        glutInit(&argc, argv);
        glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA);
        glutInitWindowPosition(100, 100);
        glutInitWindowSize(640, 480);
        glutCreateWindow("Clear window");
        glutDisplayFunc(display);
        glutMainLoop();

        return 0;
        }


        先ほど説明した雛形のmain関数の中で、GLUTでウィンドウを表示しています。

        glutCreateWindowまでの処理が、GLUTの初期化、ディスプレイモードの設定、ウィンドウの位置とサイズの設定、ウィンドウの生成の処理を行っています。

        ここでは、ウィスプレイモードとして、シングルバッファを指定し、RGBA形式(インデックス形式によるものではなく)による色表現を指定しています。

        glutInitWindowPositionはウィンドウの表示位置のx座標とy座標をウィンドウ座標で指定します。単位はピクセルです。
        ウィンドウ座標は、ディスプレイの左上を原点とし、右方向がx座標のプラス、下方向がy座標のプラスになります。
        ここでは、ウィンドウの表示位置を(100,100)に指定しています。

        また、glutInitWindowSizeはウィンドウの幅と高さのサイズを指定します。単位は、glutInitWindowPositionと同様に、ピクセルです。
        ここでは、幅を640ピクセル、高さを480ピクセルに指定しています。

        glutCreateWindowはウィンドウの生成を行います。
        引数には、ウィンドウのタイトル文字列を渡します。


        ウィンドウの生成が終わったら、コールバック関数を設定します。
        glutDisplayFuncはウィンドウが表示されたときや再描画が必要になったときに呼び出されるコールバック関数を設定します。
        ここでは、先ほどのOpenGLで画面をクリアする関数、displayを設定しています。
        コールバック関数は、ウィンドウを生成した後に設定します。
        コールバック関数の設定をウィンドウの生成の前に行うと、実行時エラーとなります。


        最後に、glutMainLoopでメインループに入ります。


        以上の、GLUTOpenGLで画面をクリアするプログラムの全体のコードは、以下のようになります。
        GLUTを使用しているため、非常にシンプルなものになります。


        #include "stdafx.h"
        #include  <GL/glut.h>


        void display()
        {
        glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);
        glFlush();
        }


        int main(int argc, char* argv[])
        {
        glutInit(&argc, argv);
        glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA);
        glutInitWindowPosition(100, 100);
        glutInitWindowSize(640, 480);
        glutCreateWindow("Clear window");
        glutDisplayFunc(display);
        glutMainLoop();

        return 0;
        }



        実行結果は、次のようになります。

        GLUTとOpenGLで画面を赤くクリアするプログラム

        ソース(マウス右クリック、対象ファイルに保存でダウンロードして下さい)


        <関連記事>

        GLUTとOpenGLで三角形を描画するプログラム
        GLUTとOpenGLで立方体を描画するプログラム
        GLUTとOpenGLでライティングされた立方体を描画するプログラム
        GLUTとOpenGLでティーポットを描画するプログラム
        GLUTとOpenGLで立方体を回転させて表示するプログラム
        法線ベクトルを計算する方法
        Win32とOpenGLで画面をクリアするプログラム

        GLUTとOpenGLで三角形を描画するプログラム

        0
          今回は、GLUTOpenGLを使って、2次元の赤い三角形を描画するプログラムを作ってみたいと思います。

          OpenGLで2次元の三角形を描画するには、OpenGLでの描画の始まりと終わりを示すglBeginとglEnd、頂点を指定するglVertex2f、図形の色を指定するglColor3fを使う必要があります。

          まず、glBeginとglEndは、OpenGLによる図形の描画の開始と終了を指定します。
          glBeginに渡す引数で、どのような図形を描画するかを指定します。
          今回は、多角形を描画するGL_POLYGONを使うことにします。

          次に、glBeginとglEndの間で、glVertex2fにより、三角形の頂点を指定していきます。
          glVertexで始まる関数はいくつかありますが、2次元の図形を描画するときは、このglVertex2fという関数を使うのが一番簡単です。
          この関数に三角形の各頂点のx座標とy座標をfloat型で指定していきます。
          三角形を描画するときは、glVertex2fを3回呼び出す必要があります。

          三角形の頂点を指定する前に、三角形を塗りつぶす色を指定する必要があります。これには、glColor3fを使います。
          この関数には、塗りつぶす色の赤(R)、緑(G)、青(B)の値を0.0から1.0の間で指定します。
          三角形を単一の色で塗りつぶすときは、glVertex2fで頂点を指定する前に、glColor3fを一回だけ呼び出せば大丈夫です。

          以上をまとめると、以下のサンプルコードのようになります。
          こららの処理は、前回も出てきたdisplayというコールバック関数の中で実行しています。

          なおglBeginの引数には色々な定数を指定でき、様々な図形を描画することができます。
          これらについては、書籍などを参考にしてください。


          void display()
          {
          glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
          glClear(GL_COLOR_BUFFER_BIT);

          glColor3f(1.0f, 0.0f, 0.0f);
          glBegin(GL_POLYGON);
          glVertex2f(-0.5f, -0.5f);
          glVertex2f( 0.5f, -0.5f);
          glVertex2f( 0.0f, 0.5f);
          glEnd();

          glFlush();
          }



          main関数の中のGLUTの処理は、前回とほとんど同じです。全体では以下のようになります。



          #include "stdafx.h"
          #include  <GL/glut.h>


          void display()
          {
          glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
          glClear(GL_COLOR_BUFFER_BIT);

          glColor3f(1.0f, 0.0f, 0.0f);
          glBegin(GL_POLYGON);
          glVertex2f(-0.5f, -0.5f);
          glVertex2f( 0.5f, -0.5f);
          glVertex2f( 0.0f, 0.5f);
          glEnd();

          glFlush();
          }


          int main(int argc, char* argv[])
          {
          glutInit(&argc, argv);
          glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA);
          glutInitWindowPosition(100, 100);
          glutInitWindowSize(640, 480);
          glutCreateWindow("2D Triangle");
          glutDisplayFunc(display);
          glutMainLoop();

          return 0;
          }



          実行結果は、次のようになります。

          GLUTとOpenGLで2次元の三角形を表示するプログラム

          ソース(マウス右クリック、対象ファイルに保存でダウンロードして下さい)


          <関連記事>

          <GLUTとOpenGLで画面をクリアするプログラム
          GLUTとOpenGLで立方体を描画するプログラム
          GLUTとOpenGLでライティングされた立方体を描画するプログラム
          GLUTとOpenGLでティーポットを描画するプログラム
          GLUTとOpenGLで立方体を回転させて表示するプログラム
          法線ベクトルを計算する方法
          Win32とOpenGLで画面をクリアするプログラム

          GLUTとOpenGLで立方体を描画するプログラム

          0
            今回は、GLUTOpenGLで3次元の立方体を描画するプログラムを作ってみたいと思います。


            GLUTOpenGLで、3次元の立方体を描画する処理は、大きく2つに分かれます。

            1つは、ビューポート、投影変換行列、視点位置の指定です、
            これらは、ちょうど、描画したい3次元物体の前にカメラを設置するのに似ています。
            これらの処理は、ウィンドウのサイズが変更されるたびに行います、

            もう1つは、3次元空間内で立方体の頂点を指定していき、描画する処理です。
            これは、2次元で三角形を描画した処理を3次元に拡張したものです。
            これらの処理は、ウィンドウが表示されたときに行います。


            ●視点の設定

            まず、ビューポート、投影変換行列、視点位置の指定について説明します。

            ビューポートはウィンドウ内で作成した3次元画像を表示する領域です。
            ビューポートの設定は、OpenGLのglViewportという関数で行います。
            この関数は、ビューポートの左下のx座標、y座標と幅、高さを引数に取ります。
            ビューポートの位置は、ウィンドウの左下を原点とし、右が+Xの方向、上が+Yの方向として設定します。

            投影変換は、3次元空間上の図形を描画し、2次元平面にマッピングする変換です。
            投影変換は平行投影と透視投影がありますが、今回は、透視投影を使います。
            透視投影とは、遠近法に基づいて、遠くのものの方が近くのものより小さく見えるように3次元画像を描画する投影方法です
            OpenGLで透視投影変換行列を設定するには、gluPerspectiveという関数を使用します。
            この関数は、Y方向の視野角(単位は度(degree))、アスペクト比(ビューボリュームの幅を高さで割った比)、ニアクリップ面までの距離、ファークリップ面までの距離を引数にとります。
            この関数を使うには、glMatrixModeにGL_PROJECTIONを指定して、OpenGLのカレントの行列を投影変換行列にする必要があります。

            次に、視点位置の指定ですが、これは、3次元のワールド空間上の座標で指定します。
            OpenGLで視点位置を指定するには、gluLookAtという関数を使います。
            この関数は、視点位置のx、y、zの値、注視点のx、y、zの値、上方向ベクトルのx、y、zの値を引数にとります。
            視点位置を指定するときには、OpenGLのカレントの行列は、モデルビュー変換行列にしておく必要があります。
            glMatrixModeにGL_MODELVIEWを指定して呼び出します。

            なお、gluPerspective、gluLookAtを使用するときは、GL/glu.hをインクルードし、glu32.libをリンクする必要があります。

            これらの処理は、GLUTのglutReshapeFuncに設定するコールバック関数の中で行います。
            このコールバック関数はウィンドウのサイズが変更されたときに呼び出されます。
            この関数は、int型の引数として、ウィンドウの幅と高さを受け取ります。
            ここでは、この関数をreshapeとします。

            このコールバック関数のサンプルは以下のようになります。



            void reshape(int width, int height)
            {
            glViewport(0, 0, width, height);

            glMatrixMode(GL_PROJECTION);
            glLoadIdentity();
            gluPerspective(45.0, (double)width / (double)height, 0.1, 100.0);

            glMatrixMode(GL_MODELVIEW);
            glLoadIdentity();
            gluLookAt(0.5, 1.5, 2.5, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
            }



            ●立方体の描画

            次に、頂点を指定して、3次元の立方体を描画します。

            まず、最初に、描画処理を行う前に、陰面消去と背面消去を有効にします。
            陰面消去は、手前にあるもののあとに奥のものを描画するとき、手前のものに隠れた部分は描画しないようにする処理で、OpenGLではZバッファ法による陰面消去を行います。
            背面消去は、面の表と裏を決め、裏の面は描画しないで、描画処理を省力化するものです。
            OpenGLは、デフォルトでは反時計回りの面を表としています。
            陰面消去を有効化するには、OpenGLのglEnableという関数にGL_DEPTH_TESTを指定して呼び出します。また、glutInitDisplayModeの引数にGLUT_DEPTHをOR演算で追加します。
            背面消去を有効化するには、OpenGLのglEnableという関数にGL_CULL_FACEを指定して呼び出します。

            次に、カラーバッファと陰面消去で使うZバッファをクリアします。
            これらは、OpenGLのglClearにGL_COLOR_BUFFER_BITとGL_DEPTH_BUFFER_BITをOR演算して指定します。
            カラーバッファをクリアする色は、OpenGLのglClearColorで指定します。

            いよいよ頂点を指定しての描画処理ですが、これは、2次元の三角形を描画したときと、基本的には変わりません。

            まず、一つ一つの面をglBeginとglEndで囲んで描画します。
            立方体は6つの面があるので、この呼び出しが6回になります。

            それぞれの面の描画では、面の頂点の座標をOpenGLのglVertex3fvで指定していきます。
            この関数には、3次元の頂点座標をGLfloat(float)型の配列で指定します。
            引数に渡す配列は3つの要素を持ツ配列で、最初の要素がx座標、2つ目がy座標、3つ目がz座標です。
            今回は、立方体の8つの頂点を2次元配列としてあらかじめ定義しておき、その要素をglVertex3fvに渡して描画します。
            そして、それぞれの面は四角形となるため、頂点は4回ずつ指定していきます。
            なお、glVertex3fvではなくglVertex3fを使用すれば、頂点の座標を配列ではなく、x、y、zの3つの引数で指定できます。

            面の色は、三角形のときと同様に、OpenGLのglColor3fで指定します。
            glColor3fを呼び出した後に指定した頂点の色は、次のglColor3fが呼び出されるまで同じ色になります。
            また、別々の色に指定された頂点の間の面はそれぞれの頂点の色を補完して決められます。

            6つのglBeginとglEndの組を呼び出して立方体の面を描画した後は、glFlushを呼び出して、バッファのOpenGLコマンドを強制的に送り出して描画します。

            これらの処理は、2次元で三角形を描画したときと同じように、ウィンドウが表示されら時に呼び出されるコールバック関数の中で行います。
            ここでは、このコールバック関数をdisplayとします。

            この関数のサンプルは、以下のようになります。



            void display()
            {
            static GLfloat vertices [8][3] =
            {
            {-0.5f, -0.5f, 0.5f},
            { 0.5f, -0.5f, 0.5f},
            { 0.5f, 0.5f, 0.5f},
            {-0.5f, 0.5f, 0.5f},
            { 0.5f, -0.5f, -0.5f},
            {-0.5f, -0.5f, -0.5f},
            {-0.5f, 0.5f, -0.5f},
            { 0.5f, 0.5f, -0.5f}
            };

            glEnable(GL_DEPTH_TEST);
            glEnable(GL_CULL_FACE);

            glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
            glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

            /* 前 */
            glBegin(GL_POLYGON);
            glColor3f(1.0f, 1.0f, 1.0f);
            glVertex3fv(vertices[0]);
            glVertex3fv(vertices[1]);
            glColor3f(1.0f, 0.0f, 0.0f);
            glVertex3fv(vertices[2]);
            glVertex3fv(vertices[3]);
            glEnd();

            /* 後 */
            glBegin(GL_POLYGON);
            glColor3f(1.0f, 1.0f, 1.0f);
            glVertex3fv(vertices[4]);
            glVertex3fv(vertices[5]);
            glColor3f(1.0f, 0.0f, 0.0f);
            glVertex3fv(vertices[6]);
            glVertex3fv(vertices[7]);
            glEnd();

            /* 右 */
            glBegin(GL_POLYGON);
            glColor3f(1.0f, 1.0f, 1.0f);
            glVertex3fv(vertices[1]);
            glVertex3fv(vertices[4]);
            glColor3f(1.0f, 0.0f, 0.0f);
            glVertex3fv(vertices[7]);
            glVertex3fv(vertices[2]);
            glEnd();

            /* 左 */
            glBegin(GL_POLYGON);
            glColor3f(1.0f, 1.0f, 1.0f);
            glVertex3fv(vertices[5]);
            glVertex3fv(vertices[0]);
            glColor3f(1.0f, 0.0f, 0.0f);
            glVertex3fv(vertices[3]);
            glVertex3fv(vertices[6]);
            glEnd();

            /* 上 */
            glBegin(GL_POLYGON);
            glColor3f(1.0f, 1.0f, 1.0f);
            glVertex3fv(vertices[3]);
            glVertex3fv(vertices[2]);
            glVertex3fv(vertices[7]);
            glVertex3fv(vertices[6]);
            glEnd();

            /* 下 */
            glBegin(GL_POLYGON);
            glColor3f(1.0f, 0.0f, 0.0f);
            glVertex3fv(vertices[1]);
            glVertex3fv(vertices[0]);
            glVertex3fv(vertices[5]);
            glVertex3fv(vertices[4]);
            glEnd();

            glFlush();
            }



            ●main関数

            最後にmain関数ですが、今までのものに、最初に出てきた、ウィンドウのサイズが変更されたときに呼び出されるコールバック関数の設定が加わります。
            このコールバック関数reshapeを、GLUTのglutReshapeFuncで設定します。

            main関数のサンプルは以下のようになります。



            int main(int argc, char* argv[])
            {
            glutInit(&argc, argv);
            glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA | GLUT_DEPTH);
            glutInitWindowPosition(100, 100);
            glutInitWindowSize(640, 480);
            glutCreateWindow("3D Cube");
            glutReshapeFunc(reshape);
            glutDisplayFunc(display);
            glutMainLoop();

            return 0;
            }



            このプログラムの実行結果は以下のようになります。


            GLUTとOpenGLで3次元の立方体を表示するプログラム

            ソース(マウス右クリック、対象ファイルに保存でダウンロードして下さい)


            <関連記事>

            GLUTとOpenGLで画面をクリアするプログラム
            GLUTとOpenGLで三角形を描画するプログラム
            GLUTとOpenGLでライティングされた立方体を描画するプログラム
            GLUTとOpenGLでティーポットを描画するプログラム
            GLUTとOpenGLで立方体を回転させて表示するプログラム
            法線ベクトルを計算する方法
            Win32とOpenGLで画面をクリアするプログラム

            GLUTとOpenGLでライティングされた立方体を描画するプログラム

            0
              前回、GLUTOpenGLで、立方体を描画したときは、色は適当につけました。
              そのため、現実感のない立方体が描画されたと思います。
              現実感のある立体図形を描画するには、ライトの光によりライティングさせて描画する必要があります。

              そこで、今回は、GLUTOpenGLで、ライティングされた、材質感のある立方体を描画してみたいと思います。

              OpenGLでライティングされた3次元の立方体を描画する手順は、次のように3つの処理に分かれます。

              1つは、光源、つまりライトをシーンの中で設定する処理です。
              もう1つは、立方体の材質、つまりマテリアルを設定する処理です。
              最後は、立方体を法線ベクトルを指定しながら描画します。


              ●光源(ライト)を設定する

              光源を設定するには、まず、OpenGLでの光源の使用を有効にする必要があります。
              そして、光源の位置と色を設定しなければなりません。

              OpenGLで光源を有効にするには、OpenGLの関数、glEnableにGL_LIGHTINGを指定して呼び出します。
              そして、0番目の光源を有効にするには、同じglEnableにGL_LIGHT0を指定して呼び出します。

              次に、光源の位置を指定します。

              OpenGLで光源の位置を指定するには、glLightfvという関数を使用します。

              この関数は、引数を3つとります。

              最初の引数には、光源の番号を表す定数を指定します。
              0番目の光源なら、先ほどglEnableで有効にするときに使用した、GL_LIGHT0になります。

              2つ目の引数には、パラメータの種類を表す定数を指定します。
              光源の位置を指定するときは、GL_POSITIONとなります。

              3つ目の引数は、実際に値を設定するためのパラメータとなります。
              光源の位置を指定するときは、光源の位置(x, y, z, w)の4つの要素を持つ配列を指定します。
              ここで、このベクトルは同次座標系のベクトルで、w=1のときは位置(x, y, z)の点光源、w=0のときはベクトル(x, y, z)方向の無限遠からの平行光源となります。

              光源の位置を指定したら、次は、光源の色を指定します。
              光源の色には、環境光、拡散光、鏡面光の色があります。
              これらについては、後で材質のところで説明します。

              OpenGLで光源の色を指定するときは、位置を指定したときと同じglLightfvを使用します。

              最初の引数は、光源の位置を指定したときと同じです。

              2つ目の引数は、環境光の色を指定するときはGL_AMBIENTを、拡散光の色を指定するときはGL_DIFFUSEを、鏡面光の色を指定するときはGL_SPECULARを指定します。

              3つ目の引数には、これらの光の色の赤(R)、緑(G)、青(B)の成分を要素に持つ配列を指定します。
              基本的には、R、G、Bのそれぞれの色の成分の値は、0.0から1.0の間で表します。

              今回は、これら、光源の有効化、光源の位置の指定、光源の色の指定を、視点の位置の指定をするのと同じ、ウィンドウのサイズが変更されたときに、GLUTで呼び出されるコールバック関数の中で行うことにします。

              なお、光源の位置の指定は、一般に、視点位置を指定した後に行います。
              これは、OpenGLで視点位置を設定することは、ワールド座標系をカメラ座標系に変換することに相当するからです。
              光源の位置は、ワールド座標系の中で指定します。
              また、OpenGLの座標変換の順序は、関数の呼び出し順の逆になります。

              この関数のサンプルコードは次のようになります。



              void reshape(int width, int height)
              {
              static GLfloat lightPosition[4] = {0.25f, 1.0f, 0.25f, 0.0f};
              static GLfloat lightDiffuse[3] = {1.0f, 1.0f, 1.0f};
              static GLfloat lightAmbient[3] = {0.25f, 0.25f, 0.25f};
              static GLfloat lightSpecular[3] = {1.0f, 1.0f, 1.0f};

              glEnable(GL_LIGHTING);
              glEnable(GL_LIGHT0);

              glShadeModel(GL_SMOOTH);

              glViewport(0, 0, width, height);

              glMatrixMode(GL_PROJECTION);
              glLoadIdentity();
              gluPerspective(45.0, (double)width / (double)height, 0.1, 100.0);

              glMatrixMode(GL_MODELVIEW);
              glLoadIdentity();
              gluLookAt(0.5, 1.5, 2.5, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);

              glLightfv(GL_LIGHT0, GL_POSITION, lightPosition);
              glLightfv(GL_LIGHT0, GL_DIFFUSE, lightDiffuse);
              glLightfv(GL_LIGHT0, GL_AMBIENT, lightAmbient);
              glLightfv(GL_LIGHT0, GL_SPECULAR, lightSpecular);
              }



              ●立方体の材質(マテリアル)を設定する

              光源を有効にして、光源の位置と色を指定したら、次は、これから描画する立方体の材質(マテリアル)の色(反射光の色)を設定します。

              材質の色には、環境光、拡散光、鏡面光があります。

              環境光は、物体の暗い部分にも薄っすらとかかっている光の色です。

              拡散光は、物体の表面にあたって、四方へ広がる光の色で、物体の色を最もよく表します。
              拡散光の強さは、物体の表面の法線ベクトルと反射方向のなす角の余弦(cos)に比例します。
              法線ベクトルとは、面に垂直な方向の単位ベクトル(長さ1のベクトル)です。

              鏡面光は、物体の表面で、特定の方向に強く反射するハイライトの色です。
              物体の表面に対して、光の入射角と反対の方向が最も強くなります。
              この光は、ほとんどこの方向に反射します。

              OpenGLで、これらの拡散光や鏡面光を計算する式は決まっていますが、これらの式についてはOpenGLの書籍などを参考にしてください。

              OpenGLで材質の色を指定するには、glMaterialfvという関数を使います。

              この関数は、引数を3つとります。

              1つ目の引数は、材質を設定するのが、面の表か裏か、それとも両方かを表す定数となります。
              表面の材質を設定するときは、GL_FRONTを指定します。

              2つ目の引数には、反射光の種類を表す定数を指定します。
              環境光のときはGL_AMBIENTを、拡散光のときはGL_DIFFUSEを、鏡面光のときはGL_SPECULARを指定します。
              また、鏡面光の強さを表すときは、GL_SHININESSを指定します。

              3つ目の引数には、反射光の色を表すパラメータとして、色の赤(R)、緑(G)、青(B)の成分を要素に持つ配列を指定します。
              R、G、Bのそれぞれの色の成分の値は、0.0から1.0の間で表します。
              第2引数にGL_SHININESSを指定したときは、光の強さを表す数値を0から128の間で指定します。
              この値が大きいほど同じ方向にまとまって鏡面光が反射します。
              つまり、この値が大きいほど鋭い鏡面光となります。


              ●立方体を法線ベクトルを指定しながら描画する

              材質の設定が終わったら、最後に、立方体の頂点を指定して描画します。

              この処理は、前回とほとんど同じですが、今回は、面の色は指定しません。

              ライティングされた立方体を描画するときは、面の色を指定する代わりに、面の法線ベクトルを指定します。
              これは、材質の色の計算に面の法線ベクトルを使うからです。
              逆に、材質と光の色を指定すると、OpenGLがライティングされた面の色を計算するため、面の色を直接指定する必要はありません。

              ここで、どうやって法線ベクトルを計算するかが問題になりますが、立方体のような簡単な図形の場合は、面の法線ベクトルはすぐに分かるので、その値を配列に入れておいて使います。
              法線ベクトルの計算方法は、また、別のところで説明する予定です。

              これら、立方体の材質の指定と、立方体の描画の処理は、ウィンドウが表示されたときに、GLUTに呼び出されるコールバック関数の中で行います。

              そのサンプルコードは、以下のようになります。



              void display()
              {
              static GLfloat vertices [8][3] =
              {
              {-0.5f, -0.5f, 0.5f},
              { 0.5f, -0.5f, 0.5f},
              { 0.5f, 0.5f, 0.5f},
              {-0.5f, 0.5f, 0.5f},
              { 0.5f, -0.5f, -0.5f},
              {-0.5f, -0.5f, -0.5f},
              {-0.5f, 0.5f, -0.5f},
              { 0.5f, 0.5f, -0.5f}
              };

              static GLfloat normals[6][3] =
              {
              { 0.0f, 0.0f, 1.0f},
              { 0.0f, 0.0f, -1.0f},
              { 1.0f, 0.0f, 0.0f},
              {-1.0f, 0.0f, 0.0f},
              { 0.0f, 1.0f, 0.0f},
              { 0.0f, -1.0f, 0.0f}
              };

              static GLfloat diffuse[3] = {1.0f, 0.0f, 0.0f};
              static GLfloat ambient[3] = {0.25f, 0.25f, 0.25f};
              static GLfloat specular[3] = {1.0f, 1.0f, 1.0f};
              static GLfloat shininess[1] = {32.0f};

              glMaterialfv(GL_FRONT, GL_DIFFUSE, diffuse);
              glMaterialfv(GL_FRONT, GL_AMBIENT, ambient);
              glMaterialfv(GL_FRONT, GL_SPECULAR, specular);
              glMaterialfv(GL_FRONT, GL_SHININESS, shininess);

              glEnable(GL_DEPTH_TEST);
              glEnable(GL_CULL_FACE);

              glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
              glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

              /* 前 */
              glBegin(GL_POLYGON);
              glNormal3fv(normals[0]);
              glVertex3fv(vertices[0]);
              glVertex3fv(vertices[1]);
              glVertex3fv(vertices[2]);
              glVertex3fv(vertices[3]);
              glEnd();

              /* 後 */
              glBegin(GL_POLYGON);
              glNormal3fv(normals[1]);
              glVertex3fv(vertices[4]);
              glVertex3fv(vertices[5]);
              glVertex3fv(vertices[6]);
              glVertex3fv(vertices[7]);
              glEnd();

              /* 右 */
              glBegin(GL_POLYGON);
              glNormal3fv(normals[2]);
              glVertex3fv(vertices[1]);
              glVertex3fv(vertices[4]);
              glVertex3fv(vertices[7]);
              glVertex3fv(vertices[2]);
              glEnd();

              /* 左 */
              glBegin(GL_POLYGON);
              glNormal3fv(normals[3]);
              glVertex3fv(vertices[5]);
              glVertex3fv(vertices[0]);
              glVertex3fv(vertices[3]);
              glVertex3fv(vertices[6]);
              glEnd();

              /* 上 */
              glBegin(GL_POLYGON);
              glNormal3fv(normals[4]);
              glVertex3fv(vertices[3]);
              glVertex3fv(vertices[2]);
              glVertex3fv(vertices[7]);
              glVertex3fv(vertices[6]);
              glEnd();

              /* 下 */
              glBegin(GL_POLYGON);
              glNormal3fv(normals[5]);
              glVertex3fv(vertices[1]);
              glVertex3fv(vertices[0]);
              glVertex3fv(vertices[5]);
              glVertex3fv(vertices[4]);
              glEnd();

              glFlush();
              }



              ●main関数

              main関数内の処理は今までと同じで、以下のようになります。



              int main(int argc, char* argv[])
              {
              glutInit(&argc, argv);
              glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA | GLUT_DEPTH);
              glutInitWindowPosition(100, 100);
              glutInitWindowSize(640, 480);
              glutCreateWindow("Lighting Cube");
              glutReshapeFunc(reshape);
              glutDisplayFunc(display);
              glutMainLoop();

              return 0;
              }



              このプログラムの実行結果は以下のようになります。


              GLUTとOpenGLでライティングされた立方体を描画するプログラム

              ソース(マウス右クリック、対象ファイルに保存でダウンロードして下さい)


              <関連記事>

              GLUTとOpenGLで画面をクリアするプログラム
              GLUTとOpenGLで三角形を描画するプログラム
              GLUTとOpenGLで立方体を描画するプログラム
              GLUTとOpenGLでティーポットを描画するプログラム
              GLUTとOpenGLで立方体を回転させて表示するプログラム
              法線ベクトルを計算する方法
              Win32とOpenGLで画面をクリアするプログラム

              GLUTとOpenGLでティーポットを描画するプログラム

              0
                今日は、ちょっと一休みで、GLUTOpenGLでティーポットを描画するプログラムを作ってみたいと思います。

                実は、GLUTには、OpenGLを使って様々な立体を簡単に描画できる関数が用意されています。
                その中には、なんとティーポットを描画してくれるものもあります。

                GLUTでティーポットを描画するには、glutSolidTeapotという関数を使います。
                この関数は引数を1つだけとります。
                この引数にはティーポットの大きさを指定します。

                このglutSolidTeapotは法線ベクトルの計算も自動的に行ってくれます。
                OpenGLのglBeginとglEndを繰り返して描画する処理が、すべてこのGLUTの関数1つでできてしまいます。
                もし、OpenGLだけでティーポットを描画しようとしたら、ティーポットのモデルデータを探してきて、そのデータをロードして、法線ベクトルの計算などもしながら描画しなければならなくなります。

                GLUTOpenGLでティーポットを描画するプログラムは、前回のライティングされた立方体を描画したプログラムを少しだけ修正して作ることができます。

                視点位置の指定や光源(ライト)の指定、材質(マテリアル)の指定は、前回と同じにしておきます。
                そして、OpenGLのglBeginとglEndの間で法線ベクトルと頂点を指定して立方体を描画していた部分を、GLUTのglutSolidTeapotに置き換えます。
                これで、赤いティーポットを描画するプログラムが出来上がります。

                このGLUTOpenGLでティーポットを描画するプログラムのサンプルコードは次のようになります。



                #include "stdafx.h"
                #include  <GL/glut.h>


                void reshape(int width, int height)
                {
                static GLfloat lightPosition[4] = {0.25f, 1.0f, 0.25f, 0.0f};
                static GLfloat lightDiffuse[3] = {1.0f, 1.0f, 1.0f};
                static GLfloat lightAmbient[3] = {0.25f, 0.25f, 0.25f};
                static GLfloat lightSpecular[3] = {1.0f, 1.0f, 1.0f};

                glEnable(GL_LIGHTING);
                glEnable(GL_LIGHT0);

                glShadeModel(GL_SMOOTH);

                glViewport(0, 0, width, height);

                glMatrixMode(GL_PROJECTION);
                glLoadIdentity();
                gluPerspective(45.0, (double)width / (double)height, 0.1, 100.0);

                glMatrixMode(GL_MODELVIEW);
                glLoadIdentity();
                gluLookAt(0.5, 1.5, 2.5, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);

                glLightfv(GL_LIGHT0, GL_POSITION, lightPosition);
                glLightfv(GL_LIGHT0, GL_DIFFUSE, lightDiffuse);
                glLightfv(GL_LIGHT0, GL_AMBIENT, lightAmbient);
                glLightfv(GL_LIGHT0, GL_SPECULAR, lightSpecular);
                }


                void display()
                {
                static GLfloat diffuse[3] = {1.0f, 0.0f, 0.0f};
                static GLfloat ambient[3] = {0.25f, 0.25f, 0.25f};
                static GLfloat specular[3] = {1.0f, 1.0f, 1.0f};
                static GLfloat shininess[1] = {32.0f};

                glMaterialfv(GL_FRONT, GL_DIFFUSE, diffuse);
                glMaterialfv(GL_FRONT, GL_AMBIENT, ambient);
                glMaterialfv(GL_FRONT, GL_SPECULAR, specular);
                glMaterialfv(GL_FRONT, GL_SHININESS, shininess);

                glEnable(GL_DEPTH_TEST);
                glEnable(GL_CULL_FACE);

                glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
                glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

                glutSolidTeapot(0.5);

                glutSwapBuffers();
                }


                int main(int argc, char* argv[])
                {
                glutInit(&argc, argv);
                glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA | GLUT_DEPTH);
                glutInitWindowPosition(100, 100);
                glutInitWindowSize(640, 480);
                glutCreateWindow("Solid Teapot");
                glutReshapeFunc(reshape);
                glutDisplayFunc(display);
                glutMainLoop();

                return 0;
                }



                このプログラムの実行結果は以下のようになります。


                GLUTとOpenGLでティーポットを描画するプログラム

                ソース(マウス右クリック、対象ファイルに保存でダウンロードして下さい)


                <関連記事>

                GLUTとOpenGLで画面をクリアするプログラム
                GLUTとOpenGLで三角形を描画するプログラム
                GLUTとOpenGLで立方体を描画するプログラム
                GLUTとOpenGLでライティングされた立方体を描画するプログラム
                GLUTとOpenGLで立方体を回転させて表示するプログラム
                法線ベクトルを計算する方法
                Win32とOpenGLで画面をクリアするプログラム



                GLUTとOpenGLで立方体を回転させて表示するプログラム

                0
                  今回は、GLUTOpenGLを使って、ライティングされた立方体を回転させて表示するプログラムを作成してみたいと思います。

                  前回までは、”動き”のないプログラムを作ってきました。しかし、ほとんどのリアルタイム3次元CGを使ったプログラムには”動き”がつきものです。
                  3次元の物体に様々な”動き”をつけて、アニメーション表示することで、ゲームやシミュレータなどのソフトウェアは完成します。

                  今回は、その”動き”のあるアニメーションプログラムをGLUTOpenGLで作る最も初歩的なものとして、OpenGLで描画した立方体を、OpenGLの幾何変換の機能を使って回転させるプログラムを作ります。

                  GLUTOpenGLを使ったプログラムでアニメーション表示を行うには、GLUTのglutIdleFuncを使うのが便利です。
                  この関数に登録したコールバック関数は、GLUTのメインループでイベントが発生していないときに、何度も呼び出されます。

                  glutIdleFuncに登録するコールバック関数の中は、ライティングされた立方体を、毎回少にずつ回転させながら描画します。
                  そのためには、コールバック関数の中に、回転角を保持する変数をstaticで宣言しておく必要があります。
                  staticで宣言することで、前回この関数が呼び出されたときに保持された値を参照することができます。
                  そして、OpenGLの回転の幾何変換を設定する関数を使って、回転された立方体を描画します。

                  OpenGLで回転の幾何変換を設定するには、glRotatefという関数を使います。
                  この関数は引数を4つとります。最初の引数には回転角を度(degree)で指定し、残りの3つの引数には回転軸ベクトルのx、y、z座標を指定します。
                  今回は、y軸方向のベクトルを回転軸とします。

                  このglRotatefはOpenGLで立方体を描画する前に呼び出します。
                  また、この関数の呼び出し前に、glPushMatrixでOpenGLのモデルビュー変換行列の行列スタックをプッシュし、描画が終わった後に、glPopMatrixで行列スタックをポップします。
                  これは、このコールバック関数が呼び出されるたびに、新しい回転変換行列を計算し、視点変換のモデルビュー変換行列と合成するためです。

                  これらの行列スタックや幾何変換については、OpenGLの解説書に詳しく解説されていますので、そちらを参考にしてみてください。

                  なお、このコールバック関数の中でWin32のSleepという関数を使って、プロセスの処理を少し休止しています。
                  これは、プロセスを少し休止させないとCPUの負荷が高くなるためです。この関数を使うにはwindows.hをインクルードする必要があります。
                  この方法には、他にもいろいろ改善の余地があるかもしれません。
                  1つは、glutIdleFuncを使う変わりに、GLUTのglutTimerFuncを使って、タイマーを使った処理をすることも考えられます。
                  このときは、タイマーのコールバック関数の最後で、もう一度、このコールバック関数を登録するのがコツです。

                  また、今回は、アニメーション表示をさせるので、画面のちらつきを抑えるために、ダブルバッファを使用します。これには、glutInitDisplayModeの引数で、GLUT_SINGLEとなっていたところを、GLUT_DOUBLEに変更します。また、glFlushの代わりに、glutSwapBuffersを使用して、バッファをスワップして、バックバッファに描画した内容をフロントバッファに表示させます。

                  以上が、glutIdleFuncに登録するコールバック関数の処理内容です。

                  他のコールバック関数はライティングされた立方体を描画したときと同じように残しておきます。

                  以上をまとめると、このサンプルプログラムは以下のようになります。



                  #include "stdafx.h"
                  #include  <windows.h>
                  #include  <GL/glut.h>


                  void reshape(int width, int height)
                  {
                  static GLfloat lightPosition[4] = {0.25f, 1.0f, 0.25f, 0.0f};
                  static GLfloat lightDiffuse[3] = {1.0f, 1.0f, 1.0f};
                  static GLfloat lightAmbient[3] = {0.25f, 0.25f, 0.25f};
                  static GLfloat lightSpecular[3] = {1.0f, 1.0f, 1.0f};

                  glEnable(GL_LIGHTING);
                  glEnable(GL_LIGHT0);

                  glShadeModel(GL_SMOOTH);

                  glViewport(0, 0, width, height);

                  glMatrixMode(GL_PROJECTION);
                  glLoadIdentity();
                  gluPerspective(45.0, (double)width / (double)height, 0.1, 100.0);

                  glMatrixMode(GL_MODELVIEW);
                  glLoadIdentity();
                  gluLookAt(0.5, 1.5, 2.5, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);

                  glLightfv(GL_LIGHT0, GL_POSITION, lightPosition);
                  glLightfv(GL_LIGHT0, GL_DIFFUSE, lightDiffuse);
                  glLightfv(GL_LIGHT0, GL_AMBIENT, lightAmbient);
                  glLightfv(GL_LIGHT0, GL_SPECULAR, lightSpecular);
                  }


                  void display()
                  {
                  static GLfloat vertices [8][3] =
                  {
                  {-0.5f, -0.5f, 0.5f},
                  { 0.5f, -0.5f, 0.5f},
                  { 0.5f, 0.5f, 0.5f},
                  {-0.5f, 0.5f, 0.5f},
                  { 0.5f, -0.5f, -0.5f},
                  {-0.5f, -0.5f, -0.5f},
                  {-0.5f, 0.5f, -0.5f},
                  { 0.5f, 0.5f, -0.5f}
                  };

                  static GLfloat normals[6][3] =
                  {
                  { 0.0f, 0.0f, 1.0f},
                  { 0.0f, 0.0f, -1.0f},
                  { 1.0f, 0.0f, 0.0f},
                  {-1.0f, 0.0f, 0.0f},
                  { 0.0f, 1.0f, 0.0f},
                  { 0.0f, -1.0f, 0.0f}
                  };

                  static GLfloat diffuse[3] = {1.0f, 0.0f, 0.0f};
                  static GLfloat ambient[3] = {0.25f, 0.25f, 0.25f};
                  static GLfloat specular[3] = {1.0f, 1.0f, 1.0f};
                  static GLfloat shininess[1] = {32.0f};

                  glMaterialfv(GL_FRONT, GL_DIFFUSE, diffuse);
                  glMaterialfv(GL_FRONT, GL_AMBIENT, ambient);
                  glMaterialfv(GL_FRONT, GL_SPECULAR, specular);
                  glMaterialfv(GL_FRONT, GL_SHININESS, shininess);

                  glEnable(GL_DEPTH_TEST);
                  glEnable(GL_CULL_FACE);

                  glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
                  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

                  /* 前 */
                  glBegin(GL_POLYGON);
                  glNormal3fv(normals[0]);
                  glVertex3fv(vertices[0]);
                  glVertex3fv(vertices[1]);
                  glVertex3fv(vertices[2]);
                  glVertex3fv(vertices[3]);
                  glEnd();

                  /* 後 */
                  glBegin(GL_POLYGON);
                  glNormal3fv(normals[1]);
                  glVertex3fv(vertices[4]);
                  glVertex3fv(vertices[5]);
                  glVertex3fv(vertices[6]);
                  glVertex3fv(vertices[7]);
                  glEnd();

                  /* 右 */
                  glBegin(GL_POLYGON);
                  glNormal3fv(normals[2]);
                  glVertex3fv(vertices[1]);
                  glVertex3fv(vertices[4]);
                  glVertex3fv(vertices[7]);
                  glVertex3fv(vertices[2]);
                  glEnd();

                  /* 左 */
                  glBegin(GL_POLYGON);
                  glNormal3fv(normals[3]);
                  glVertex3fv(vertices[5]);
                  glVertex3fv(vertices[0]);
                  glVertex3fv(vertices[3]);
                  glVertex3fv(vertices[6]);
                  glEnd();

                  /* 上 */
                  glBegin(GL_POLYGON);
                  glNormal3fv(normals[4]);
                  glVertex3fv(vertices[3]);
                  glVertex3fv(vertices[2]);
                  glVertex3fv(vertices[7]);
                  glVertex3fv(vertices[6]);
                  glEnd();

                  /* 下 */
                  glBegin(GL_POLYGON);
                  glNormal3fv(normals[5]);
                  glVertex3fv(vertices[1]);
                  glVertex3fv(vertices[0]);
                  glVertex3fv(vertices[5]);
                  glVertex3fv(vertices[4]);
                  glEnd();

                  glutSwapBuffers();
                  }


                  void idle()
                  {
                  static GLfloat vertices [8][3] =
                  {
                  {-0.5f, -0.5f, 0.5f},
                  { 0.5f, -0.5f, 0.5f},
                  { 0.5f, 0.5f, 0.5f},
                  {-0.5f, 0.5f, 0.5f},
                  { 0.5f, -0.5f, -0.5f},
                  {-0.5f, -0.5f, -0.5f},
                  {-0.5f, 0.5f, -0.5f},
                  { 0.5f, 0.5f, -0.5f}
                  };

                  static GLfloat normals[6][3] =
                  {
                  { 0.0f, 0.0f, 1.0f},
                  { 0.0f, 0.0f, -1.0f},
                  { 1.0f, 0.0f, 0.0f},
                  {-1.0f, 0.0f, 0.0f},
                  { 0.0f, 1.0f, 0.0f},
                  { 0.0f, -1.0f, 0.0f}
                  };

                  static GLfloat diffuse[3] = {1.0f, 0.0f, 0.0f};
                  static GLfloat ambient[3] = {0.25f, 0.25f, 0.25f};
                  static GLfloat specular[3] = {1.0f, 1.0f, 1.0f};
                  static GLfloat shininess[1] = {32.0f};

                  static float angle = 0.0f;

                  glMaterialfv(GL_FRONT, GL_DIFFUSE, diffuse);
                  glMaterialfv(GL_FRONT, GL_AMBIENT, ambient);
                  glMaterialfv(GL_FRONT, GL_SPECULAR, specular);
                  glMaterialfv(GL_FRONT, GL_SHININESS, shininess);

                  glEnable(GL_DEPTH_TEST);
                  glEnable(GL_CULL_FACE);

                  glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
                  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

                  angle += 2.5f;
                  if (angle > 360.0f) {
                  angle -= 360.0f;
                  }

                  glPushMatrix();
                  glRotatef(angle, 0.0f, 1.0f, 0.0f);

                  /* 前 */
                  glBegin(GL_POLYGON);
                  glNormal3fv(normals[0]);
                  glVertex3fv(vertices[0]);
                  glVertex3fv(vertices[1]);
                  glVertex3fv(vertices[2]);
                  glVertex3fv(vertices[3]);
                  glEnd();

                  /* 後 */
                  glBegin(GL_POLYGON);
                  glNormal3fv(normals[1]);
                  glVertex3fv(vertices[4]);
                  glVertex3fv(vertices[5]);
                  glVertex3fv(vertices[6]);
                  glVertex3fv(vertices[7]);
                  glEnd();

                  /* 右 */
                  glBegin(GL_POLYGON);
                  glNormal3fv(normals[2]);
                  glVertex3fv(vertices[1]);
                  glVertex3fv(vertices[4]);
                  glVertex3fv(vertices[7]);
                  glVertex3fv(vertices[2]);
                  glEnd();

                  /* 左 */
                  glBegin(GL_POLYGON);
                  glNormal3fv(normals[3]);
                  glVertex3fv(vertices[5]);
                  glVertex3fv(vertices[0]);
                  glVertex3fv(vertices[3]);
                  glVertex3fv(vertices[6]);
                  glEnd();

                  /* 上 */
                  glBegin(GL_POLYGON);
                  glNormal3fv(normals[4]);
                  glVertex3fv(vertices[3]);
                  glVertex3fv(vertices[2]);
                  glVertex3fv(vertices[7]);
                  glVertex3fv(vertices[6]);
                  glEnd();

                  /* 下 */
                  glBegin(GL_POLYGON);
                  glNormal3fv(normals[5]);
                  glVertex3fv(vertices[1]);
                  glVertex3fv(vertices[0]);
                  glVertex3fv(vertices[5]);
                  glVertex3fv(vertices[4]);
                  glEnd();

                  glPopMatrix();

                  glutSwapBuffers();

                  Sleep(10);
                  }


                  int main(int argc, char* argv[])
                  {
                  glutInit(&argc, argv);
                  glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH);
                  glutInitWindowPosition(100, 100);
                  glutInitWindowSize(640, 480);
                  glutCreateWindow("Animation Cube");
                  glutReshapeFunc(reshape);
                  glutIdleFunc(idle);
                  glutDisplayFunc(display);
                  glutMainLoop();

                  return 0;
                  }



                  このプログラムの実行結果は以下のようになります。


                  GLUTとOpenGLで立方体を回転させて表示するプログラム1

                  GLUTとOpenGLで立方体を回転させて表示するプログラム2



                  ソース(マウス右クリック、対象ファイルに保存でダウンロードして下さい)



                  <関連記事>

                  GLUTとOpenGLで画面をクリアするプログラム
                  GLUTとOpenGLで三角形を描画するプログラム
                  GLUTとOpenGLで立方体を描画するプログラム
                  GLUTとOpenGLでライティングされた立方体を描画するプログラム
                  GLUTとOpenGLでティーポットを描画するプログラム
                  法線ベクトルを計算する方法
                  Win32とOpenGLで画面をクリアするプログラム



                  法線ベクトルを計算する方法

                  0
                    今日は、GLUTOpenGLの話題から少し離れて、C言語で法線ベクトルを計算する方法を説明します。

                    法線ベクトルとは、簡単にいうと、面に垂直な単位ベクトルです。単位ベクトルとは、長さが1のベクトルのことです。

                    3次元CGのプログラムを作っていると、どうしても、面の法線ベクトルを計算する必要がある場合が出てきます。

                    なぜなら、光源(ライト)と面の材質(マテリアル)を決めて、照光された面の色を計算するときに、面の法線ベクトルが必要になるからです。
                    OpenGLを例に、具体的にいうと、頂点を指定して描画するときに、glNormal3fvなどで面の法線ベクトルを指定する必要があります。

                    数学が苦手という方も多いと思いますが、面の法線ベクトルを求めるのは、それほど難しくありません。

                    手順さえ覚えてしまえば、法線ベクトルを求めるプログラムを書くのは、それほど難しくはありません。

                    法線ベクトルnを求める手順は以下のとおりです。
                    ここで、面は、p1,p2,p3...という頂点で、この順で定義されるとします。

                    1)p2→p1という向きのベクトルv1(= p1 - p2)を求めます。
                    2)同様に、p2→p3という向きのベクトルv2(= p3 - p2)を求めます。
                    3)v2からv1への外積v2×v1(= cross)を求めます。
                    4)外積crossの長さlengthを求めます。
                    5)外積crossをその長さlengthで割った結果が法線ベクトルnとなります。

                    C言語の関数にすると、以下のようになります。

                    この関数は、3つの頂点p1、p2、p3を入力とし、求めた法線ベクトルnを出力とします。

                    なお、長さlengthが0のときは、法線ベクトルnは求められません。
                    法線ベクトルnを求めるためには、3つの点p1、p2、p3が一直線上にないことが前提です。

                    この関数では、法線ベクトルが求められたときは1(true)を、求められないときは0(false)を返すことにします。



                    int CalculateNormal(
                    const float p1[3],
                    const float p2[3],
                    const float p3[3],
                    float n[3])
                    {
                    float v1[3];
                    float v2[3];
                    float cross[3];
                    float length;
                    int i;

                    /* v1 = p1 - p2を求める */
                    for (i = 0; i < 3; i++) {
                    v1[i] = p1[i] - p2[i];
                    }

                    /* v2 = p3 - p2を求める */
                    for (i = 0; i < 3; i++) {
                    v2[i] = p3[i] - p2[i];
                    }

                    /* 外積v2×v1(= cross)を求める */
                    for (i = 0; i < 3; i++) {
                    cross[i] = v2[(i+1)%3] * v1[(i+2)%3] -
                    v2[(i+2)%3] * v1[(i+1)%3];
                    }

                    /* 外積v2×v1の長さ|v2×v1|(= length)を求める */
                    length = sqrtf(cross[0] * cross[0] +
                    cross[1] * cross[1] +
                    cross[2] * cross[2]);

                    /* 長さ|v2×v1|が0のときは法線ベクトルは求められない */
                    if (length == 0.0f) {
                    return 0;
                    }

                    /* 外積v2×v1を長さ|v2×v1|で割って法線ベクトルnを求める */
                    for (i = 0; i < 3; i++) {
                    n[i] = cross[i] / length;
                    }

                    return 1;
                    }



                    なお、今回は、C言語の関数として法線ベクトルを求める関数を作る方法を解説しました。C++を使うときは、さらに一歩進んで、ベクトルをクラスとして定義してしまうと、いろいろと便利です。

                    C++を使ったベクトルクラスや行列クラスの作り方と、サンプルコードは以下の書籍に詳しく解説されています。

                    MFCによるOpenGL3Dプログラミング―ライブラリを使って簡単に3D-CG図形を描画する! (I/O BOOKS)
                    (伊藤拡、工学社)

                    実例で学ぶゲーム3D数学
                    (Fletcher Dunn、Ian Parberry著、松田晃一訳、オライリージャパン)



                    <関連記事>

                    GLUTとOpenGLで画面をクリアするプログラム
                    GLUTとOpenGLで三角形を描画するプログラム
                    GLUTとOpenGLで立方体を描画するプログラム
                    GLUTとOpenGLでライティングされた立方体を描画するプログラム
                    GLUTとOpenGLでティーポットを描画するプログラム
                    GLUTとOpenGLで立方体を回転させて表示するプログラム
                    Win32とOpenGLで画面をクリアするプログラム

                    Win32とOpenGLで画面をクリアするプログラム

                    0
                      今回は、少し進んで、Win32 APIを使ったWindowsプログラムでOpenGLを使う方法を説明したいと思います。

                      ここでは、簡単にさわりの部分だけを説明します。
                      詳しくは、参考文献などを参考にされてください

                      Win32を使ったWindowsプログラムでOpenGLを使うときは、Windows固有のレンダリングコンテキストを生成してやる必要があります。
                      レンダリングコンテキストは、ちょうどWindowsプログラムでGDIを使った2D描画を行うときにデバイスコンテキストが必要なように、OpenGLを使って3D描画を行うときに必要となるものです。
                      具体的には、OpenGLの関数で設定するステート(状態)を保存しています。

                      GLUTでOpenGLを使ったプログラミングをするときは、GLUTがレンダリングコンテキストに関する処理を内部的に行っているため、レンダリングコンテキストに関して、プログラマーは何もする必要がありませんでした。
                      しかし、Win32を使って、OepnGLのプログラミングをするときは、自分でレンダリングコンテキストを生成し、管理してやる必要があります。

                      Win32でレンダリングコンテキストを生成するには、2つの手順をふむ必要があります。
                      まず最初に、PIXELFORMATの設定を行い、次にレンダリングコンテキストの生成を行います。


                      ●PIXELFORMATDESCRIPTORの設定

                      PIXELFORMATを設定するには、PIXELFORMATDESCRIPTOR構造体に適切な値を設定して、SetPixelFormatという関数を使います。
                      PIXELFORMATの設定では、レンダリングする際のフレームバッファの仕様の設定などを行います。
                      PIXELFORMATDESCRIPTOR構造体やSetPixelFormatなどの関数の仕様については、最後に紹介する書籍などを参考にしてください。


                      ●レンダリングコンテキストの生成・使用・破棄

                      PIXELFORMATの設定が終わったら、レンダリングコンテキストを生成します。
                      このレンダリングコンテキストは、OpenGLでコマンドを発行するときに必要になります。

                      レンダリングコンテキストを生成するには、wglCreateContextという関数を使用します。
                      この関数には、レンダリングコンテキストを関連付けるデバイスのデバイスコンテキストのハンドルを渡します。
                      ウィンドウにOpenGLの描画結果を表示したい場合は、出力するウィンドウのウィンドウハンドルから取得した、デバイスコンテキストのハンドルを渡します。
                      この関数は、戻り値として、生成したレンダリングコンテキストのハンドルを返します。

                      通常、PIXELFORMATの設定とレンダリングコンテキストの生成は、ウィンドウが生成されるときに行います。
                      つまり、WM_CREATEメッセージを受け取ったときに、これらの処理を行います。


                      レンダリングコンテキストを生成したら、OpenGLで描画するためのコマンドを発行する前に、レンダリングコンテキストをカレントにする必要があります。
                      レンダリングコンテキストをカレントにするには、、wglMakeCurrentという関数を使います。
                      この関数に、レンダリングコンテキストに関連付けたデバイスコンテキストと、生成しておいたレンダリングコンテキストのハンドルを渡します。

                      OpenGLのコマンドの発行が終わったら、レンダリングコンテキストをカレントではなくします。
                      レンダリングコンテキストをカレントではなくすには、この関数の引数の、レンダリングコンテキストの方、または、両方にNULLを渡します。


                      プログラムの最後に、生成したレンダリングコンテキストを破棄する必要があります。

                      レンダリングコンテキストを破棄するときは、wglDeleteContextという関数に、破棄したいレンダリングコンテキストを渡します。
                      通常、これは、最後にウィンドウが破棄されるタイミングで行います。
                      つまり、WM_DESTROYメッセージを受け取ったときに、この処理を行います。


                      ●OpenGLによる画面のクリア

                      Windows環境で、OpenGLを使って画面をクリアする方法は、GLUTを使ったときと同じに、glClearColorとglClearを使います。
                      OpenGL自体はウィンドウの設定などはサポートしていませんから、基本的にはプラットフォームに依存しません。
                      そのため、GLUTを使ったときと同じように、プログラムを書くことができます。

                      サンプルプログラムは以下のようになります。



                      #include  <windows.h>

                      #include  <GL/gl.h>

                      HGLRC g_hGLRC;

                      LRESULT CALLBACK WindowProc(HWND hWnd, UINT nMessage, WPARAM wParam, LPARAM lParam);

                      TCHAR szWindowName[] = TEXT("OpenGLClearWin32");

                      void OnCreate(HWND hWnd);
                      void OnPaint(HWND hWnd);
                      void OnDestroy(HWND hWnd);


                      int WINAPI
                      WinMain(HINSTANCE hCurrInstance, HINSTANCE hPrevInstance, LPSTR szArgs, int nWinMode)
                      {
                      HWND hWnd;
                      MSG msg;
                      WNDCLASSEX wc;

                      wc.cbSize = sizeof (WNDCLASSEX);
                      wc.hInstance = hCurrInstance;
                      wc.lpszClassName = szWindowName;
                      wc.lpfnWndProc = WindowProc;
                      wc.style = 0;
                      wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
                      wc.hIconSm = NULL;
                      wc.hCursor = LoadCursor(NULL, IDC_ARROW);
                      wc.lpszMenuName = NULL;
                      wc.cbClsExtra = 0;
                      wc.cbWndExtra = 0;
                      wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
                      if (!RegisterClassEx(&wc)) {
                      return 0;
                      }

                      hWnd = CreateWindow(szWindowName,
                      TEXT("OpenGLClearWin32"),
                      WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
                      CW_USEDEFAULT,
                      CW_USEDEFAULT,
                      CW_USEDEFAULT,
                      CW_USEDEFAULT,
                      NULL,
                      NULL,
                      hCurrInstance,
                      NULL);

                      if (!hWnd) {
                      return 0;
                      }

                      ShowWindow(hWnd, nWinMode);
                      UpdateWindow(hWnd);

                      while (GetMessage(&msg, NULL, 0, 0)) {
                      TranslateMessage(&msg);
                      DispatchMessage(&msg);
                      }

                      return msg.wParam;
                      }


                      LRESULT CALLBACK WindowProc(HWND hWnd, UINT nMessage, WPARAM wParam, LPARAM lParam)
                      {
                      switch (nMessage) {
                      case WM_CREATE:
                      OnCreate(hWnd);
                      break;
                      case WM_PAINT:
                      OnPaint(hWnd);
                      break;
                      case WM_DESTROY:
                      OnDestroy(hWnd);
                      PostQuitMessage(0);
                      break;
                      default:
                      return DefWindowProc(hWnd, nMessage, wParam, lParam);
                      }

                      return 0;
                      }


                      void OnCreate(HWND hWnd)
                      {
                      HDC hDC;
                      int pfdID;
                      BOOL bResult;

                      static PIXELFORMATDESCRIPTOR pfd = {
                      sizeof (PIXELFORMATDESCRIPTOR),
                      1,
                      PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL,
                      PFD_TYPE_RGBA,
                      24,
                      0, 0, 0,
                      0, 0, 0,
                      0, 0,
                      0, 0, 0, 0, 0,
                      32,
                      0,
                      0,
                      PFD_MAIN_PLANE,
                      0,
                      0,
                      0,
                      0
                      };

                      hDC = GetDC(hWnd);

                      pfdID = ChoosePixelFormat(hDC, &pfd);
                      if (pfdID == 0) {
                      return;
                      }

                      bResult = SetPixelFormat(hDC, pfdID, &pfd);
                      if (bResult == FALSE) {
                      return;
                      }

                      g_hGLRC = wglCreateContext(hDC);
                      if (g_hGLRC == NULL) {
                      return;
                      }

                      ReleaseDC(hWnd, hDC);
                      }

                      void OnPaint(HWND hWnd)
                      {
                      PAINTSTRUCT ps;
                      HDC hDC;

                      hDC = BeginPaint(hWnd, &ps);

                      wglMakeCurrent(hDC, g_hGLRC);

                      glClearColor(0.0f, 0.0f, 0.25f, 1.0f);
                      glClear(GL_COLOR_BUFFER_BIT);

                      wglMakeCurrent(NULL, NULL);

                      EndPaint(hWnd, &ps);
                      }

                      void OnDestroy(HWND hWnd)
                      {
                      if (g_hGLRC != NULL) {
                      wglDeleteContext(g_hGLRC);
                      }
                      }



                      ●参考文献

                      WindowsでのOpenGL 3Dプログラミングを解説した本としては、昔は、『Win32 OpenGLプログラミング』という本が有名でした。

                      しかし、この本は絶版になっているようで、古本でしか買うことができません。
                      高くなっていることと、サンプルプログラムが、CD-ROMではなく、フロッピーディスクなのも難点です。


                      最近、WindowsでOpenGLを使う本として、『MFCによるOpenGL 3Dプログラミング』という本が出ました。
                      こちらでも、MFCを使って、Windows環境でOpenGLを使う方法が、詳しく解説されています。
                      もちろん、MFCを使わずにWin32OpenGLのプログラムを書くのにも参考になります。
                      値段もあまり高くないですし、今は、こちらがお勧めです。


                      MFCによるOpenGL3Dプログラミング―ライブラリを使って簡単に3D-CG図形を描画する! (I/O BOOKS)
                      (伊藤拡、工学社)
                      工学社ホームページ(MFCによるOpenGL3Dプログラミング)

                      Win32 OpenGLプログラミング―Windows NT/95 3次元グラフィックスプログラミング入門 (Windows programming technique)
                      (クレイトン・ウォルナム 著, 松田晃一訳、ピアソン・エデュケーション)


                      また、Win32 APIを使ったWindowsプログラミングについては、『猫でもわかるWindowsプログラミング 』という本が分かりやすくていいと思います。また、より詳しく解説されている本としては、『Windows2000プログラミング標準講座』がとても参考になります。


                      猫でもわかるWindowsプログラミング 第3版 (NEKO series)
                      (粂井康孝、ソフトバンククリエイティブ)

                      『Windows2000プログラミング標準講座―Windows95/98/NT共通の基本的なプログラミング技法とWindows2000の新機能 (Programmer's SELECTION)』
                      (ハーバート・シルト著、Herbert Schildt原著、山本信雄監訳、翔泳社)





                      <関連記事>

                      GLUTとOpenGLで画面をクリアするプログラム
                      GLUTとOpenGLで三角形を描画するプログラム
                      GLUTとOpenGLで立方体を描画するプログラム
                      GLUTとOpenGLでライティングされた立方体を描画するプログラム
                      GLUTとOpenGLでティーポットを描画するプログラム
                      GLUTとOpenGLで立方体を回転させて表示するプログラム
                      法線ベクトルを計算する方法

                      MFCとOpenGLで画面をクリアするプログラム

                      VisualC++ 2008 Express EditionでWin32とOpenGLで画面をクリアするプログラムを作成する


                      calendar
                       123456
                      78910111213
                      14151617181920
                      21222324252627
                      282930    
                      << September 2014 >>
                      selected entries
                      categories
                      archives
                      recommend
                      recommend
                      recommend
                      recommend
                      links
                      profile
                      search this site.
                      others
                      mobile
                      qrcode
                      powered
                      無料ブログ作成サービス JUGEM