OpenGLに関して



freeglut

freeglutについて

GLUTはversion3.7 (1998年) を最後として,それ以降開発されていません. freeglutはGLUT(OpenGL Utility Toolkit)の代替となるオープンソースのOpenGLツールキットであり,GLUTの命令と完全に互換性を持っています. GLUTからfreeglutに乗り換えるときは,単純にインクルードするファイルを変更するだけで済みます. 

ダウンロードとインストール

  • ビルド
    1. ダウンロードしたファイルを解凍
    2. (2.8.1以前) 解凍したフォルダのVisualStudio/2012/freeglut.slnがVS2012用のソリューションファイル.freeglut.slnを開いてビルドする.スタティックビルドの場合はプロジェクト構成で*_Staticというのを選択する.
    3. (3.0.0以降) CMakeをインストールし,CMake(GUI)で"Where is the source code"にfreeglutを解凍したフォルダ(srcフォルダではないので注意),"Where to build the binaries"にプロジェクトを作成するフォルダへのパスを入力し,"Configure"→ビルドオプション指定→"Generate"でプロジェクトを作成,ソリューションファイルを開いてビルドする.
  • インストール
    解凍フォルダ直下のinclude/GLフォルダ内にインクルードファイルがある.GLフォルダごとプロジェクトのインクルードディレクトリにコピーするか,includeフォルダをインクルードディレクトリとして設定する.
    ライブラリファイルは解凍フォルダ直下(CMakeを使った場合はプロジェクトを作成したフォルダ直下)のlibフォルダにある. ダイナミックライブラリの場合は,
    freeglut.dll freeglut.lib
    がRelease,Debugフォルダにできているので,適切なフォルダにコピーする.
    スタティックビルドの場合は,
    freeglut_static.lib
    が生成されている.こちらも適切なフォルダにコピーする.さらに,スタティックビルドの場合は,コンパイル時のプリプロセッサに
    FREEGLUT_STATIC
    を指定する.

TIPS

平面クリップ

平面クリップを行うには,glClipPlaneを用いる.

void glClipPlane(GLenum plane, const GLdouble *equation);

planeにはクリップ平面番号(GL_CLIP_PLANE0, GL_CLIP_PLANE1など)を設定する. 最大数は,GL_MAX_CLIP_PLANESを参照することで調べられる. equationには平面方程式(ax+by+cz+d=0)の係数(a,b,c,d)を格納した4要素配列を指定する. ax+by+cz+d<0の範囲がクリップされる.

平面クリップのON/OFFは,

glEnable(GL_CLIP_PLANE0);
glDisable(GL_CLIP_PLANE0);

マルチサンプリングによるアンチエリアシング

  • 描画する前にマルチサンプリングをONにする
    glEnable(GL_MULTISAMPLE);
  • GLUTの場合,glutInitDisplayModeにGLUT_MULTISAMPLEを追加.例えば,
    glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH | GLUT_MULTISAMPLE);
  • サンプリングの対応状況を調べる
    GLint buf, sbuf;
    glGetIntegerv(GL_SAMPLE_BUFFERS, &buf);
    cout << "number of sample buffers is " << buf << endl;
    glGetIntegerv(GL_SAMPLES, &sbuf);
    cout << "number of samples is " << sbuf << endl;
    buf,sbufが0ならばGPUが対応していない or ビデオカードの設定が適切でない可能性がある.
    NVIDIAのGPUの場合,
    1. NVIDIAコントロールパネルの3D設定の管理を開く.
    2. グローバル設定の「アンチエリアシング - モード」を「アプリケーション設定の変更」にする.
    3. 「アンチエリアシング - 設定」が選択できるようになっているので,2xや4xなどを設定する.
  • サンプリング本数をアプリケーションから設定する場合は, 上記「アンチエリアシング - モード」を「アプリケーションによるコントロール」にして, ピクセルフォーマットを手動で設定する必要がある. wglChoosePixelFormatARBを用いると設定は可能だがGLUTでは使えない.
    freeglutを用いている場合は,freeglutのソースコードをいじれば変更できる. 変更箇所は,
    • freeglut_init.c : 89行目の/* SampleNumber */のところを希望のサンプリング数に変更(デフォルトは4) 変更後ライブラリをビルドし直す.
      私はサンプリング設定を変えるたびにライブラリをビルドし直すのが面倒なので, プログラム実行時のコマンドラインオプションでサンプリング数を設定できるようにしてある. 例えば,
      glapp.exe -sample 8
      とすればサンプリング数が8となる. この設定のためには,freeglut_init.cの807行目あたりに
      else if( strcmp(argv[i], "-sample") == 0 )
      {
          if( ++i >= argc )
              fgError( "-sample parameter must be followed by sampling number" );
      
          fgState.SampleNumber = atoi(argv[i]);
      
          argv[i-1] = NULL;
          argv[i]   = NULL;
          (*pargc) -= 2;
      }
      を追加する.

