OpenGLで遊ぶ

OpenGLを触るきっかけがあったのでfreeglutで遊んでみた。

Homer Simpson

かわいい。

よく使ったオブジェクト

void gluSphere(
    GLUquadric *qobj, 
    GLdouble radius, 
    GLint slices, 
    GLint stacks
);
GLquadric *qobj

quadricオブジェクトのポインタ。 初学者なのでquadricオブジェクトが何か知らない。 gluNewQuadric()で全て誤魔化した。

GLdouble radius

半径。

GLint slices

経度の分割数。 数が大きいほど滑らか。

GLint stacks

緯度の分割数。

円筒

void gluCylinder(
    GLUquadric *qobj,
    GLdouble   baseRadius,
    GLdouble   topRadius,
    GLdouble   height,
    GLint      slices,
    GLint      stacks
);
GLquadric *qobj

quadricオブジェクトのポインタ。

GLdouble baseRadiius

底面の半径。

GLdouble topRadius

上面の半径。

GLdouble height

円筒の高さ

GLint slices

経度の分割数。

GLint stacks

緯度の分割数。

座標の平行移動

void glTranslated(
    GLdouble x, 
    GLdouble y, 
    GLdouble z
);
GLdouble x, y, z

平行移動するベクトル。

座標の回転

void glRotated(
    GLdouble angle,
    GLdouble x,
    GLdouble y,
    GLdouble z
);
GLdouble angle

回転する角度。

GLdouble x, y, z

回転軸ベクトル。

拡大・縮小

void glScaled(
    GLdouble x,
    GLdouble y,
    GLdouble z
);
GLdouble x, y, z

それぞれの方向に対する拡大率。

座標系の一時的な変更

void glPushMatrix(void);

座標系の保存。

void glPopMatrix(void);

座標系の元に戻す。

e.g.

glPushMatrix();
    /* 座標系を変更する処理 */
    glTranslated(1.0, 0.0, 0.0); // ... (#)
    glSphere(glNewQuadric(), 1.0, 1.0, 3.0, 30, 30);
glPopMatrix();

/* ここでは(#)の処理が反映されない */
glSphere(glNewQuadric(), 1.0, 1.0, 3.0, 30, 30);

C言語だから仕方ないけど、Pythonなら絶対withステートメント使う。

色付け

void glMaterialfv(
    GLenum face, 
    GLenum pname, 
    const GLfloat *params
);
GLenum face

GL_FRONT: 表 GL_BACK: 裏 GL_FRONT_AND_BACK: 表と裏

GLenum pname

GL_DIFFUSE: 拡散反射 GL_SPECULAR: 鏡面反射 GL_AMBIENT: 環境光

const GLfloat *params

色を指定する。 (0.0, 1.0)のRGBと透過率(α値)で指定する。

// e.g.
GLfloat red[] = {1.0, 0.0, 0.0, 1.0};

まだこんな少数の関数しか覚えてないけど、誤魔化しながらホーマー描けたので満足。

参考文献

docs.microsoft.com

(accessed on 2021/05/23)

#include <GL/glut.h>
#include <math.h>
#define PI2 (3.1415*2)

GLfloat white[] = {1.0, 1.0, 1.0, 1.0};
GLfloat jeans[] = {0, 0.5, 0.7, 1.0};
GLfloat yellow[] = {1.0, 0.9, 0.0, 1.0};
GLfloat stubble[] = {0.596, 0.486, 0.165, 1.0};
GLfloat grey[] = {0.4, 0.4, 0.4, 1.0};
GLfloat black[] = {0, 0, 0, 1.0};

void Homer();
void head();
void body();
void arms();
void legs();

void eyes();
void mouse();
void nose();
void ears();
void hand(int isRight);


void Homer(void) {
    /* head */
    glPushMatrix();
        glTranslatef(0, 0.2, 2.2);
        glScalef(0.8, 0.8, 0.8);
        head();
    glPopMatrix();

    /* body */
    body();

    /* legs */
    glPushMatrix();
        glTranslated(0.0, 0.0, -2.5);
        legs();
    glPopMatrix();
}

