OpenGLでカメラ設定

カメラの位置

カメラは描画のはじめにやればよさそう。

    // setup view
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    gluLookAt( -30.0f, 30.0f, -30.0f,
               centerX, centerY, centerZ,
               0.5f, 0.5f, 0.5f);

gluLookAt関数がカメラの設定をしており、

  • 1行目の(30.0f, 30.0f, 30.0f)はカメラの位置
  • 2行目の(centerX, centerY, centerZ)はカメラが見つめている先の位置
  • 3行目の(0.5f, 0.5f, 0.5f)はカメラの頭の方向を表すベクトル

を表している。詳しくは下記のサイト参照。
http://homepage3.nifty.com/li-chu/OpenGL/OpenGL04.html#look
頭の方向のベクトルについては大きさはそこまで関係ない?比率が大事っぽい。

サンプル

キーボード入力のテストも兼ねて、視線の位置を変更させるプログラム作ったので載せとく。

/* -*- coding:utf-8 -*- */
/**
 * opengl_lookat.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 : lookat test";
static const Uint32 WINDOW_WIDTH = 640;
static const Uint32 WINDOW_HEIGHT = 480;
static const Uint32 WINDOW_BPP = 32;

static const GLdouble SIGHT_ANGLE = 45.0;
static const GLdouble SIGHT_NEAR = 2.0;
static const GLdouble SIGHT_FAR = 200.0;

static const Uint32 FPS = 60;

static SDL_Window* window;
static SDL_GLContext context;

static GLfloat centerX = 0.0f;
static GLfloat centerY = 0.0f;
static GLfloat centerZ = 0.0f;

bool init();
bool finalize();

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

int main(int argc, char** argv) {
    // initialize
    if (!init()) {
        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() {
    // 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;
    gluPerspective(SIGHT_ANGLE, aspect, SIGHT_NEAR, SIGHT_FAR);

    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( -30.0f, 30.0f, -30.0f,
               centerX, centerY, centerZ,
               0.5f, 0.5f, 0.5f);

    // draw grid lines
    drawGrid();

    // draw center point
    drawCenter();

    glFlush();
}

void drawGrid() {
    // z-axis
    glBegin(GL_LINES);
    {
        glLineWidth(5.0f);
        glColor3d(0.0, 1.0, 1.0);
        glVertex3d(0, 0, -250.0);
        glVertex3d(0, 0, 250.0);
    }
    glEnd();

    for (GLint x=-5; x<=5; x++) {
        glBegin(GL_LINES);
        {
            glColor3d(1.0, 1.0, 1.0);
            glVertex3d(x, 0, -5.0);
            glVertex3d(x, 0, 5.0);
        }
        glEnd();
    }

    // x-axis
    glBegin(GL_LINES);
    {
        glLineWidth(5.0f);
        glColor3d(1.0, 0.0, 1.0);
        glVertex3d(-250, 0, 0);
        glVertex3d(250, 0, 0);
    }
    glEnd();

    for (GLint z=-5; z<=5; z++) {
        glBegin(GL_LINES);
        {
            glColor3d(1.0, 1.0, 1.0);
            glVertex3d(-5.0, 0, z);
            glVertex3d(5.0, 0, z);
        }
        glEnd();
    }

    // y-axis
    glBegin(GL_LINES);
    {
        glLineWidth(5.0f);
        glColor3d(1.0, 1.0, 0.0);
        glVertex3d(0, -250, 0);
        glVertex3d(0, 250, 0);
    }
    glEnd();
}

void drawCenter() {
    glPointSize(10.0f);
    glColor3d(1.0, 0.0, 0.0);
    glBegin(GL_POINTS);
    {
        glVertex3f(centerX, centerY, centerZ);
    }
    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;
            }
            // UP
            if(key == SDLK_UP){
                centerZ += 1.0f;
            }
            // DOWN
            if(key == SDLK_DOWN){
                centerZ -= 1.0f;
            }
            // LEFT
            if(key == SDLK_LEFT){
                centerX += 1.0f;
            }
            // RIGHT
            if(key == SDLK_RIGHT){
                centerX -= 1.0f;
            }
            // u
            if(key == SDLK_u){
                centerY += 1.0f;
            }
            // d
            if(key == SDLK_d){
                centerY -= 1.0f;
            }
            // r
            if(key == SDLK_r){
                centerX = 0.0f;
                centerY = 0.0f;
                centerZ = 0.0f;
            }
        }
        break;
        }
    }
    return true;
}

コンパイルは下記の通り。

g++ -o opengl_lookat opengl_lookat.cpp `sdl2-config --cflags` `sdl2-config --libs` -framework GLUT -framework OpenGL

実行すると下のようになる。紫色の線はx軸、黄色の線はy軸、水色の線はz軸を表している。白いグリッド船はxz平面上に間隔1.0で引いてある。中心の赤い点は視線の先で、さっきの(centerX, centerY, centerZ)の位置。
キーボードで視点の位置が変化する。詳細は以下の通り。

  • 上矢印 : z軸方向に1進む
  • 下矢印 : z軸方向に-1進む
  • 左矢印 : x軸方向に1進む
  • 右矢印 : x軸方向に-1進む
  • u : y軸方向に1進む
  • d : y軸方向に-1進む
  • r : 視線の先の座標を(0,0,0)に戻す