テクスチャマッピング
サイズが1024×1024の画像をOpenGLで画面に表示するために、
1024×1024の長方形の平板を作る。
テクスチャを作ってそこに画像データを流し込み、平板に貼り付ける(テクスチャマッピング)。
視点を平板の中央真上に、視線の向きを平板の法線に固定する(モデルビュー行列の設定)。
平板がコントロール(ウィンドウ)に収まるように表示する範囲を決める(プロジェクション行列の設定)。
をする。
1~3はリンク先の記事に記載しているのでこの記事では触れない。
この記事では4と5、特に5のテクスチャマッピングに焦点を当てる。
1. テクスチャを作る
1つあれば事足りるが、美術館のように絵をいくつも並べてみたいので
4つ作ってみる。
const int N_TEX = 4 ;
unsigned int* textureNames = new unsigned int[N_TEX];
memset( textureNames, 0x00, sizeof( unsigned int ) * N_TEX );
glGenTextures( N_TEX, textureNames );
// glReadPixels()
glPixelStorei( GL_UNPACK_ALIGNMENT, 4 );
2. テクスチャに画像を流し込む
作成したテクスチャに、サイズ width × height の画像データ imageData を流し込む。
imageData は画像の各ピクセルのRGB値の配列で、R、G、Bに1バイトずつ使用しているので、glTexImge2Dの7番目、8番目の引数に GL_RGB、GL_UNSIGNED_BYTE を指定している。
void SetImageToTexture( unsigned int textureName,
int width, int height, const void* imageData )
{
glBindTexture( GL_TEXTURE_2D, textureName );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP );
glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0,
GL_RGB, GL_UNSIGNED_BYTE, data );
glBindTexture( GL_TEXTURE_2D, 0 );
}
3. 平板を作る
XY平面上にサイズ width × height の長方形(平板)を作る。
長方形の左下隅を原点にする。
画像をテクスチャとして貼り付けるので、長方形の各頂点にUV座標を設定しておく。
画像の大きさと平板の大きさをそろえているので、単純に四隅のUV値を( 0, 0 ), ( 1, 0 ), ( 1, 1 ), ( 0, 1 ) にすれば平板全体に画像が表示されアスペクト比も維持される。
void CreateBoard( int width, int height, float zValue = 0 )
{
glBegin( GL_QUADS );
glNormal3f( 0, 0, 1 );
glTexCoord2f( 0, 0 );
glVertex3f( 0, 0, zValue );
glTexCoord2f( 1, 0 );
glVertex3f( width, 0, zValue );
glTexCoord2f( 1, 1 );
glVertex3f( width, height, zValue );
glTexCoord2f( 0, 1 );
glVertex3f( 0, height, zValue );
glEnd();
}
4. 平板にテクスチャを貼り付ける
上で作った関数を利用して、テクスチャを貼り付けた平板を作る。
一度平板を作ったらその形や大きさ、貼り付けたテクスチャを変えることはないので、ディスプレイリストにしておく。
unsigned int CreateImageBoardByUsingDisplayList(
unsigned int textureName, int width, int height )
{
unsigned int displayList = glGenLists( 1 );
glNewList( displayList, GL_COMPILE );
glBindTexture( GL_TEXTURE_2D, textureName );
CreateBoard( width, height );
glEndList();
return displayList;
}
まとめ
以上の1~4の処理をまとめると以下のようなコードになる。
1と2の間の glPixelStorei は適切に設定すれば、glTexImage2D で OpenGL に画像データを渡す時の処理効率を上げる効果がある。画像データは1ピクセルRGBの3バイトなので「3」を指定できればおそらく効率最高になるが、1、2、4、8 しか指定できない。3の約数は1だけなので、今回は1を指定せざるを得ない。
// 1. テクスチャを作る
const int N_TEX = 4 ;
unsigned int* textureNames = new unsigned int[N_TEX];
memset( textureNames, 0x00, sizeof( unsigned int ) * N_TEX );
glGenTextures( N_TEX, textureNames );
// OpenGLに、画像データがメモリ上にどう格納されているか教える
glPixelStorei( GL_UNPACK_ALIGNMENT, 1 );
// 2. テクスチャに画像を流し込む
SetImageToTexture( textureNames[0], width, height, imageData );
// 3. 平板を作ってテクスチャを貼り付ける
unsigned int displayList
= CreateImateBordByUsingDisplayList( textureNames[0], width, height );
// 4. 描画する
glCallList( displayList );