void head(void) {
    GLdouble headHeight = 1.05;
    GLdouble headRadius = 0.63;
    glMaterialfv(GL_FRONT, GL_DIFFUSE, yellow);
    glPushMatrix();
        glRotatef(20, 1, 0, 0);
        glPushMatrix();
            gluCylinder(gluNewQuadric(), headRadius+0.02, headRadius, headHeight, 30, 10);
            glPushMatrix();
                glTranslated(0, 0.0, 0.01);
                glScalef(1, 1, 0.9);
                gluSphere(gluNewQuadric(), headRadius+0.02, 30, 30);
            glPopMatrix();
            glPushMatrix();
                glTranslated(0, 0, headHeight);
                glScalef(1, 1, 0.8);
                gluSphere(gluNewQuadric(), headRadius, 30, 30);
            glPopMatrix();
        glPopMatrix();

        /* hear side */
        glMaterialfv(GL_FRONT, GL_DIFFUSE, black);
        int numSpike = 30;
        double step = PI2 / numSpike;
        GLdouble sideHearHeight = 0.45;
        GLdouble sideHearRadius = headRadius + 0.01;
        GLdouble sideHearWidth = 2.0;
        int start = 5, end = 23;
        glLineWidth(sideHearWidth);
        glBegin(GL_LINE_STRIP);
        for (int i = 0; i < numSpike; i++) {
            if (i < start) continue;
            if (i > end) continue;
            glVertex3d(sideHearRadius * sin(i * step), sideHearRadius * cos(i * step), sideHearHeight);
            glVertex3d(sideHearRadius * sin((i+0.5) * step), sideHearRadius * cos((i+0.5) * step), sideHearHeight+0.3);
        }
        glVertex3d(sideHearRadius * sin((end+1) * step), sideHearRadius * cos((end+1) * step), sideHearHeight);
        glEnd();
        
        /* hear top */
        GLdouble topHearHeight = headHeight + 0.3;
        GLdouble between = 0.1;
        for (int i = 0; i < 2; i++) {
            glPushMatrix();
                glTranslated(0, (i == 0)? between : -between, topHearHeight);
                hear();
            glPopMatrix();
        }
    glPopMatrix();

    /* eyes */
    GLdouble eyePos[] = {0, 0.4, 0.60};
    glPushMatrix();
        glTranslated(eyePos[0], eyePos[1], eyePos[2]);
        eyes();
    glPopMatrix();

    /* mouse */
    GLdouble mousePos[] = {0, 0.29, -0.08};
    glPushMatrix();
        glTranslated(mousePos[0], mousePos[1], mousePos[2]);
        mouse();
    glPopMatrix();

    /* nose */
    GLdouble nosePos[] = {0, 0.5, 0.36};
    glPushMatrix();
        glTranslated(nosePos[0], nosePos[1], nosePos[2]);
        nose();
    glPopMatrix();

    /* ears */
    GLdouble earPos[] = {0, 0, 0.27};
    glPushMatrix();
        glTranslated(earPos[0], earPos[1], earPos[2]);
        ears();
    glPopMatrix();
}


void body(void) {
    /* arms */
    GLdouble armPos[] = {0, 0, 1.55};
    glPushMatrix();
        glTranslated(armPos[0], armPos[1], armPos[2]);
        arms();
    glPopMatrix();

    glPushMatrix();
        /* waist */
        glMaterialfv(GL_FRONT, GL_DIFFUSE, jeans);
        gluSphere(gluNewQuadric(), 1.0, 30, 30);
        
        /* chest */
        glTranslatef(0, 0, -0.18);
        glMaterialfv(GL_FRONT, GL_DIFFUSE, white);
        gluCylinder(gluNewQuadric(), 1.15, 0.4, 1.9, 30, 10);

        /* neck */
        glTranslated(0, 0, 1.7);
        glMaterialfv(GL_FRONT, GL_DIFFUSE, yellow);
        gluCylinder(gluNewQuadric(), 0.4, 0.4, 0.6, 30, 10);
    glPopMatrix();
}

