CX's Hatena Blog

はてなブログを使ってみるテスト

OpenGL で地球を回してみるテスト

ちょっと興味のある質問を見かけたので、方法について調べてみました。

(大至急お願いします)OpenGLでテクスチャマッピングを使用した.. - 人力検索はてな

f:id:cx20:20140122050101p:plain

「自然」がテーマの 3D アニメーションを「OpenGL」で作りたいということだったので、地球を回してみることにしました。

ちなみに自分の「OpenGL」のスキルとしては「Hello, World!」を書いた程度で、ほとんど未経験者です(^^;;

0. 事前準備

以下のコンパイラとライブラリを用意します。

1. Visual C++ 2013 を起動する

f:id:cx20:20140120001921p:plain

2. 新しいプロジェクトを作成する

新しいプロジェクト名は「earth」としておきます。

f:id:cx20:20140120002043p:plain

f:id:cx20:20140120002044p:plain

f:id:cx20:20140120002045p:plain

3. ライブラリをプロジェクトフォルダにコピーする

にて入手したインクルードファイルとライブラリをプロジェクトフォルダにコピーします。

f:id:cx20:20140120002050p:plain

f:id:cx20:20140120002046p:plain

4. プロジェクトのプロパティでインクルードファイルとライブラリのパスを追加する

f:id:cx20:20140120002047p:plain

f:id:cx20:20140120002048p:plain

f:id:cx20:20140120002049p:plain

5. ソースを修正する

f:id:cx20:20140120002051p:plain

ウィザードで生成された上記の「earth.cpp」を以下のコードに置き換えます。

#include "stdafx.h"
#include <windows.h>
#include <stdio.h>
#include <math.h>
#include <iostream>
#include <GL/gl.h>
#include <GL/glut.h>

GLuint texture[1];
double angle = 0;
typedef struct
{
    int X;
    int Y;
    int Z;
    double U;
    double V;
}VERTICES;

const double PI = 3.1415926535897;
const int space = 10;
const int VertexCount = (90 / space) * (360 / space) * 4;
VERTICES VERTEX[VertexCount];
GLuint LoadTextureRAW(const char * filename);

void DisplaySphere(double R, GLuint texture){
    int b;
    glScalef(0.0125 * R, 0.0125 * R, 0.0125 * R);
    glRotatef(90, 1, 0, 0);
    glBindTexture(GL_TEXTURE_2D, texture);
    glBegin(GL_TRIANGLE_STRIP);
    for (b = 0; b <= VertexCount; b++){
        glTexCoord2f(VERTEX[b].U, VERTEX[b].V);
        glVertex3f(VERTEX[b].X, VERTEX[b].Y, -VERTEX[b].Z);
    }
    for (b = 0; b <= VertexCount; b++){
        glTexCoord2f(VERTEX[b].U, -VERTEX[b].V);
        glVertex3f(VERTEX[b].X, VERTEX[b].Y, VERTEX[b].Z);
    }
    glEnd();
}

void CreateSphere(double R, double H, double K, double Z) {
    int n;
    double a;
    double b;
    n = 0;
    for (b = 0; b <= 90 - space; b += space){
        for (a = 0; a <= 360 - space; a += space){
            VERTEX[n].X = R * sin((a) / 180 * PI) * sin((b) / 180 * PI) - H;
            VERTEX[n].Y = R * cos((a) / 180 * PI) * sin((b) / 180 * PI) + K;
            VERTEX[n].Z = R * cos((b) / 180 * PI) - Z;
            VERTEX[n].V = (2 * b) / 360;
            VERTEX[n].U = (a) / 360;
            n++;
            VERTEX[n].X = R * sin((a) / 180 * PI) * sin((b + space) / 180 * PI) - H;
            VERTEX[n].Y = R * cos((a) / 180 * PI) * sin((b + space) / 180 * PI) + K;
            VERTEX[n].Z = R * cos((b + space) / 180 * PI) - Z;
            VERTEX[n].V = (2 * (b + space)) / 360;
            VERTEX[n].U = (a) / 360;
            n++;
            VERTEX[n].X = R * sin((a + space) / 180 * PI) * sin((b) / 180 * PI) - H;
            VERTEX[n].Y = R * cos((a + space) / 180 * PI) * sin((b) / 180 * PI) + K;
            VERTEX[n].Z = R * cos((b) / 180 * PI) - Z;
            VERTEX[n].V = (2 * b) / 360;
            VERTEX[n].U = (a + space) / 360;
            n++;
            VERTEX[n].X = R * sin((a + space) / 180 * PI) * sin((b + space) / 180 * PI) - H;
            VERTEX[n].Y = R * cos((a + space) / 180 * PI) * sin((b + space) / 180 * PI) + K;
            VERTEX[n].Z = R * cos((b + space) / 180 * PI) - Z;
            VERTEX[n].V = (2 * (b + space)) / 360;
            VERTEX[n].U = (a + space) / 360;
            n++;
        }
    }
}

void display(void) {
    glClearDepth(1);
    glClearColor(0.0, 0.0, 0.0, 1.0);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glLoadIdentity();
    glTranslatef(0, 0, -10);
    glRotatef(angle, 0, 1, 0);
    DisplaySphere(5, texture[0]);
    glutSwapBuffers();
    angle++;
    Sleep(10);
}

void init(void) {
    glEnable(GL_DEPTH_TEST);
    glEnable(GL_TEXTURE_2D);
    glDepthFunc(GL_LEQUAL);
    glCullFace(GL_BACK);
    glFrontFace(GL_CCW);
    glEnable(GL_CULL_FACE);
    texture[0] = LoadTextureRAW("earth.raw");
    CreateSphere(70, 0, 0, 0);
}

void reshape(int w, int h) {
    glViewport(0, 0, (GLsizei)w, (GLsizei)h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(60, (GLfloat)w / (GLfloat)h, 0.1, 100.0);
    glMatrixMode(GL_MODELVIEW);
}

int main(int argc, char **argv) {
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_DEPTH);
    glutInitWindowSize(450, 450);
    glutInitWindowPosition(100, 100);
    glutCreateWindow("Hello, OpenGL World!");
    init();
    glutDisplayFunc(display);
    glutIdleFunc(display);
    glutReshapeFunc(reshape);
    glutMainLoop();
    return 0;
}

GLuint LoadTextureRAW(const char * filename)
{
    GLuint texture;
    int width, height;
    unsigned char * data;
    FILE * file;
    fopen_s(&file, filename, "rb");
    if (file == NULL) return 0;
    width = 1024;
    height = 512;
    data = (unsigned char *)malloc(width * height * 3);
    fread(data, width * height * 3, 1, file);
    fclose(file);
    glGenTextures(1, &texture);
    glBindTexture(GL_TEXTURE_2D, texture);
    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    gluBuild2DMipmaps(GL_TEXTURE_2D, 3, width, height, GL_RGB, GL_UNSIGNED_BYTE, data);
    free(data);
    return texture;
}

上記、コードは、

■ 31. OpenGL Sphere Creation – Swiftless Tutorials - OpenGL Tutorials

http://www.swiftless.com/tutorials/opengl/sphere.html

のものを、コンパイルを通す為に一部、修正したものになります。

6. コンパイルする

[F5] キーでコンパイルします。

f:id:cx20:20140120002055p:plain

「正常終了」が出れば、とりあえず、コンパイル完了です。警告は必要に応じて修正してください。

7. 出力先フォルダにライブラリとテクスチャファイルをコピーする

f:id:cx20:20140120002052p:plain

テクスチャに使用する画像ファイルは、1024x512サイズの「RAW」形式のものを用意してください。

f:id:cx20:20140120002054p:plain

※ RAW 形式ファイルは GIMP 等で作成可能です。画像サイズが1024x512なのはプログラムでハードコードしている為です(変更する場合は、ソースも合わせて修正してください)

8. 実行する

f:id:cx20:20140122050101p:plain

地球が回れば、成功です。

参考

■ 31. OpenGL Sphere Creation – Swiftless Tutorials - OpenGL Tutorials

http://www.swiftless.com/tutorials/opengl/sphere.html

GLUTによる「手抜き」OpenGL入門

http://www.wakayama-u.ac.jp/~tokoi/opengl/libglut.html

■ ☆PROJECT ASURA☆ [OpenGL] 『テクスチャを読み込む!!(1) ~RAWファイル~』

http://asura.iaigiri.com/OpenGL/gl4.html

追記

他のライブラリでも試してみたくなったので、シリーズ化してみました。

今回日程 シリーズ構成言語
1日目OpenGL で地球を回してみるテスト C/C++
2日目DirectX で地球を回してみるテスト C/C++
3日目Java 3D で地球を回してみるテスト Java
4日目WebGL で地球を回してみるテスト JavaScript
5日目Three.js で地球を回してみるテスト JavaScript
6日目Babylon.js で地球を回してみるテストJavaScript
7日目gl.enchant.js で地球を回してみるテストJavaScript
8日目PhiloGL で地球を回してみるテストJavaScript
9日目CubicVR.js で地球を回してみるテストJavaScript
最終日Away3D で地球を回してみるテストJavaScript
追加+1Flash 版 Away3D で地球を回してみるテストActionScript
追加+2WPF で地球を回してみるテストC# + XAML
追加+3JavaFX 3D で地球を回してみるテストJava