HOME > natural science Laboratory > コンピュータ・シミュレーション講座 > OpenGL入門

VisualC++ を使った OpenGL 入門
【6日目】 記憶

文責:遠藤 理平 (2009年11月11日) カテゴリ:OpenGL入門(27)

6日目。
アニメーションの作成のために、ビットマップデータを保存します。

OpenGL と C++ によるソース

描画したアニメーションを動画として記録します。

#include <math.h>
#include <fstream>
#include <sstream>
#include <iostream>
#include <direct.h>
#include <GL/glut.h>
#include <GL/gl_screenshot.h> 
using namespace std;

double PI = acos(-1.0);

int WindowPositionX = 200;  //生成するウィンドウ位置のX座標
int WindowPositionY = 200;  //生成するウィンドウ位置のY座標
int WindowWidth     = 512;  //生成するウィンドウの幅
int WindowHeight    = 512;  //生成するウィンドウの高さ
char WindowTitle[]  = "世界の始まり";  //ウィンドウのタイトル

double ViewPointX   = 0.0;
double ViewPointY   = -200.0;
double ViewPointZ   = 20.0;

bool _Bitmap = true; 
int tn = 0 ;
double t = 0;
double dt = 0.1;
double omega = 2.0 * PI / 10.0;
gl_screenshot gs; //bmpファイルの出力

//----------------------------------------------------
// 直方体の定義
//----------------------------------------------------
GLdouble vertex[][3] = {
  { 0.0, 0.0, 0.0 },
  { 2.0, 0.0, 0.0 },
  { 2.0, 2.0, 0.0 },
  { 0.0, 2.0, 0.0 },
  { 0.0, 0.0, 30.0 },
  { 2.0, 0.0, 30.0 },
  { 2.0, 2.0, 30.0 },
  { 0.0, 2.0, 30.0 }
};
int face[][4] = {//面の定義
  { 0, 1, 2, 3 },
  { 1, 5, 6, 2 },
  { 5, 4, 7, 6 },
  { 4, 0, 3, 7 },
  { 4, 5, 1, 0 },
  { 3, 2, 6, 7 }
};
GLdouble normal[][3] = {//面の法線ベクトル
  { 0.0, 0.0,-1.0 },
  { 1.0, 0.0, 0.0 },
  { 0.0, 0.0, 1.0 },
  {-1.0, 0.0, 0.0 },
  { 0.0,-1.0, 0.0 },
  { 0.0, 1.0, 0.0 }
};
//----------------------------------------------------
// 物質質感の定義
//----------------------------------------------------
struct MaterialStruct {
  GLfloat ambient[4];
  GLfloat diffuse[4];
  GLfloat specular[4];
  GLfloat shininess;
};
//jade(翡翠)
MaterialStruct ms_jade = {
  {0.135,     0.2225,   0.1575,   1.0},
  {0.54,      0.89,     0.63,     1.0},
  {0.316228,  0.316228, 0.316228, 1.0},
  12.8};
//ruby(ルビー)
MaterialStruct ms_ruby  = {
  {0.1745,   0.01175,  0.01175,   1.0},
  {0.61424,  0.04136,  0.04136,   1.0},
  {0.727811, 0.626959, 0.626959,  1.0},
  76.8};
//----------------------------------------------------
// 色の定義の定義
//----------------------------------------------------
GLfloat red[] = { 0.8, 0.2, 0.2, 1.0 }; //赤色
GLfloat green[] = { 0.2, 0.8, 0.2, 1.0 };//緑色
GLfloat blue[] = { 0.2, 0.2, 0.8, 1.0 };//青色
GLfloat yellow[] = { 0.8, 0.8, 0.2, 1.0 };//黄色
GLfloat white[] = { 1.0, 1.0, 1.0, 1.0 };//白色
GLfloat shininess = 30.0;//光沢の強さ
//-----------------------------------------

