PNGでテクスチャ ― 2013/11/05
今回はタイトル通り、PNG画像の読み込みとテクスチャの設定・描画を行います。
PNGの読み込み、つまりはPNGデータを読み込んでテクスチャデータに展開するのですが、OpenGLやAndroid NDKにはそのような機能はありません。そこで、libpngというPNG読み込み(と書き込み)のライブラリを使用します。
ありがたいことにlibpngにはAndroid用がありますので、そちらを使わせていただきます。
libpng for Android
jni/sysの下に丸々コピーして、jni/Android.mkから jni/sys/libpng/jni/Android.mkをインクルードするようにします。これでlibpngがプロジェクトに組み込まれます。
それではテクスチャ管理クラス sys::Textureです。
PNGの読み込み部分(load_png)と、テクスチャの設定部分(make)があります。
Texture.h
Texture.cpp
・PNG読み込み(load_png)
大体は libpngの普通の使い方になっていますが、ちょっと変えているのは以下の2点。
libpngは基本的にデータをファイルから読み込むようになっていますが、メモリから読み込むように読み込みコールバックを差し替えています(76行目)。
また、ピクセルフォーマットを24ビットRGB(不透明)か、32ビットRGBA(α付き)に揃えています(84行目から)。
・テクスチャ設定(make)
glGenTexturesでテクスチャオブジェクトを作成して、glTexImage2Dでデータの設定という、これまた普通の使い方です。
GL_TEXTURE_WRAP_S、GL_TEXTURE_WRAP_Tを GL_CLAMP_TO_EDGEに設定していますが、こうするとOpenGL ES 2.0ではテクスチャの幅や高さが2の累乗でなくても良くなるそうです。
テクスチャ描画のために、新しくシェーダプログラムを追加しています。
Renderer.cpp
シェーダが増えたことで切り替え処理など他にも変更点はあるのですが、ここでは省略します.
最後に実施にテクスチャを描画する部分です。
AppMain.cpp
初期化部でテクスチャを読み込み、稼働部で描画しています。
描画は生ポリゴンの時の処理に、テクスチャのバインドとUV座標の設定が追加された位です。四角形の描画なので、トライアングルストリップ(GL_TRIANGLE_STRIP)を使用しています。
PNGの読み込み、つまりはPNGデータを読み込んでテクスチャデータに展開するのですが、OpenGLやAndroid NDKにはそのような機能はありません。そこで、libpngというPNG読み込み(と書き込み)のライブラリを使用します。
ありがたいことにlibpngにはAndroid用がありますので、そちらを使わせていただきます。
libpng for Android
jni/sysの下に丸々コピーして、jni/Android.mkから jni/sys/libpng/jni/Android.mkをインクルードするようにします。これでlibpngがプロジェクトに組み込まれます。
それではテクスチャ管理クラス sys::Textureです。
PNGの読み込み部分(load_png)と、テクスチャの設定部分(make)があります。
Texture.h
#ifndef ___TEXTURE_H___ #define ___TEXTURE_H___ #include "common.h" #include <GLES2/gl2.h> #include <GLES2/gl2ext.h> namespace sys { /**************** テクスチャ ****************/ class Texture { public : // ピクセルフォーマット enum { FORMAT_RGBA, FORMAT_RGB, }; GLuint texture; // テクスチャオブジェクト short format; // テクスチャフォーマット short width, height; // サイズ Texture(void) // コンストラクタ { texture = 0; } ~Texture() // デストラクタ { clear(); } void make(const u8*); // 作成 void clear(void); // 削除 void load_png(const u8*); // PNG読み込み }; } #endif /********************* End of File ********************************/
Texture.cpp
/*************************** テクスチャ ***************************/ #include "Texture.h" #include <libpng/jni/png.h> namespace sys { /***************************************** 作成 引数 data = テクスチャデータ *****************************************/ void Texture::make(const u8* data) { clear(); glGenTextures(1, &texture); // テクスチャオブジェクト作成 glBindTexture(GL_TEXTURE_2D, texture); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); switch ( format ) { case FORMAT_RGBA : glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); break; case FORMAT_RGB : glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data); break; } } /********** 削除 **********/ void Texture::clear(void) { if ( texture ) { glDeleteTextures(1, &texture); texture = 0; } } /******************************** メモリ読み込みコールバック ********************************/ static void png_read(png_structp png_ptr, png_bytep data, png_size_t length) { u8** _p = (u8**)png_get_io_ptr(png_ptr); // データポインタ memcpy(data, *_p, length); *_p += length; } /********************************** PNG読み込み 引数 data = PNGデータ **********************************/ void Texture::load_png(const u8* data) { png_structp _png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); assert(_png); png_infop _info = png_create_info_struct(_png); assert(_info); png_set_read_fn(_png, NULL, png_read); png_init_io(_png, (png_FILE_p)&data); png_read_info(_png, _info); png_uint_32 _width, _height; int _bit_depth, _color_type; png_get_IHDR(_png, _info, &_width, &_height, &_bit_depth, &_color_type, NULL, NULL, NULL); switch ( _color_type ) { case PNG_COLOR_TYPE_RGB : format = FORMAT_RGB; break; case PNG_COLOR_TYPE_PALETTE : png_set_palette_to_rgb(_png); png_read_update_info(_png, _info); png_get_IHDR(_png, _info, &_width, &_height, &_bit_depth, &_color_type, NULL, NULL, NULL); format = (_color_type == PNG_COLOR_TYPE_RGB) ? FORMAT_RGB : FORMAT_RGBA; break; case PNG_COLOR_TYPE_GRAY : png_set_gray_to_rgb(_png); png_read_update_info(_png, _info); format = FORMAT_RGB; break; case PNG_COLOR_TYPE_GRAY_ALPHA : png_set_gray_to_rgb(_png); png_read_update_info(_png, _info); format = FORMAT_RGBA; break; default : format = FORMAT_RGBA; break; } width = (short)_width; height = (short)_height; int _row_bytes = width*((format == FORMAT_RGBA) ? 4 : 3); u8* _buf = (u8*)memalign(4, _row_bytes*height); png_bytep _rows[height]; assert(_buf != NULL); for (int i = 0; i < height; i++) { _rows[i] = _buf + i*_row_bytes; } png_read_image(_png, _rows); make(_buf); // テクスチャ作成 png_destroy_read_struct(&_png, &_info, NULL); free(_buf); } } /**************** End of File ******************************************/
・PNG読み込み(load_png)
大体は libpngの普通の使い方になっていますが、ちょっと変えているのは以下の2点。
libpngは基本的にデータをファイルから読み込むようになっていますが、メモリから読み込むように読み込みコールバックを差し替えています(76行目)。
また、ピクセルフォーマットを24ビットRGB(不透明)か、32ビットRGBA(α付き)に揃えています(84行目から)。
・テクスチャ設定(make)
glGenTexturesでテクスチャオブジェクトを作成して、glTexImage2Dでデータの設定という、これまた普通の使い方です。
GL_TEXTURE_WRAP_S、GL_TEXTURE_WRAP_Tを GL_CLAMP_TO_EDGEに設定していますが、こうするとOpenGL ES 2.0ではテクスチャの幅や高さが2の累乗でなくても良くなるそうです。
テクスチャ描画のために、新しくシェーダプログラムを追加しています。
Renderer.cpp
{ // SHADER_TEXTURE(テクスチャ有り) static const char gVertexShader[] = // 頂点シェーダプログラム "attribute vec4 position;" "attribute vec4 color;" "varying vec4 vColor;" "attribute vec2 texcoord;" "varying vec2 vTexcoord;" "uniform mat4 projection;" "void main() {" "gl_Position = projection*position;" "vColor = color;" "vTexcoord = texcoord;" "}"; static const char gFragmentShader[] = // フラグメントシェーダプログラム "precision mediump float;" "varying vec4 vColor;" "varying vec2 vTexcoord;" "uniform sampler2D texture;" "void main() {" "gl_FragColor = texture2D(texture, vTexcoord)*vColor;" "}"; vert_shader = loadShader(GL_VERTEX_SHADER, gVertexShader); // 頂点シェーダ frag_shader = loadShader(GL_FRAGMENT_SHADER, gFragmentShader); // フラグメントシェーダ shader[SHADER_TEXTURE].init(vert_shader, frag_shader, TRUE); // シェーダプログラム作成 glDeleteShader(vert_shader); glDeleteShader(frag_shader); }
シェーダが増えたことで切り替え処理など他にも変更点はあるのですが、ここでは省略します.
最後に実施にテクスチャを描画する部分です。
AppMain.cpp
#include "common.h" #include "Renderer.h" #include "Texture.h" static sys::Texture texture; // テクスチャ /************ 初期化 ************/ void init_app(void) { u8* _buf = (u8*)sys::load_asset("photo.png"); // テクスチャ用PNG読み込み texture.load_png(_buf); // テクスチャ設定 free(_buf); } /****************************** 稼働 戻り値 アプリ続行か ******************************/ Bool update_app(void) { static const float texcoord[] = { 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f }; static const float vertex[] = { -320.0f, -480.0f, 320.0f, -480.0f, -320.0f, 480.0f, 320.0f, 480.0f, }; static const GLubyte color[] = { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, }; sys::ShaderProgram* _shader; _shader = sys::Renderer::use_shader(sys::Renderer::SHADER_TEXTURE); // テクスチャ用シェーダ glBindTexture(GL_TEXTURE_2D, texture.texture); // テクスチャ glVertexAttribPointer(_shader->texcoord, 2, GL_FLOAT, GL_FALSE, 0, texcoord); // UV座標 glVertexAttribPointer(_shader->position, 2, GL_FLOAT, GL_FALSE, 0, vertex); // 頂点座標 glVertexAttribPointer(_shader->color, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, color); // カラー glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); return TRUE; } /**************** End of File *************************************************/
初期化部でテクスチャを読み込み、稼働部で描画しています。
描画は生ポリゴンの時の処理に、テクスチャのバインドとUV座標の設定が追加された位です。四角形の描画なので、トライアングルストリップ(GL_TRIANGLE_STRIP)を使用しています。
プロジェクト一式はこちらです。
最近のコメント