SDLでOpenGLを使う



SDLOpenGLを使う時のメモです。

OpenGL Exsample





VC++のプロジェクト作成


  • 「ファイル」⇒「新規作成」⇒「プロジェクト」を選択。
  • 作成するプロジェクトの種類を「Win32」、テンプレートをWin32プロジェクトに設定。
  • プロジェクト名を「GL_SDL」とする。(任意)
  • 「ソリューションディレクトリを作成する」にチェックする。

アプリケーションの設定


  • アプリケーションの種類をWindowsアプリケーション
  • 追加のオプションで、「空のプロジェクト」にチェック

プロジェクトにcppファイルを追加する


  • ソリューションエクスプローラーのプロジェクトを右クリック⇒「新規追加」

#include <SDL/SDL.h>
#include <SDL/SDL_opengl.h>
#include <gl/glu.h>

#pragma comment(lib,"SDL.lib")
#pragma comment(lib,"SDLmain.lib")
#pragma comment(lib,"OpenGL32.Lib") 
#pragma comment(lib,"GlU32.Lib")    //環境により適宜変更する

//以下サンプルはこちらより拝借⇒http://zinnia.dyndns.org/~cvsweb/sdldoc-jp/guidevideoopengl.html#AEN192

static GLboolean should_rotate=GL_TRUE;
static void draw_screen( void );
static void handle_key_down( SDL_keysym* keysym );
static void process_events( void );
static void quit_tutorial( int code );
static void setup_opengl( int width, int height );

static void quit_tutorial( int code )
{
    /*
     * SDL を終了してフルスクリーンモードを解放し、
     * 以前のビデオ設定などを戻す。
     */
    SDL_Quit( );

    /* プログラムを終了する。*/
    exit( code );
}

static void handle_key_down( SDL_keysym* keysym )
{

    /* 
     * 興味があるのは 'ESC' が押された時だけ。
     *
     * 練習:
     * 矢印キーを処理し、表示位置・角度を変更するようにせよ。
     */
    switch( keysym->sym ) {
    case SDLK_ESCAPE:
        quit_tutorial( 0 );
        break;
    case SDLK_SPACE:
        should_rotate=!should_rotate;
        break;
    default:
        break;
    }

}

static void process_events( void )
{
    /* SDL イベントの置き場 */
    SDL_Event event;

    /* すべてのイベントをキューからつかみ取る */
    while( SDL_PollEvent( &event ) ) {

        switch( event.type ) {
        case SDL_KEYDOWN:
            /* キー押下を処理 */
            handle_key_down( &event.key.keysym );
            break;
        case SDL_QUIT:
            /* 終了要求 (Ctrl-c など) を処理 */
            quit_tutorial( 0 );
            break;
        }

    }

}