//----------------------------------------------------
// 関数プロトタイプ(後に呼び出す関数名と引数の宣言)
//----------------------------------------------------
void Initialize(void);
void Display(void);
void Idle();
void Ground(void);  //大地の描画Keyboard
//----------------------------------------------------
// メイン関数
//----------------------------------------------------
int main(int argc, char *argv[]){
  if(_Bitmap) _mkdir("bitmap"); //bmpファイル保存用のフォルダの作成

  glutInit(&argc, argv);//環境の初期化
  glutInitWindowPosition(WindowPositionX, WindowPositionY);//ウィンドウの位置の指定
  glutInitWindowSize(WindowWidth, WindowHeight); //ウィンドウサイズの指定
  glutInitDisplayMode(GLUT_RGBA | GLUT_DEPTH | GLUT_DOUBLE);//ディスプレイモードの指定
  glutCreateWindow(WindowTitle);  //ウィンドウの作成
  glutDisplayFunc(Display); //描画時に呼び出される関数を指定する(関数名:Display)

  glutIdleFunc(Idle);       //プログラムアイドル状態時に呼び出される関数
  Initialize(); //初期設定の関数を呼び出す
  glutMainLoop();
  return 0;
}
//----------------------------------------------------
// 初期設定の関数
//----------------------------------------------------
void Initialize(void){
  glClearColor(1.0, 1.0, 1.0, 1.0); //背景色
  glEnable(GL_DEPTH_TEST);//デプスバッファを使用:glutInitDisplayMode() で GLUT_DEPTH を指定する

  //光源の設定--------------------------------------
  GLfloat light_position0[] = { -50.0, -50.0, 20.0, 1.0 }; //光源0の座標
  glLightfv(GL_LIGHT0, GL_POSITION, light_position0); //光源0を

}
//----------------------------------------------------
// 描画の関数
//----------------------------------------------------
void Display(void) {
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //バッファの消去
  
  t = dt * tn;
  ViewPointX = -100.0 * cos( omega * t);
  ViewPointY = -100.0 * sin( omega * t);

  //透視変換行列の設定------------------------------
  glMatrixMode(GL_PROJECTION);//行列モードの設定(GL_PROJECTION : 透視変換行列の設定、GL_MODELVIEW:モデルビュー変換行列)
  glLoadIdentity();//行列の初期化
  gluPerspective(30.0, (double)WindowWidth/(double)WindowHeight, 0.1, 1000.0); //透視投影法の視体積gluPerspactive(th, w/h, near, far);

  //視点の設定------------------------------
  gluLookAt(
       ViewPointX, ViewPointY, ViewPointZ, // 視点の位置x,y,z;
       0.0,        0.0,        ViewPointZ, // 視界の中心位置の参照点座標x,y,z
       0.0,        0.0,        1.0 ) ;     //視界の上方向のベクトルx,y,z
  //----------------------------------------

  //モデルビュー変換行列の設定--------------------------
  glMatrixMode(GL_MODELVIEW);//行列モードの設定(GL_PROJECTION : 透視変換行列の設定、GL_MODELVIEW:モデルビュー変換行列)
  glLoadIdentity();//行列の初期化
  glViewport(0, 0, WindowWidth, WindowHeight);
  //----------------------------------------------

  //陰影ON-----------------------------
  glEnable(GL_LIGHTING);
  glEnable(GL_LIGHT0);//光源0を利用
  //-----------------------------------

  //球
  glPushMatrix();
  glMaterialfv(GL_FRONT, GL_AMBIENT, ms_ruby.ambient);
  glMaterialfv(GL_FRONT, GL_DIFFUSE, ms_ruby.diffuse);
  glMaterialfv(GL_FRONT, GL_SPECULAR, ms_ruby.specular);
  glMaterialfv(GL_FRONT, GL_SHININESS, &ms_ruby.shininess);
  glTranslated(0.0, 10.0, 20.0);//平行移動値の設定
  glutSolidSphere(4.0, 20, 20);//引数:(半径, Z軸まわりの分割数, Z軸に沿った分割数)
  glPopMatrix();

  //立方体
  glPushMatrix();
  glMaterialfv(GL_FRONT, GL_DIFFUSE, green);
  glTranslated(-20.0, -30.0, 20.0);//平行移動値の設定
  glutSolidCube(10.0);//引数:(一辺の長さ)
  glPopMatrix();

  //円錐
  glPushMatrix();
  glMaterialfv(GL_FRONT, GL_DIFFUSE, blue);
  glTranslated(20.0, 110.0, 0.0);//平行移動値の設定
  glutSolidCone(5.0,10.0,20,20);//引数:(半径, 高さ, Z軸まわりの分割数, Z軸に沿った分割数)
  glPopMatrix();
  
  //直方体
  glPushMatrix();
  glMaterialfv(GL_FRONT, GL_AMBIENT, ms_jade.ambient);
  glMaterialfv(GL_FRONT, GL_DIFFUSE, ms_jade.diffuse);
  glMaterialfv(GL_FRONT, GL_SPECULAR, ms_jade.specular);
  glMaterialfv(GL_FRONT, GL_SHININESS, &ms_jade.shininess);
  glTranslated(30.0, 40.0, 0.0);//平行移動値の設定
  glBegin(GL_QUADS);
  for (int j = 0; j < 6; ++j) {
    glNormal3dv(normal[j]); //法線ベクトルの指定
    for (int i = 0; i < 4; ++i) {
      glVertex3dv(vertex[face[j][i]]);
    }
  }
  glEnd();
  glPopMatrix();

  //陰影OFF-----------------------------
  glDisable(GL_LIGHTING);
  //-----------------------------------

  Ground();

  if(_Bitmap){
    ostringstream fname;
    int tt = tn +10000;
    fname  << "bitmap/" << tt << ".bmp" ;//出力ファイル名
    string name = fname.str();
    gs.screenshot(name.c_str(), 24);
  }
  tn++ ;

  glutSwapBuffers(); //glutInitDisplayMode(GLUT_DOUBLE)でダブルバッファリングを利用可
}
//----------------------------------------------------
// アイドル時に呼び出される関数
//----------------------------------------------------
void Idle(){
  glutPostRedisplay(); //glutDisplayFunc()を1回実行する
}

