読者です 読者をやめる 読者になる 読者になる

個人的自由ノート

ふと自分が気になった事を書いてます

OpenGLを学習中

OpenGLを勉強している最中です。この記事は2回目です。

マウスのドラッグ機能で線を描くプログラムです。ウィンドウの描画領域内でドラッグをすると線が描画されます。

#include <GL/glut.h>
#include <stdio.h>

#define MAXPOINTS 100
GLint point[MAXPOINTS][2];
int pointnum = 0;
int rubberband = 0;

void display(void){
    int i;
    glClear(GL_COLOR_BUFFER_BIT);

    if(pointnum > 1){
        glColor3d(0.0, 0.0, 0.0);
        glBegin(GL_LINES);
        for(i = 0; i < pointnum; ++i){
            glVertex2iv(point[i]);
        }
        glEnd();
    }

    glFlush();
}

void resize(int w, int h){
    glViewport(0, 0, w, h);
    glLoadIdentity();

    glOrtho(-0.5, (GLdouble)w - 0.5, (GLdouble)h - 0.5, -0.5, -1.0, 1.0);
}

void mouse(int button, int state, int x, int y){

    switch(button){
        case GLUT_LEFT_BUTTON:
            point[pointnum][0] = x;
            point[pointnum][1] = y;
            if(state == GLUT_UP){
                glColor3d(0.0, 0.0, 0.0);
                glBegin(GL_LINES);
                glVertex2iv(point[pointnum - 1]);
                glVertex2iv(point[pointnum]);
                glEnd();
                glFlush();

                rubberband = 0;
            } else {
            }
            if(pointnum < MAXPOINTS - 1) ++pointnum;
            break;
        case GLUT_MIDDLE_BUTTON:
            break;
        case GLUT_RIGHT_BUTTON:
            break;
        default:
            break;
    }
}

void motion(int x, int y){
    static GLint savepoint[2];

    glEnable(GL_COLOR_LOGIC_OP);
    glLogicOp(GL_INVERT);

    glBegin(GL_LINES);
    if(rubberband){
        glVertex2iv(point[pointnum - 1]);
        glVertex2iv(savepoint);
    }
    glVertex2iv(point[pointnum - 1]);
    glVertex2i(x, y);
    glEnd();

    glFlush();

    glLogicOp(GL_COPY);
    glDisable(GL_COLOR_LOGIC_OP);

    savepoint[0] = x;
    savepoint[1] = y;

    rubberband = 1;
}

void init(void){
    glClearColor(1.0, 1.0, 1.0, 1.0);
}

int main(int argc, char** argv){
    glutInitWindowPosition(100, 100);
    glutInitWindowSize(320, 240);
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_RGBA);
    glutCreateWindow(argv[0]);
    glutDisplayFunc(display);
    glutReshapeFunc(resize);
    glutMouseFunc(mouse);
    glutMotionFunc(motion);
    init();
    glutMainLoop();
    return 0;
}

glEnable(GLenum cap) 引数 cap に指定した機能を使用可能にします. GL_LOGIC_OP もしくは GL_COLOR_LOGIC_OP は, 図形の描画の際にウィンドウに既に描かれている内容と, これから描こうとする内容の間で論理演算を行うことができるようにします.

glDisable(GLenum cap) 引数 cap に指定した機能を使用不可にします.

glLogicOp(GLenum opcode) 引数 opcode にはウィンドウに描かれている内容と, これから描こうとする内容との間で行う論理演算のタイプを指定します. GL_COPY はこれから描こうとする内容をそのままウィンドウ内に描きます. GL_INVERT はウィンドウに描かれている内容の, これから描こうとする図形の領域を反転します. 詳しくは man glLogicOp を参照してください.

glutMotionFunc(void (*func)(int x, int y)) 引数 func には, マウスのいずれかのボタンを押しながらマウスを動かしたときに 実行する関数のポインタを与えます. この関数の引数 x と y には, 現在のマウスの位置が渡されます. この設定を解除するには, 引数に 0 (ヌルポインタ) を指定します (stdio.h 等の中で定義されている記号定数 NULL を使用しても良い).

%e3%82%b9%e3%82%af%e3%83%aa%e3%83%bc%e3%83%b3%e3%82%b7%e3%83%a7%e3%83%83%e3%83%88-2016-11-12-17-40-45