static void draw_screen( void )
{
    /* 回転角 */
    static float angle=0.0f;

    /*
     * 練習:
     * このひどいごみを頂点配列で置き換え、
     * glDrawElements を呼び出せ。
     *
     * 練習:
     * 上を終えた後、コンパイルされた頂点配列に変更せよ。
     *
     * 練習:
     * 私の螺旋形状が正しいことを確認せよ。;)
     */
    static GLfloat v0[]={ -1.0f, -1.0f,  1.0f };
    static GLfloat v1[]={  1.0f, -1.0f,  1.0f };
    static GLfloat v2[]={  1.0f,  1.0f,  1.0f };
    static GLfloat v3[]={ -1.0f,  1.0f,  1.0f };
    static GLfloat v4[]={ -1.0f, -1.0f, -1.0f };
    static GLfloat v5[]={  1.0f, -1.0f, -1.0f };
    static GLfloat v6[]={  1.0f,  1.0f, -1.0f };
    static GLfloat v7[]={ -1.0f,  1.0f, -1.0f };
    static GLubyte red[]  ={ 255,   0,   0, 255 };
    static GLubyte green[]={   0, 255,   0, 255 };
    static GLubyte blue[] ={   0,   0, 255, 255 };
    static GLubyte white[]={ 255, 255, 255, 255 };
    static GLubyte yellow[]={   0, 255, 255, 255 };
    static GLubyte black[]={   0,   0,   0, 255 };
    static GLubyte orange[]={ 255, 255,   0, 255 };
    static GLubyte purple[]={ 255,   0, 255,   0 };

    /* 色・デプスバッファを消去 */
    glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

    /* 射影行列は変更したくない */
    glMatrixMode( GL_MODELVIEW );
    glLoadIdentity( );

    /* z 軸の方向に下げる */
    glTranslatef( 0.0, 0.0, -5.0 );

    /* 回転 */
    glRotatef( angle, 0.0, 1.0, 0.0 );

    if( should_rotate ) {

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

    }

    /* 三角形データをパイプラインに送る */
    glBegin( GL_TRIANGLES );

    glColor4ubv( red );
    glVertex3fv( v0 );
    glColor4ubv( green );
    glVertex3fv( v1 );
    glColor4ubv( blue );
    glVertex3fv( v2 );

    glColor4ubv( red );
    glVertex3fv( v0 );
    glColor4ubv( blue );
    glVertex3fv( v2 );
    glColor4ubv( white );
    glVertex3fv( v3 );

    glColor4ubv( green );
    glVertex3fv( v1 );
    glColor4ubv( black );
    glVertex3fv( v5 );
    glColor4ubv( orange );
    glVertex3fv( v6 );

    glColor4ubv( green );
    glVertex3fv( v1 );
    glColor4ubv( orange );
    glVertex3fv( v6 );
    glColor4ubv( blue );
    glVertex3fv( v2 );

    glColor4ubv( black );
    glVertex3fv( v5 );
    glColor4ubv( yellow );
    glVertex3fv( v4 );
    glColor4ubv( purple );
    glVertex3fv( v7 );

    glColor4ubv( black );
    glVertex3fv( v5 );
    glColor4ubv( purple );
    glVertex3fv( v7 );
    glColor4ubv( orange );
    glVertex3fv( v6 );

    glColor4ubv( yellow );
    glVertex3fv( v4 );
    glColor4ubv( red );
    glVertex3fv( v0 );
    glColor4ubv( white );
    glVertex3fv( v3 );

    glColor4ubv( yellow );
    glVertex3fv( v4 );
    glColor4ubv( white );
    glVertex3fv( v3 );
    glColor4ubv( purple );
    glVertex3fv( v7 );

    glColor4ubv( white );
    glVertex3fv( v3 );
    glColor4ubv( blue );
    glVertex3fv( v2 );
    glColor4ubv( orange );
    glVertex3fv( v6 );

    glColor4ubv( white );
    glVertex3fv( v3 );
    glColor4ubv( orange );
    glVertex3fv( v6 );
    glColor4ubv( purple );
    glVertex3fv( v7 );

    glColor4ubv( green );
    glVertex3fv( v1 );
    glColor4ubv( red );
    glVertex3fv( v0 );
    glColor4ubv( yellow );
    glVertex3fv( v4 );

    glColor4ubv( green );
    glVertex3fv( v1 );
    glColor4ubv( yellow );
    glVertex3fv( v4 );
    glColor4ubv( black );
    glVertex3fv( v5 );

    glEnd( );

    /*
     * 練習:
     * 'Spc' で回転停止、'Esc' で終了することを
     * ユーザーに教えるテキストを描画せよ。
     * ベクターとテクスチャが貼られた四角形で行え。
     */

    /*
     * バッファを交換する。これはバックバッファからの
     * 次のフレームの描画と、
     * フロントバッファであったものに起こる
     * すべての描画操作の設定をドライバに通知する。
     *
     * ダブルバッファによって、
     * 更新中の画面領域へアプリケーションが同時に描画することから起きる、
     * 表示の乱れが防止される。
     */
    SDL_GL_SwapBuffers( );
}

static void setup_opengl( int width, int height )
{
    float ratio=(float) width / (float) height;

    /* シェーディングモデルは Gouraud (なめらか) */
    glShadeModel( GL_SMOOTH );

    /* 裏面を取り除く */
    glCullFace( GL_BACK );
    glFrontFace( GL_CCW );
    glEnable( GL_CULL_FACE );

    /* 消去時の色をセット */
    glClearColor( 0, 0, 0, 0 );

    /* ビューポートを設定 */
    glViewport( 0, 0, width, height );

    /*
     * 射影行列を変更し、ビューボリュームにセット。
     */
    glMatrixMode( GL_PROJECTION );
    glLoadIdentity( );
    /*
     * 練習:
     * これを glFrustum の呼び出しに置き換えよ。
     */
    gluPerspective( 60.0, ratio, 1.0, 1024.0 );
}