//----------------------------------------------------
// 大地の描画
//----------------------------------------------------
void Ground(void){
  double ground_max_x = 500.0;
  double ground_max_y = 500.0;
  glColor3d(0.8, 0.8, 0.8);  // 大地の色
  glBegin(GL_LINES);
  for(double ly = -ground_max_y ;ly <= ground_max_y; ly+=10.0){
    glVertex3d(-ground_max_x, ly,0);
    glVertex3d(ground_max_x, ly,0);
  }
  for(double lx = -ground_max_x ;lx <= ground_max_x; lx+=10.0){
    glVertex3d(lx, ground_max_y,0);
    glVertex3d(lx, -ground_max_y,0);
  }
  glEnd();
}

ソースの解説

OpenGLで描画したアニメーションをgifアニメやm1v(DVDの形式)の動画として記録するまでを解説します。

#include <GL/gl_screenshot.h> 

gl_screenshot gs; //bmpファイルの出力

「gl_screenshot.h」ファイルをインクルードし、gl_screenshot クラスのインスタンスを生成します。 (「gl_screenshot.h」ファイルについては、こちらを参照してください。)
描画されたウィンドウ内容をビットマップファイルに書き出したいところで、「gs.screenshot("ファイルの名前", 24);」と記述します。

    ostringstream fname;
    int tt = tn +10000;
    fname  << "bitmap/" << tt << ".bmp" ;//出力ファイル名
    string name = fname.str();
    gs.screenshot(name.c_str(), 24);