特定のキーボードのキーを入力すると、描画ウィンドウが終了されます。以下がそのプログラムです。

#include <GL/glut.h>
#include <stdio.h>
#include <stdlib.h>

#define MAXPOINTS 100
GLint point[MAXPOINTS][2];
int pointnum = 0;
int rubberband = 0;

void display(void){
    int i;
    glClear(GL_COLOR_BUFFER_BIT);

    if(pointnum > 1){
        glColor3d(0.0, 0.0, 0.0);
        glBegin(GL_LINES);
        for(i = 0; i < pointnum; ++i){
            glVertex2iv(point[i]);
        }
        glEnd();
    }

    glFlush();
}

void resize(int w, int h){
    glViewport(0, 0, w, h);
    glLoadIdentity();

    glOrtho(-0.5, (GLdouble)w - 0.5, (GLdouble)h - 0.5, -0.5, -1.0, 1.0);
}

void mouse(int button, int state, int x, int y){

    switch(button){
        case GLUT_LEFT_BUTTON:
            point[pointnum][0] = x;
            point[pointnum][1] = y;
            if(state == GLUT_UP){
                glColor3d(0.0, 0.0, 0.0);
                glBegin(GL_LINES);
                glVertex2iv(point[pointnum - 1]);
                glVertex2iv(point[pointnum]);
                glEnd();
                glFlush();

                rubberband = 0;
            } else {
            }
            if(pointnum < MAXPOINTS - 1) ++pointnum;
            break;
        case GLUT_MIDDLE_BUTTON:
            break;
        case GLUT_RIGHT_BUTTON:
            break;
        default:
            break;
    }
}

void motion(int x, int y){
    static GLint savepoint[2];

    glEnable(GL_COLOR_LOGIC_OP);
    glLogicOp(GL_INVERT);

    glBegin(GL_LINES);
    if(rubberband){
        glVertex2iv(point[pointnum - 1]);
        glVertex2iv(savepoint);
    }
    glVertex2iv(point[pointnum - 1]);
    glVertex2i(x, y);
    glEnd();

    glFlush();

    glLogicOp(GL_COPY);
    glDisable(GL_COLOR_LOGIC_OP);

    savepoint[0] = x;
    savepoint[1] = y;

    rubberband = 1;
}

void keyboard(unsigned char key, int x, int y){
    switch(key){
        case 'q':
        case 'Q':
        case '\033':
            exit(0);
        default:
            break;
    }
}

void init(void){
    glClearColor(1.0, 1.0, 1.0, 1.0);
}

int main(int argc, char** argv){
    glutInitWindowPosition(100, 100);
    glutInitWindowSize(320, 240);
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_RGBA);
    glutCreateWindow(argv[0]);
    glutDisplayFunc(display);
    glutReshapeFunc(resize);
    glutMouseFunc(mouse);
    glutMotionFunc(motion);
    glutKeyboardFunc(keyboard);
    init();
    glutMainLoop();
    return 0;
}

glutKeyboardFunc(void (*func)(unsigned char key, int x, int y)) 引数 func には, キーがタイプされたときに実行する関数のポインタを与えます. この関数の引数 key にはタイプされたキーの ASCII コードが渡されます. また x と y にはキーがタイプされたときのマウスの位置が渡されます.

図形を回転させたプログラムです。

#include <GL/glut.h>

void display(void){
    glClear(GL_COLOR_BUFFER_BIT);
    glRotated(25.0, 0.0, 1.0, 0.0);
    glBegin(GL_POLYGON);
    glColor3d(1.0, 0.0, 0.0);
    glVertex2d(-0.9, -0.9);
    glColor3d(0.0, 1.0, 0.0);
    glVertex2d(0.9, -0.9);
    glColor3d(0.0, 0.0, 1.0);
    glVertex2d(0.9, 0.9);
    glColor3d(1.0, 1.0, 0.0);
    glVertex2d(-0.9, 0.9);
    glEnd();
    glFlush();
}

void init(void){
    glClearColor(1.0, 1.0, 1.0, 1.0);
}

int main(int argc, char** argv){
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_RGBA);
    glutCreateWindow(argv[0]);
    glutDisplayFunc(display);
    init();
    glutMainLoop();
    return 0;
}