DualViewなどでのフルスクリーン(Windows Only)

GLUT,freeglutではglutFullScreen関数で全画面表示できる. 標準ではマルチディスプレイの場合,スパン設定なら問題ないがDiualViewなどでは メイン画面上でしかフルスクリーンにならない. これは,DualViewではメインディスプレイのサイズがAPIが与える物理的な画面サイズとして用いられるためである. DualViewでは仮想的なスパン画面が設定されているので,これをglutFullScreenから参照されるようにしなければならない.

freeglutのソースコードを変更する.freeglut_init.c の fghInitialize関数内の

   fgDisplay.ScreenWidth  = GetSystemMetrics( SM_CXSCREEN );
   fgDisplay.ScreenHeight = GetSystemMetrics( SM_CYSCREEN );

の部分を

   fgDisplay.ScreenWidth  = GetSystemMetrics( SM_CXVIRTUALSCREEN );
   fgDisplay.ScreenHeight = GetSystemMetrics( SM_CYVIRTUALSCREEN );

とすればよい. glutFullScreen命令を呼んだときだけ変えたいときや別の関数を使ってやりたいときは, freeglut_window.c内のglutFullScreen関数の,

rect.left   = 0;
        rect.top    = 0;
        rect.right  = fgDisplay.ScreenWidth;
        rect.bottom = fgDisplay.ScreenHeight;

rect.left   = 0;
	rect.top    = 0;
	rect.right  = GetSystemMetrics(SM_CXVIRTUALSCREEN);
	rect.bottom = GetSystemMetrics(SM_CYVIRTUALSCREEN);

などとする. また,マルチディスプレイ用のフルスクリーン命令を新たに作りたい場合は, この関数をコピーして名前を変えて,上記の変更をすればよい. 新しい関数を追加した場合(例えば,glutFullScreenMultiとする)は,

  • freeglut_ext.hに関数宣言を追加
    FGAPI void    FGAPIENTRY glutFullScreenMulti( int left, int top, int right, int bottom );
  • freeglut_ext.cの最初の方の #define CHECK_NAME(x) に追加
       CHECK_NAME(glutFullScreenMulti);
  • freeglutdll.defに関数名(glutFullScreenMulti)を追加

また,任意のマルチディスプレイに対応するために, 関数の宣言を

void FGAPIENTRY glutFullScreenMulti( int left, int top, int right, int bottom )

として,上記の変更点を以下のようにする.

if(!left && !right && !top && !bottom){
			rect.left   = 0;
			rect.top    = 0;
			rect.right  = GetSystemMetrics(SM_CXVIRTUALSCREEN);
			rect.bottom = GetSystemMetrics(SM_CYVIRTUALSCREEN);
		}
		else{
			rect.left   = left;
			rect.top    = top;
			rect.right  = right;
			rect.bottom = bottom;
		}

関数を呼び出すときにフルスクリーンの範囲を指定する.

画像読み込み

OpenCVを利用

#pragma comment(lib, "cv120.lib")
#pragma comment(lib, "cxcore120.lib")
#pragma comment(lib, "cvaux120.lib")
#pragma comment(lib, "highgui120.lib")

#include "cv.h"
#include "cxcore.h"
#include "highgui/highgui.h"

/*!
 * OpenCVで画像読込み -> OpenGLテクスチャ登録
 * @param[in] fn ファイル名
 * @param[inout] tex_name テクスチャ名(0なら新たに生成)
 * @param[in] mipmap ミップマップ使用フラグ
 * @param[in] compress テクスチャ圧縮使用フラグ
 */
