OpenGL+SDL

最近、OpenGLを勉強してて、なんで今までDirectXとか使ってたんだろうとか思ってきた。最近のビデオカードの性能をフルにひきだしたいとか、HLSLを使いたいとかじゃなくて、単に高速に2Dを描画したいだけだったら、はるかにOpenGL+SDLのほうが楽だと思う。

ということで、以下、OpenGL+SDLで四角形を描画するサンプル。

#include <SDL.h>
#include <SDL_opengl.h>
#include <stdio.h>

const int SCREEN_WIDTH = 640;       // 画面の幅
const int SCREEN_HEIGHT = 480;      // 画面の高さ

// 初期化する
// 成功したときは0を、失敗したときは-1を返す
int Initialize()
{
    // SDLを初期化する
    if (SDL_Init(SDL_INIT_VIDEO) < 0) {
        fprintf(stderr, "SDLの初期化に失敗しました:%s\n", SDL_GetError());
        return -1;
    }

    // タイトルを設定する
    SDL_WM_SetCaption("OpenGL Sample", NULL);

    // 画面を初期化する
    SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
    SDL_Surface* screen = SDL_SetVideoMode(SCREEN_WIDTH, SCREEN_HEIGHT, 32, SDL_OPENGL);
    if (screen == NULL) {
        fprintf(stderr, "画面の初期化に失敗しました:%s\n", SDL_GetError());
        SDL_Quit();
        return -1;
    }

    // 画面をクリアする色を指定する
    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);

    // 射影マトリクスを設定する
    glOrtho(0.0, SCREEN_WIDTH, SCREEN_HEIGHT, 0.0, -1.0, 1.0);

    return 0;
}

// 更新する
void Update()
{
}

// 描画する
void Draw()
{
    // 画面をクリアする
    glClear(GL_COLOR_BUFFER_BIT);

    // 四角形を描く
    glBegin(GL_QUADS);
        // 描画色を白に設定
        glColor3f(1.0, 1.0f, 1.0);
        // 4つの頂点を指定
        glVertex3f(100.0f, 100.0f, 0);
        glVertex3f(540.0f, 100.0f, 0);
        glVertex3f(540.0f, 380.0f, 0);
        glVertex3f(100.0f, 380.0f, 0);
    glEnd();

    // 更新を画面に反映する
    SDL_GL_SwapBuffers();
}

// メインループ
void MainLoop()
{
    SDL_Event event;
    double next_frame = SDL_GetTicks();
    double wait = 1000.0 / 60;

    for (;;) {
        // すべてのイベントを処理する
        while (SDL_PollEvent(&event)) {
            // QUIT イベントが発生するか、ESC キーが押されたら終了する
            if ((event.type == SDL_QUIT) ||
                (event.type == SDL_KEYUP && event.key.keysym.sym == SDLK_ESCAPE))
                return;
        }
        // 1秒間に60回Updateされるようにする
        if (SDL_GetTicks() >= next_frame) {
            Update();
            // 時間がまだあるときはDrawする
            if (SDL_GetTicks() < next_frame + wait)
                Draw();
            next_frame += wait;
            SDL_Delay(0);
        }
    }
}

// 終了処理を行なう
void Finalize()
{
    SDL_Quit();
}

// メイン関数
int main(int argc, char* argv[])
{
    if (Initialize() < 0)
        return 1;
    MainLoop();
    Finalize();
    return 0;
}

スクリーンショット

さらにもうひとつ。リサジュー曲線。

#include <SDL.h>
#include <SDL_opengl.h>
#include <stdio.h>
#include <math.h>

const int SCREEN_WIDTH = 640;       // 画面の幅
const int SCREEN_HEIGHT = 480;      // 画面の高さ

float t = 0;

// 初期化する
// 成功したときは0を、失敗したときは-1を返す
int Initialize()
{
    // SDLを初期化する
    if (SDL_Init(SDL_INIT_VIDEO) < 0) {
        fprintf(stderr, "SDLの初期化に失敗しました:%s\n", SDL_GetError());
        return -1;
    }

    // タイトルを設定する
    SDL_WM_SetCaption("OpenGL Sample", NULL);

    // 画面を初期化する
    SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
    SDL_Surface* screen = SDL_SetVideoMode(SCREEN_WIDTH, SCREEN_HEIGHT, 32, SDL_OPENGL);
    if (screen == NULL) {
        fprintf(stderr, "画面の初期化に失敗しました:%s\n", SDL_GetError());
        SDL_Quit();
        return -1;
    }

    // 画面をクリアする色を指定する
    glClearColor(1.0f, 0.5f, 0.0f, 0.0f);

    // 射影マトリクスを設定する
    glOrtho(0.0, SCREEN_WIDTH, SCREEN_HEIGHT, 0.0, -1.0, 1.0);

    return 0;
}

// 更新する
void Update()
{
    t += 0.01f;
}

// 描画する
void Draw()
{
    const int N = 500;              // 頂点の数
    const float PI = 3.141592f;     // 円周率

    glClear(GL_COLOR_BUFFER_BIT);
   
    glColor3f(1.0f, 1.0f, 1.0f);
    glBegin(GL_LINE_LOOP);
    for (int i = 0; i < N; ++i) {
        glVertex3f(300*cos(3*2*PI*i/N + t) + 320,
                   220*sin(5*2*PI*i/N - PI/4 + t) + 240,
                   0.0);
    }
    glEnd();

    // 更新を画面に反映する
    SDL_GL_SwapBuffers();
}

// メインループ
void MainLoop()
{
    SDL_Event event;
    double next_frame = SDL_GetTicks();
    double wait = 1000.0 / 60;

    for (;;) {
        // すべてのイベントを処理する
        while (SDL_PollEvent(&event)) {
            // QUIT イベントが発生するか、ESC キーが押されたら終了する
            if ((event.type == SDL_QUIT) ||
                (event.type == SDL_KEYUP && event.key.keysym.sym == SDLK_ESCAPE))
                return;
        }
        // 1秒間に60回Updateされるようにする
        if (SDL_GetTicks() >= next_frame) {
            Update();
            // 時間がまだあるときはDrawする
            if (SDL_GetTicks() < next_frame + wait)
                Draw();
            next_frame += wait;
            SDL_Delay(0);
        }
    }
}

// 終了処理を行なう
void Finalize()
{
    SDL_Quit();
}

// メイン関数
int main(int argc, char* argv[])
{
    if (Initialize() < 0)
        return 1;
    MainLoop();
    Finalize();
    return 0;
}