void arms(void) {
    GLdouble sleeveLength = 0.5;
    GLdouble sleeveRadius = 0.4;
    GLdouble armLength = 1.0;
    GLdouble pos[3] = {0.4, 0, 0};
    GLfloat angle = -30;

    /* right arm */
    glPushMatrix();
        glTranslated(pos[0], pos[1], pos[2]);
        glRotatef(angle, 0, 1, 0);
        sleeve_and_arm(sleeveRadius, sleeveLength, armLength, 1);
    glPopMatrix();

    /* left arm */
    glPushMatrix();
        glTranslated(-pos[0], pos[1], pos[2]);
        glRotatef(-angle, 0, 1, 0);
        sleeve_and_arm(sleeveRadius, sleeveLength, armLength, 0);
    glPopMatrix();
}

void sleeve_and_arm(GLdouble sleeveRadius, GLdouble sleeveLength, GLdouble armLength, int isRight) {
    glPushMatrix();
        /* sleeve */
        glMaterialfv(GL_FRONT, GL_DIFFUSE, white);
        glTranslated(0, 0, -sleeveRadius);
        gluSphere(gluNewQuadric(), sleeveRadius, 30, 30);
        glTranslated(0, 0, -sleeveLength);
        gluCylinder(gluNewQuadric(), 0.4, 0.4, sleeveLength, 30, 30);

        /* arm */
        glMaterialfv(GL_FRONT, GL_DIFFUSE, yellow);
        glTranslated(0, 0, -armLength);
        gluCylinder(gluNewQuadric(), 0.3, 0.35, armLength+sleeveLength, 30, 30);

        /* hand */
        hand(isRight);
    glPopMatrix();
}

void hand(int isRight) {
    GLdouble thumbLength = 0.25;
    GLdouble indexLength = 0.36;
    GLdouble middleLength = 0.36;
    GLdouble pinkieLength = 0.35;
    double t = (isRight)? 1.0 : -1.0;
    glMaterialfv(GL_FRONT, GL_DIFFUSE, yellow);
    glPushMatrix();
        glScalef(1, 1, 0.6);
        gluSphere(gluNewQuadric(), 0.3, 30, 30);
    glPopMatrix();
    
    /* thumb */
    glPushMatrix();
        glRotatef(20*t, 0, 1, 0);
        glTranslated(-0.11*t, 0.16, -thumbLength);
        gluCylinder(gluNewQuadric(), 0.1, 0.11, thumbLength, 30, 30);
        gluSphere(gluNewQuadric(), 0.1, 30, 30);   
    glPopMatrix();

    /* index */
    glPushMatrix();
        glRotatef(10*t, 0, 1, 0);
        glTranslated(0.13*t, 0.12, -indexLength);
        gluCylinder(gluNewQuadric(), 0.1, 0.12, indexLength, 30, 30);
        gluSphere(gluNewQuadric(), 0.1, 30, 30);   
    glPopMatrix();

    /* middle */
    glPushMatrix();
        glRotatef(10*t, 0, 1, 0);
        glTranslated(0.17*t, -0.03, -middleLength);
        gluCylinder(gluNewQuadric(), 0.1, 0.12, middleLength, 30, 30);
        gluSphere(gluNewQuadric(), 0.1, 30, 30);   
    glPopMatrix();

    /* pinlie */
    glPushMatrix();
        glRotatef(10*t, 0, 1, 0);
        glTranslated(0.13*t, -0.14, -pinkieLength);
        gluCylinder(gluNewQuadric(), 0.09, 0.11, pinkieLength, 30, 30);
        gluSphere(gluNewQuadric(), 0.09, 30, 30);   
    glPopMatrix();
}

void legs(void) {
    GLdouble legLength = 2.3;
    GLfloat pos[3] = {0.37, 0, 0};
    GLdouble legRadius = 0.5;
    for (int i = 0; i < 2; i++) {
        glPushMatrix();
            glMaterialfv(GL_FRONT, GL_DIFFUSE, jeans);
            glTranslatef((i==0)? pos[0] : -pos[0], pos[1], pos[2]);
            gluCylinder(gluNewQuadric(), legRadius, legRadius, legLength, 30, 10);
            
            /* shoe */
            glMaterialfv(GL_FRONT, GL_DIFFUSE, grey);
            glScalef(1, 1.4, 0.45);
            glTranslated(0, 0.1, -0.2);
            gluSphere(gluNewQuadric(), legRadius, 30, 30);
        glPopMatrix();
    }
}

