OpenGLの設定メモ

OpenGLの基礎

以前書いたsdl_with_opengl.cppでの初期化処理をもっと詳しく見ていく。

SDL_GL_SetAttribute

74行目の処理で次のことを行っている。

    SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);

OpenGLのパラメタを設定する。SDL1.2ではSDL_SetVideoModeを呼び出すまでは有効にならない。
http://www.tacoworks.jp/software/SDLdoc-jp/html/sdlglsetattribute.html
SDL2.0だとおそらくSDL_CreateWindowを呼び出す前に必要なパラメタを設定する必要があるみたい。
SDL付属のサンプルソースでは次のような属性が設定されている。

        /* Upload GL settings */
        SDL_GL_SetAttribute(SDL_GL_RED_SIZE, state->gl_red_size);
        SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, state->gl_green_size);
        SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, state->gl_blue_size);
        SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, state->gl_alpha_size);
        SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, state->gl_double_buffer);
        SDL_GL_SetAttribute(SDL_GL_BUFFER_SIZE, state->gl_buffer_size);
        SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, state->gl_depth_size);
        SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, state->gl_stencil_size);
        SDL_GL_SetAttribute(SDL_GL_ACCUM_RED_SIZE, state->gl_accum_red_size);
        SDL_GL_SetAttribute(SDL_GL_ACCUM_GREEN_SIZE, state->gl_accum_green_size);
        SDL_GL_SetAttribute(SDL_GL_ACCUM_BLUE_SIZE, state->gl_accum_blue_size);
        SDL_GL_SetAttribute(SDL_GL_ACCUM_ALPHA_SIZE, state->gl_accum_alpha_size);
        SDL_GL_SetAttribute(SDL_GL_STEREO, state->gl_stereo);
        SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, state->gl_multisamplebuffers);
        SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, state->gl_multisamplesamples);
        if (state->gl_accelerated >= 0) {
            SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL,
                                state->gl_accelerated);
        }
        SDL_GL_SetAttribute(SDL_GL_RETAINED_BACKING, state->gl_retained_backing);
        if (state->gl_major_version) {
            SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, state->gl_major_version);
            SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, state->gl_minor_version);
        }

結構あるるる。とりあえず必要になったら設定していく感じでいいのかしら。

SDL_GL_CreateContext

OpenGLを利用するwindowのOpenGLのコンテキストを作成する。
http://wiki.libsdl.org/moin.cgi/SDL_GL_CreateContext
...こんてきすと?良き聞くけどいまいち理解していない言葉の一つ。
OpenGLのcontextについては以下のサイトに簡単に概要がまとめてある。
http://www.opengl.org/wiki/OpenGL_context
要するにコンテキストは「情報の集合」であり、OpenGLを利用するオブジェクト(今回だとwindow)で利用する色々な情報を保管するためのところという感じらしい。
コンテキストを作成しないとOpenGLで利用する情報を管理する場所がないため動作させることができない。
OpenGLはコンテキストの作成に始まり、コンテキストの破棄に終わる。

glViewport

ビューポートを設定するメソッド。
ビューポートは、画像を描画するための領域のこと。
http://android.keicode.com/basics/opengl-drawing-basic-shapes.php

前書いたプログラムでビューポートのwidthとheightを半分にしてみる。

確かに半分になってる。(ブログ上だとそう見えないかも)
画面内に更に画面を持たせるとかいうときには多段で設定するのかね。

glPerspective

視野を設定するメソッド。
下記のサイトが個人的にはとってもわかりやすい。
http://homepage3.nifty.com/li-chu/OpenGL/OpenGL04.html

設定するのは視野(画像を射影する領域)をどうするか。
射影には

  • 正射影
  • 透視法射影

の2つがある。
正射影は奥行きが無視される描画方法のことで、2Dゲームならこっち。奥行きが無視されるので、物体が手前にあろうが奥にあろうが画面上では大きさの違いがない。
透視法射影はいわゆる普通の射影で、奥行きが必要な3Sゲームならこっち。

2つの視点で同じ物体を描画してみる。赤が手前 (z=10)、緑が中間 (z=50)、青が奥 (z=90)。

正射影で書いたのがこれ。全部同じ大きさに見える。

透視法射影で書いたのがこれ。奥に行くほど小さく見える。あと、奥のものほど中心点からの距離が近く見える (赤と青は中心から同じだけ離れてる)。

テストで使ったコードは以下の通り。正射影だとglOrthoで指定した座標がそのまま描画時に指定する大きさと合うけど、gluPerspectiveだと描画時の座標が実際にどうなるのかようわからん。視野角とかその辺から頑張って計算するとわかるのかも。

/* -*- coding:utf-8 -*- */
/**
 * opengl_projection.cpp
 */

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

#include <iostream>
#include <string>

#include <cmath>

static const std::string WINDOW_CAPTION = "SDL with OpenGL : projection test";
static const Uint32 WINDOW_WIDTH = 640;
static const Uint32 WINDOW_HEIGHT = 480;
static const Uint32 WINDOW_BPP = 32;

static const Uint32 FPS = 60;

static SDL_Window* window;
static SDL_GLContext context;

typedef enum {
    PROJ_2D,
    PROJ_3D
} eProjType;

bool init(eProjType projType = PROJ_3D);
bool finalize();

void update();
void draw();
bool pollingEvent();