static int LoadGLTexture(const string &fn, GLuint &tex_name, bool mipmap, bool compress)
{
	IplImage* img = cvLoadImage(fn.c_str(), CV_LOAD_IMAGE_COLOR);

	if(!img){
		return 0;
	}

	GLuint iformat, format;
	int w, h, c;
	w = img->width;
	h = img->height;
	c = img->nChannels;

	// 画像フォーマット
	format = GL_BGRA;
	if(c == 1){
		format = GL_LUMINANCE;
	}
	else if(c == 3){
		format = GL_BGR;
	}

	// OpenGL内部の格納フォーマット
	if(compress){
		iformat = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
		if(c == 1){
			iformat = GL_COMPRESSED_LUMINANCE_ARB;
		}
		else if(c == 3){
			iformat = GL_COMPRESSED_RGB_S3TC_DXT1_EXT ;
		}
	}
	else{
		iformat = GL_RGBA;
		if(c == 1){
			iformat = GL_LUMINANCE;
		}
		else if(c == 3){
			iformat = GL_RGB;
		}
	}

	//glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

	// テクスチャ作成
	if(tex_name == 0){
		glGenTextures(1, &tex_name);

		glBindTexture(GL_TEXTURE_2D, tex_name);
		
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, (mipmap ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR));
		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);

		if(mipmap){
			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 6);
		}

		glTexImage2D(GL_TEXTURE_2D, 0, iformat, w, h, 0, format, GL_UNSIGNED_BYTE, img->imageData);

		if(mipmap){
			glGenerateMipmapEXT(GL_TEXTURE_2D);
		}
	}
	else{
		glBindTexture(GL_TEXTURE_2D, tex_name);
		glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, format, GL_UNSIGNED_BYTE, img->imageData);

		if(mipmap){
			glGenerateMipmapEXT(GL_TEXTURE_2D);
		}
	}

	glBindTexture(GL_TEXTURE_2D, 0);

	return 1;
}

ミップマップ作成にglGenerateMipmapEXTを使っている. これはもともとFBO拡張の一部であるが,FBOなしでも使え, ハードウェアによるアクセラレーションにも対応しているので新しめのGPUなら高速かもしれない. 代わりに,

gluBuild2DMipmaps(GL_TEXTURE_2D, iformat, w, h, format, GL_UNSIGNED_BYTE, img->imageData); 

を用いてもよい.

描画の画像保存

bool SaveFrameBuffer(const string &fn, int w, int h)
{
	int c = 3;
	vector<unsigned char> img_buf(w*h*c);

	int format = GL_BGRA;
	if(c == 3){
		format = GL_BGR;
	}

	glRasterPos2i(0, h);
	glReadPixels(0, 0, w, h, format, GL_UNSIGNED_BYTE, &img_buf[0]);

	IplImage *cvimg;
	cvimg = cvCreateImage(cvSize(w, h), IPL_DEPTH_8U, 3);

	for(int i = 0; i < w; ++i){
		for(int j = 0; j < h; ++j){
			int idx = 3*(w*(h-j-1)+i);
			//int idx = 3*(j*w+i);

			int r, g, b;
			r = img_buf[idx+0];
			g = img_buf[idx+1];
			b = img_buf[idx+2];

			SetPixel(cvimg, i, j, r, g, b);
		}
	}

	cvSaveImage(fn.c_str(), cvimg);

	return true;
}

リンク集

リファレンスなど

GUIライブラリ

拡張ライブラリ

3Dモデルダウンロード

  • 3DXtras : 著作権フリーのさまざまな3Dモデルが多数投稿されている.ダウンロードするには要登録.max,3dsなど
  • 3D CHAYA : 日本の城や刀など.OBJ,DXF,3DS.
  • 3D Total : 様々なモデル.STUFF->modelsからダウンロードできる.MAX,3DS,LWOなど
  • Rodluc2001 : 西洋の城など.OBJ,3DS,VOBなど
  • ホンダ ウェブプラモ : バイク,車メーカーのHondaが車やバイク,アシモのモデルを提供している.DXF形式.

トップ   編集 凍結 差分 履歴 添付 複製 名前変更 リロード   新規 一覧 検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2024-03-08 (金) 18:06:05