OpenGLサンプル テクスチャ
OpenGLでテクスチャを表示する手順です。複数枚でブレンディングを有効にして表示してみます。
画像を読み込む
代表的な読み込み方は
・ライブラリを利用して直接読み込む( OpenCVのcvLoad, linpng, libjpgなど。または自作するなど)
・フレームワーク付属のクラスを利用してraw dataを取り出す。
ここでは読み込んだと仮定して、画像を自動生成することにします。
画像のデータをまとめてグローバルに定義しておきます。
static const int RGB = 3; static const int RGBA = 4; struct image_t{ int width; int height; int channels; void*buffer; ~image_t(){ if(buffer)::free(buffer); } }; image_t image1 = {64, 64, RGB, 0}; image_t image2 = {64, 64, RGBA, 0}; // ブレンディング対象
画像の読み込みの代わりの関数を作成します。左上端に目印を付けて表示が正しいか判断することにします。画像データの配列は、R,G,BあるいはR,G,B,Aの順で作成します。読み込むときも同様なので、BGR型の配列を読み込んだときは順序を変換しないといけません。
void *loadTexture(int width, int height, int channels, const char* filePathDummy){ // 画像を読み込む(ここでは仮として自動生成する) typedef unsigned char* iterator_t; int image_size = width * height * channels * sizeof(unsigned char); void *image = ::malloc( image_size ); // nullチェック略 iterator_t itr_dest = iterator_t(image); iterator_t itr_dest_end = itr_dest + image_size; // ストライプ作成 int pixel = 0; int stride = 3; while (itr_dest != itr_dest_end) { *itr_dest++ = 255-50 * pixel ; *itr_dest++ = 255-50 * pixel ; *itr_dest++ = 255-10 * pixel ; if (channels == RGBA) { *itr_dest++ = 100; // 半透明 } pixel = ++pixel % stride; } // 上左端に目印をつける itr_dest = iterator_t(image); for(int i = 0 ; i < width / 3; i++){ *itr_dest++ = 50; // cyan *itr_dest++ = 255; *itr_dest++ = 255; if (channels == RGBA) { *itr_dest++ = 100; } } return image; }
int main(int argc, char **argv){ //略 // load image data image1.buffer = loadTexture(image1.width, image1.height, image1.channels, "icon1.png"); image2.buffer = loadTexture(image2.width, image2.height, image2.channels, "icon2.png");
テクスチャの管理
各テクスチャはGLuint型の番号で管理します。複数枚のときはGLuint型の配列を用意しておきます。こちらも画像とセットでグローバルに定義しておきます。
const int TEXTURES = 2; GLuint textureArray[TEXTURES];
テクスチャ領域の確保と解放
glGenTextures・glDeleteTexturesを使います。
int main(int argc, char **argv){ //略... glEnable(GL_TEXTURE_2D); glGenTextures( TEXTURES, textureArray ); // ... } void onExit(){ glDeleteTextures(TEXTURES, textureArray); glDisable(GL_TEXTURE_2D); }
先ほどの配列と対応した領域が内部に作成されます。今後特定のテクスチャを指定するときはGLuint型の番号textureArray[i]を用います。特定のテクスチャに関する処理を実行するときも引数にGLuint型の番号を入れます。
テクスチャの作成
glBindTextureで現在のテクスチャを指定、glPixelStorei、glTexParameterf、glTexEnviなどでテクスチャの各パラメータを指定、glTexImage2Dで画像からテクスチャを作成します。glTexImage2Dのの引数formatには内部フォーマットを、次の引数formatには画像のフォーマットを指定します。ブレンド処理に対応させるときは双方にGL_RGBAを入れておきます。
void textureFromImage(GLuint textureId, image_t* image){ // IDによって現在のテクスチャを指定する glBindTexture(GL_TEXTURE_2D, textureId ); // パラメータ設定(例) glPixelStorei(GL_UNPACK_ALIGNMENT, 1); glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_NEAREST ); glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); // texture environment glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); // 画像からテクスチャ作成 GLenum format = GL_RGB; if(image->channels == RGBA){ format = GL_RGBA; // 4channelのときはGL_RGBAを引数にする } glTexImage2D(GL_TEXTURE_2D, 0, format, image->width, image->height, 0, format, GL_UNSIGNED_BYTE, image->buffer ); }
テクスチャの描画
glVertexPointerで描画位置を指定、glTexCoordPointerでテクスチャマッピング座標を指定します。その後にglDrawArraysで描画します。テクスチャマッピングを行う前に、glBindTextureでどのテクスチャに適用するのか指定しておきます。
void drawTexture(GLuint textureId, float x, float y, float width, float height ){ static const int VERTEX_DIMENSION = 2; static const int NUM_VERTICES = 4; // 描画位置 float vertexArray[ ] = { x, y, // 左下 x + width, y, // 右下 x, y + height, // 左上 x + width, y + height // 右上 }; glEnableClientState(GL_VERTEX_ARRAY); glVertexPointer( VERTEX_DIMENSION, GL_FLOAT, 0, vertexArray ); // texture mapping座標 static const float textureCoordArray[] = { 0.0f, 1.0f, // 左下 1.0f, 1.0f, // 右下 0.0f, 0.0f, // 左上 1.0f, 0.0f, // 右上 }; glEnable(GL_TEXTURE_2D); glEnableClientState(GL_TEXTURE_COORD_ARRAY); glBindTexture(GL_TEXTURE_2D, textureId ); glTexCoordPointer(2, GL_FLOAT, 0, textureCoordArray); // 描画 glDrawArrays(GL_TRIANGLE_STRIP, 0, NUM_VERTICES); glDisableClientState(GL_TEXTURE_COORD_ARRAY); glDisable(GL_TEXTURE_2D); }
ブレンディングの設定
ブレンディングの種類を予め指定しておきます。
glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
以上で、画像1はそのまま、画像2が半透明で描画されます。
(参考)サンプル全体のコード。
#include <stdlib.h> #include <GLUT/glut.h> // OSX上で実行 // テクスチャ const int TEXTURES = 2; GLuint textureArray[TEXTURES]; // 画像 static const int RGB = 3; static const int RGBA = 4; struct image_t{ int width; int height; int channels; void*buffer; ~image_t(){ if(buffer){ ::free(buffer);} } }; image_t image1 = {64, 64, RGB, 0}; image_t image2 = {64, 64, RGBA, 0}; // ブレンディング対象 // 関数 void *loadTexture(int width, int height, int channels, const char* filePath = ""); void drawTexture(GLuint textureId, float x, float y, float width, float height ); void textureFromImage(GLuint textureId, image_t* image); void drawScene(); void onExit(); int main(int argc, char **argv){ glutInit( &argc, argv ); glutInitDisplayMode( GLUT_RGB | GLUT_DOUBLE ); glutInitWindowSize(500, 500); glutCreateWindow( "glut window" ); glutDisplayFunc( drawScene ); ::atexit(onExit); // init openGL glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // load image data image1.buffer = loadTexture(image1.width, image1.height, image1.channels, "icon1.png"); image2.buffer = loadTexture(image2.width, image2.height, image2.channels, "icon2.png"); // generate textures glEnable(GL_TEXTURE_2D); glGenTextures( TEXTURES, textureArray ); // convert image to texture GLuint textureID1 = textureArray[0]; GLuint textureID2 = textureArray[1]; textureFromImage(textureID1, &image1); textureFromImage(textureID2, &image2); glutMainLoop(); return 0; } void onExit(){ glDeleteTextures(TEXTURES, textureArray); glDisable(GL_TEXTURE_2D); glDisable(GL_BLEND); } // 描画 void drawScene(){ glClearColor( 0.0f, 0.0f, 0.0f, 1.0f); glClear( GL_COLOR_BUFFER_BIT ); // draw textues GLuint textureID1 = textureArray[0]; GLuint textureID2 = textureArray[1]; drawTexture(textureID1, 0.1f, 0.1f, 0.2f, 0.2f); drawTexture(textureID2, -0.3f, -0.3f, 0.5f, 0.5f); glutSwapBuffers(); } // テクスチャ描画 void drawTexture(GLuint textureId, float x, float y, float width, float height ){ static const int VERTEX_DIMENSION = 2; static const int NUM_VERTICES = 4; // 描画位置 float vertexArray[ ] = { x, y, // 左下 x + width, y, // 右下 x, y + height, // 左上 x + width, y + height // 右上 }; glEnableClientState(GL_VERTEX_ARRAY); glVertexPointer( VERTEX_DIMENSION, GL_FLOAT, 0, vertexArray ); // texture mapping座標 static const float textureCoordArray[] = { 0.0f, 1.0f, // 左下 1.0f, 1.0f, // 右下 0.0f, 0.0f, // 左上 1.0f, 0.0f, // 右上 }; glEnable(GL_TEXTURE_2D); glEnableClientState(GL_TEXTURE_COORD_ARRAY); glBindTexture(GL_TEXTURE_2D, textureId ); glTexCoordPointer(2, GL_FLOAT, 0, textureCoordArray); // 描画 glDrawArrays(GL_TRIANGLE_STRIP, 0, NUM_VERTICES); glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_TEXTURE_COORD_ARRAY); glDisable(GL_TEXTURE_2D); } void textureFromImage(GLuint textureId, image_t* image){ // IDによって現在のテクスチャを指定する glBindTexture(GL_TEXTURE_2D, textureId ); // パラメータ設定(例) glPixelStorei(GL_UNPACK_ALIGNMENT, 1); glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); // 画像からテクスチャ作成 GLenum format = GL_RGB; if(image->channels == RGBA){ format = GL_RGBA; // 4channelのときはGL_RGBAを引数にする } glTexImage2D(GL_TEXTURE_2D, 0, format, image->width, image->height, 0, format, GL_UNSIGNED_BYTE, image->buffer ); } // 画像を読み込む(ここでは仮として自動生成する) void *loadTexture(int width, int height, int channels, const char* filePathDummy){ typedef unsigned char* iterator_t; int image_size = width * height * channels * sizeof(unsigned char); void *image = ::malloc( image_size ); // nullチェック略 iterator_t itr_dest = iterator_t(image); iterator_t itr_dest_end = itr_dest + image_size; // ストライプ作成 int pixel = 0; int stride = 3; while (itr_dest != itr_dest_end) { *itr_dest++ = 255-50 * pixel ; // r *itr_dest++ = 255-50 * pixel ; // g *itr_dest++ = 255-10 * pixel ; // b if (channels == RGBA) { *itr_dest++ = 100; // a:半透明 } pixel = ++pixel % stride; } // 上左端に目印をつける itr_dest = iterator_t(image); for(int i = 0 ; i < width / 3; i++){ *itr_dest++ = 50; // cyan *itr_dest++ = 255; *itr_dest++ = 255; if (channels == RGBA) { *itr_dest++ = 100; } } return image; }