本講座では、描画関数「Display」の中で「gs.screenshot」を記述し、 描画関数「Display」が呼び出されるたびにファイル名を連続的に変更させます。 その結果1ステップごとにビットマップを書き出します。そのビットマップファイルをつなぎ合わせることで動画となります。 (おすすめ動画作成ツール:TMPGEnc

ちなみに、視点の回転は以下の部分で表現しています。

  t = dt * tn;
  ViewPointX = -100.0 * cos( omega * t);
  ViewPointY = -100.0 * sin( omega * t);

gl_screenshot.h

ウインドウの内容をビットマップファイルへ出力するためのプログラムは「OpenGL(Akita National College of Technology Yamamoto's Laboratory )」で公開されています。オリジナルは、「gl_screenshot.cpp」「gl_screenshot.h」で構成されていますが、本講座では2つの上記の2つのファイルを「gl_screenshot.h」にまとめて利用しています。

/*
このソースは「Akita National College of Technology Yamamoto's Laboratory」にて公開されています。
参考:http://akita-nct.jp/yamamoto/comp/OpenGL/OpenGL.html
*/
// 画面の内容をビットマップファイルに出力
//   filename: 出力ファイル名
//   bpp: ピクセルあたりのビット数(24, 32)

#ifndef GL_SCREENSHOT_H_INCLUDED
#define GL_SCREENSHOT_H_INCLUDED

#include <stdlib.h> 
#include <GL/glut.h>
#include <iostream>
#include <fstream>
#include <vector>

// ビットマップのヘッダ
struct BITMAPFILEHEADER_ {
  //char bfType1;
  //char bfType2;
  unsigned long  bfSize;
  unsigned short bfReserved1;
  unsigned short bfReserved2;
  unsigned long  bfOffBits;
};

struct BITMAPINFOHEADER_ {
    unsigned long  biSize;
    long           biWidth;
    long           biHeight;
    unsigned short biPlanes;
    unsigned short biBitCount;
    unsigned long  biCompression;
    unsigned long  biSizeImage;
    long           biXPixPerMeter;
    long           biYPixPerMeter;
    unsigned long  biClrUsed;
    unsigned long  biClrImporant;
};

class gl_screenshot {
public:
  gl_screenshot(){};
  ~gl_screenshot(){};
  bool screenshot(const char* filename, int bpp=32)
  {
    const int width    = glutGet(GLUT_WINDOW_WIDTH);   // 画像の大きさ
    const int height   = glutGet(GLUT_WINDOW_HEIGHT);
    // ピクセルデータ全体のサイズ
    const int datasize = height*((((width*bpp/8) + 3) >> 2) << 2);
    // ファイルサイズ
    const int filesize = 2 + sizeof(BITMAPFILEHEADER_) + sizeof(BITMAPINFOHEADER_) + datasize;

    // ビットマップのヘッダ
    BITMAPFILEHEADER_ bmfh = {filesize, 0, 0, 54,};
    BITMAPINFOHEADER_ bmih = {40, width, height, 1, bpp, 0, 0, 0, 0, 0, 0,};

    // データのフォーマット
    int format;
    if (bpp == 24) {
      format = GL_RGB;
    }
    else if (bpp == 32) {
      format = GL_RGBA;
    }
    else {
      std::cerr << "invalid parameter 'bpp'" << std::endl;
      return false;
    }

    // データをもらう
    std::vector<GLubyte> buf(datasize);
    //glPixelStorei(GL_PACK_ALIGNMENT, 1);
    //glReadBuffer(GL_FRONT);
    glReadPixels(0, 0, width, height, format, GL_UNSIGNED_BYTE, &buf[0]);

    // RGB → BGR
    for (int i=0; i<datasize; i+=bpp/8) {
      std::swap(buf[i], buf[i+2]);
    }

    // 出力
    std::ofstream fs(filename, std::ios::out | std::ios::trunc | std::ios::binary);
    if (!fs) {
      std::cerr << "fstream::open() failed." << std::endl;
      return false;
    }
    
    fs.write("BM", 2);
    fs.write(reinterpret_cast<const char*>(&bmfh), sizeof(BITMAPFILEHEADER_));
    fs.write(reinterpret_cast<const char*>(&bmih), sizeof(BITMAPINFOHEADER_));
    fs.write(reinterpret_cast<const char*>(&buf[0]), datasize);
    fs.close();

    return true;
  }
};
#endif

【目次】 (VisualC++ を使った OpenGL 入門)

未分類




タグ: ,

▲このページのトップNPO法人 natural science トップ

関連記事

OpenGL入門







▲このページのトップNPO法人 natural science トップ




Warning: mysqli_connect(): (28000/1045): Access denied for user 'xsvx1015071_ri'@'sv102.xserver.jp' (using password: YES) in /home/xsvx1015071/include/natural-science/include_counter-d.php on line 8
MySQL DBとの接続に失敗しました