int main( int argc, char* argv[] )
{
    const SDL_VideoInfo* info=NULL; /* 現在のビデオ設定についての情報 */
    int width=0;        /* ウィンドウの寸法 */
    int height=0;       /* ウィンドウの色のピクセル深度 */
    int bpp=0;          /* SDL_SetVideoMode に渡すフラグ */
    int flags=0;

    /* まず、SDL ビデオサブシステムを初期化 */
    if( SDL_Init( SDL_INIT_VIDEO ) < 0 ) {
        /* 失敗したので終了 */
        fprintf( stderr, "ビデオの初期化に失敗しました: %s\n",
             SDL_GetError( ) );
        quit_tutorial( 1 );
    }

    /* いくつかのビデオ情報を取得しよう */
    info=SDL_GetVideoInfo( );

    if( !info ) {
        /* おそらくこれは絶対に起きないはず */
        fprintf( stderr, "ビデオの問い合わせに失敗しました: %s\n",
             SDL_GetError( ) );
        quit_tutorial( 1 );
    }

    /*
     * 横幅/高さを 640/480 にセット。
     * (もちろん普通のアプリケーションにおいて
     * ユーザーにこれを決定してもらうだろう)
     * 画面から要求していたピクセル深度を取得。
     * X11 では、VidMode は解像度を変えることができないため、
     * これはおそらく過度安全だろう。
     * Win32 では、ChangeDisplaySettings によって
     * ピクセル深度を変えることができる。
     */
    width=640;
    height=480;
    bpp=info->vfmt->BitsPerPixel;

    /*
     * ここで、OpenGL ウィンドウのために要求されたウィンドウ属性を設定したい。
     * RGB 各チャンネルに *少なくとも* 5 ビット欲しい。
     * また、少なくとも 16 ビットのデプスバッファも欲しい。
     *
     * 最後にする事はダブルバッファウィンドウの要求である。
     * '1' でダルバッファが有効になり、
     * '0' で無効になる。
     *
     * SDL_SetVideoMode へのフラグにおいて
     * SDL_DOUBLEBUF を使わない事に注意。
     * それは GL アトリビュートに影響せず、
     * 標準の 2D blit 転送の設定だけに影響する。
     */
    SDL_GL_SetAttribute( SDL_GL_RED_SIZE, 5 );
    SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE, 5 );
    SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE, 5 );
    SDL_GL_SetAttribute( SDL_GL_DEPTH_SIZE, 16 );
    SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );

    /*
     * SDL がフルスクリーンビデオモードにおいて
     * OpenGL ウィンドウを提供してくれるよう要求したい。
     *
     * 練習:
     * ウィンドウ状態で開始するオプションを作り、
     * glViewPort でリサイズイベントを適切に処理せよ。
     */
    //flags=SDL_OPENGL | SDL_FULLSCREEN;
    flags=SDL_OPENGL;

    /*
     * Set the video mode
     */
    if( SDL_SetVideoMode( width, height, bpp, flags )== 0 ) {
        /* 
         * これはさまざまな理由で起き得る。
         * DISPLAY が設定されていない、
         * 指定された解像度が利用可能でない、など。
         */
        fprintf( stderr, "ビデオモードのセットに失敗しました: %s\n",
             SDL_GetError( ) );
        quit_tutorial( 1 );
    }

    /*
     * ここで、OpenGL の使用のために
     * ダブルバッファのウィンドウを適切に設定したはず。
     */
    setup_opengl( width, height );

    /*
     * さて、通常のアプリケーション処理 -- たくさんの再描画と
     * イベントループを始めたい。
     */
    while( 1 ) {
        /* やってくるイベントを処理 */
        process_events( );
        /* 画面を表示 */
        draw_screen( );
    }

    /*
     * 練習:
     * SDL_GetTicks() を使ってタイミングを記録し、
     * プログラム終了時に 1 秒間のフレームを表示せよ。 
     */

    /* ここには届かない */
    return 0;
}

ビルド設定


  • ソリューションエクスプローラーのプロジェクトを右クリック⇒「プロパティ」
  • C/C++」⇒「コード生成」を「マルチスレッド DLL (/MD)」
  • デバッグプリントの機能を使うなら、サブシステムをコンソールにする事により、
    printf()を使ってデバッグ出力が可能。「リンカ」⇒「システム」⇒「サブシステム」を
    「コンソール (/SUBSYSTEM:CONSOLE)」に変更する。


OpenGL_SDLの実行例