int main(int argc, char** argv) {
    if (argc < 2) {
        std::cerr << "usage: " << argv[0] << " [2d|3d]" << std::endl;
        exit(1);
    }

    std::string projTypeStr(argv[1]);
    eProjType projType = PROJ_3D;
    if (projTypeStr == "2d") {
        projType = PROJ_2D;
    } else if (projTypeStr == "3d") {
        projType = PROJ_3D;
    } else {
        std::cerr << "ERROR: unknown proj type[" << projTypeStr << std::endl;
        exit(1);
    }

    // initialize
    if (!init(projType)) {
        std::cerr << "ERROR: failed to initialize SDL" << std::endl;
        exit(1);
    }

    // mainloop
    static const Uint32 interval = 1000 / FPS;
    static Uint32 nextTime = SDL_GetTicks() + interval;
    bool skipDraw = false;
    while (true) {
        // check event
        if (!pollingEvent()) break;

        // update and draw
        update();
        if (!skipDraw) {
            draw();
            SDL_GL_SwapWindow(window);
        }

        int delayTime = (int)(nextTime - SDL_GetTicks());
        if (delayTime > 0) {
            SDL_Delay((Uint32)delayTime);
            skipDraw = false;
        } else {
            // skip next draw step because of no time
            skipDraw = true;
        }

        nextTime += interval;
    }

    // finalize
    finalize();
  
    return 0;
}

bool init(eProjType projType) {
    // initialize SDL
    if( SDL_Init(SDL_INIT_VIDEO) < 0 ) return false;

    // enable double buffering
    SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);

    // create indow
    // set SDL_WINDOW_OPENGL to use opengl for drawing
    window = SDL_CreateWindow(WINDOW_CAPTION.c_str(),
                              SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
                              WINDOW_WIDTH, WINDOW_HEIGHT,
                              SDL_WINDOW_OPENGL);
    if (!window) return false;

    // create OpenGL context
    context = SDL_GL_CreateContext(window);
    if (!context) return false;

    // setup viewport
    glViewport(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT);
    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
    glEnable(GL_DEPTH_TEST);

    // setup projection matrix
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    GLdouble aspect = (GLdouble) WINDOW_WIDTH / (GLdouble) WINDOW_HEIGHT;
    if (projType == PROJ_2D) {
        glOrtho(-20.0*aspect, 20.0*aspect, -20.0, 20.0, 2.0, 200.0);
    } else {
        gluPerspective(45.0, aspect, 2.0, 200.0);
    }

    // setup light
    /*
    static GLfloat position[] = {-10.0f, 10.0f, 10.0f, 1.0f};
    static GLfloat ambient [] = { 1.0f, 1.0f, 1.0f, 1.0f};
    static GLfloat diffuse [] = { 1.0f, 1.0f, 1.0f, 1.0f};
    static GLfloat specular[] = { 0.0f, 0.0f, 0.0f, 0.0f};
    glLightfv(GL_LIGHT0, GL_POSITION, position);
    glLightfv(GL_LIGHT0, GL_AMBIENT, ambient);
    glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse);
    glLightfv(GL_LIGHT0, GL_SPECULAR, specular);
    glLightModelf(GL_LIGHT_MODEL_LOCAL_VIEWER, GL_TRUE);
    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);
    */

    return true;
}

bool finalize() {
  // finalize SDL
  SDL_Quit();

  return true;
}

void update() {
}

void draw() {
    // clear
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    // setup view
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    gluLookAt( 0.0f,  0.0f,-30.0f,
               0.0f,  0.0f, 0.0f,
               0.0f, -1.0f, 0.0f);

    // draw polugon
    // near (z=10)
    glBegin(GL_POLYGON);
    {
        glColor3f(1.0f, 0.0f, 0.0f);
        glVertex3f(-17.5f, -2.5f, 10.0f);
        glVertex3f(-17.5f, 2.5f, 10.0f);
        glVertex3f(-12.5f, 2.5f, 10.0f);
        glVertex3f(-12.5f, -2.5f, 10.0f);
    }
    glEnd();

    // mid (z=50)
    glBegin(GL_POLYGON);
    {
        glColor3f(0.0f, 1.0f, 0.0f);
        glVertex3f(-2.5f, -2.5f, 50.0f);
        glVertex3f(-2.5f, 2.5f, 50.0f);
        glVertex3f(2.5f, 2.5f, 50.0f);
        glVertex3f(2.5f, -2.5f, 50.0f);
    }
    glEnd();

    // near (z=90)
    glBegin(GL_POLYGON);
    {
        glColor3f(0.0f, 0.0f, 1.0f);
        glVertex3f(12.5f, -2.5f, 90.0f);
        glVertex3f(12.5f, 2.5f, 90.0f);
        glVertex3f(17.5f, 2.5f, 90.0f);
        glVertex3f(17.5f, -2.5f, 90.0f);
    }
    glEnd();
}

// polling event and execute actions
bool pollingEvent()
{
    SDL_Event ev;
    SDL_Keycode key;
    while ( SDL_PollEvent(&ev) )
    {
        switch(ev.type){
        case SDL_QUIT:
            // raise when exit event is occur
            return false;
            break;
        case SDL_KEYDOWN:
        // raise when key down
        {
            key = ev.key.keysym.sym;
            // ESC
            if(key == SDLK_ESCAPE){
                return false;
            }
        }
        break;
        }
    }
    return true;
}

コンパイルと実行

$ g++ -o opengl_projction opengl_projction.cpp `sdl2-config --cflags` `sdl2-config --libs` -framework GLUT -framework OpenGL
$ ./opengl_projction 2d
$ ./opengl_projction 3d


次回はカメラとか光源とか材質を調べないとあかんですね。うーむ、先は長い。。。
C++ばっかでやってるけど、Pythonとかスクリプト言語OpenGLヒャッホイもやりたいし。