%e3%82%b9%e3%82%af%e3%83%aa%e3%83%bc%e3%83%b3%e3%82%b7%e3%83%a7%e3%83%83%e3%83%88-2016-11-12-17-53-40

glRotated(GLdouble angle, GLdouble x, GLdouble y, GLdouble z) 変換行列に回転の行列を乗じます. 引数はいずれも GLdouble 型 (double と等価) で, 一つ目の引数 angle は回転角, 残りの三つの引数 x, y, z は回転軸の方向ベクトルです. 引数が float 型なら glRotatef() を使います. 原点を通らない軸で回転させたい場合は, glTranslated() を使って一旦軸が原点を通るように図形を移動し, 回転後に元の位置に戻します.

#include <GL/glut.h>

GLdouble vertex[][3] = {
  { 0.0, 0.0, 0.0 },
  { 1.0, 0.0, 0.0 },
  { 1.0, 1.0, 0.0 },
  { 0.0, 1.0, 0.0 },
  { 0.0, 0.0, 1.0 },
  { 1.0, 0.0, 1.0 },
  { 1.0, 1.0, 1.0 },
  { 0.0, 1.0, 1.0 }
};

int edge[][2] = {
  { 0, 1 },
  { 1, 2 },
  { 2, 3 },
  { 3, 0 },
  { 4, 5 },
  { 5, 6 },
  { 6, 7 },
  { 7, 4 },
  { 0, 4 },
  { 1, 5 },
  { 2, 6 },
  { 3, 7 }
};

void display(void){
    int i;

    glClear(GL_COLOR_BUFFER_BIT);

    glColor3d(0.0, 0.0, 0.0);
    glBegin(GL_LINES);
    for(i = 0; i < 12; ++i){
        glVertex3dv(vertex[edge[i][0]]);
        glVertex3dv(vertex[edge[i][1]]);
    }
    glEnd();
    glFlush();
}

void resize(int w, int h){
    glViewport(0, 0, w, h);

    glLoadIdentity();
    glOrtho(-2.0, 2.0, -2.0, 2.0, -2.0, 2.0);
}

void init(void){
    glClearColor(1.0, 1.0, 1.0, 1.0);
}

int main(int argc, char** argv){
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_RGBA);
    glutCreateWindow(argv[0]);
    glutDisplayFunc(display);
    glutReshapeFunc(resize);
    init();
    glutMainLoop();
    return 0;
}

%e3%82%b9%e3%82%af%e3%83%aa%e3%83%bc%e3%83%b3%e3%82%b7%e3%83%a7%e3%83%83%e3%83%88-2016-11-12-18-07-25

glVertex3dv(const GLdouble *v) glVertex3dv() は三次元の座標値を指定するのに使います. 引数 v は 3 個の要素を持つ GLdouble 型 (double と等価) 配列を指定します. v[0] には x 座標値, v[1] には y 座標値, v[2] には z 座標値を格納します.

次回、透視投影等の続きをやりますので、今回はここまでです。上記のプログラムは、以下のような画像の図形を描画するために作られています。

libglut5

GLdouble vertex[][3] = {
  { 0.0, 0.0, 0.0 }, /* A */
  { 1.0, 0.0, 0.0 }, /* B */
  { 1.0, 1.0, 0.0 }, /* C */
  { 0.0, 1.0, 0.0 }, /* D */
  { 0.0, 0.0, 1.0 }, /* E */
  { 1.0, 0.0, 1.0 }, /* F */
  { 1.0, 1.0, 1.0 }, /* G */
  { 0.0, 1.0, 1.0 }  /* H */
};

int edge[][2] = {
  { 0, 1 }, /* ア (A-B) */
  { 1, 2 }, /* イ (B-C) */
  { 2, 3 }, /* ウ (C-D) */
  { 3, 0 }, /* エ (D-A) */
  { 4, 5 }, /* オ (E-F) */
  { 5, 6 }, /* カ (F-G) */
  { 6, 7 }, /* キ (G-H) */
  { 7, 4 }, /* ク (H-E) */
  { 0, 4 }, /* ケ (A-E) */
  { 1, 5 }, /* コ (B-F) */
  { 2, 6 }, /* サ (C-G) */
  { 3, 7 }  /* シ (D-H) */
};

次回からこのプログラムの続きをやっていきます。