void eyes(void) {
    GLdouble pos[3] = {0.24, 0, 0};
    GLdouble eyeSize = 0.24;
    
    glPushMatrix();
        glScalef(1, 0.8, 1);
        for (int i = 0; i < 2; i++) {
            glPushMatrix();
            glTranslated((i==0)? pos[0] : -pos[0], pos[1], pos[2]);
            glMaterialfv(GL_FRONT, GL_DIFFUSE, white); // color
            gluSphere(gluNewQuadric(), eyeSize, 30, 30);
            glMaterialfv(GL_FRONT, GL_DIFFUSE, black); // color
            glTranslated(0, eyeSize-0.01, 0);
            glScalef(1, 0.6, 1);
            gluSphere(gluNewQuadric(), 0.06, 30, 30);
            glPopMatrix();
        }
    glPopMatrix();
}

void mouse(void) {
    glMaterialfv(GL_FRONT, GL_DIFFUSE, stubble); // color
    glPushMatrix();
        glScalef(1, 1, 0.68);
        glTranslated(0, -0.12, 0.10);
        gluSphere(gluNewQuadric(), 0.6, 20, 20);
    glPopMatrix();

    glPushMatrix();
        glTranslated(0, -0.06, -0.04);
        glScalef(1, 1, 0.6);
        gluSphere(gluNewQuadric(), 0.5, 20, 20);
    glPopMatrix();
}

void nose(void) {
    GLdouble noseSize = 0.12;
    GLdouble noseHeight = 0.15;
    glMaterialfv(GL_FRONT, GL_DIFFUSE, yellow); // color
    glPushMatrix();
        glRotatef(90, -1, 0, 0);
        glPushMatrix();
            gluCylinder(gluNewQuadric(), noseSize, noseSize, noseHeight, 20, 20);
            glTranslated(0, 0, noseHeight);
            gluSphere(gluNewQuadric(), noseSize, 20, 20);
        glPopMatrix();
    glPopMatrix();
}

void ears(void) {
    GLdouble pos[3] = {0.61, -0.2, 0};
    glMaterialfv(GL_FRONT, GL_DIFFUSE, yellow); // color
    for (int i = 0; i < 2; i++) {
        glPushMatrix();
            glTranslated((i==0)? pos[0] : -pos[0], pos[1], pos[2]);
            glRotatef(100, 0, (i==0)? 1 : -1, 0);
            glRotatef(30, -1, 0, 0);
            glutSolidTorus(0.089, 0.08, 30, 30);
        glPopMatrix();
    }
}

void hear() {
    int n = 30;
    double step = PI2 / n;
    GLdouble hearRadius = 0.4;
    GLdouble hearWidth = 2.0;
    glLineWidth(hearWidth);
    glBegin(GL_LINE_LOOP);
    for (int i = 0; i < n; i++) {
        glVertex3d(hearRadius * cos(i * step), 0, hearRadius * sin(i * step));
    }
    glEnd();
}

void display(void) {
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    Homer();
    glutSwapBuffers();
}


void init(void) {
    
    glClearColor(1.0, 1.0, 1.0, 0.0);
    glEnable(GL_DEPTH_TEST);
    glEnable(GL_CULL_FACE);
    glCullFace(GL_BACK);

    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);
    GLfloat lightPos0[] = {-5, 15, 10, 1};
    glLightfv(GL_LIGHT0, GL_POSITION, lightPos0);
    
    glEnable(GL_LIGHT1);
    GLfloat lightPos1[] = {5, 15, 10, 1};
    glLightfv(GL_LIGHT1, GL_POSITION, lightPos1);

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(30.0, 1, 1.0, 100.0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    gluLookAt(8, 15, 3.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0);
}

int main(int argc, char *argv[]) {
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH);
    glutInitWindowSize(600, 600);
    glutCreateWindow("Homer Simpson");
    glutDisplayFunc(display);
    init();
    glutMainLoop();
    return 0;
}