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)を使用しています。

プロジェクト一式はこちらです。
最近のコメント