このサイトでは、分析、カスタマイズされたコンテンツ、および広告に Cookie を使用します。このサイトを引き続き閲覧すると、Cookie の使用に同意するものと見なされます。
Hi, Developers,
straightapps.com
作成日 October 31, 2018
トップページ > Android 開発トップ > OpenGLES で2Dテクスチャを作成する

ここでは、OpenGLES で、BMP 画像から2Dテクスチャを作成する方法について、検討しています。

Windows では、BMP 画像はメモリに読み込んで BitBlt 関数等で画面(Window DC)に描画します(OpenGL 等を使用しない場合)。 画像の1ドットは基本的にウィンドウ(画面)の1ピクセルに対応し、 画面サイズも多種多様ではなくある程度限られているので、一度に表示したい内容はある程度のサイズで構成して、 必要に応じてスクロールバーで調整する、という形です(いずれも、UWP アプリではない、デスクトップアプリの場合)。

Android では、デバイスが多様で、画面解像度も画面サイズ(ピクセル数)も想定できないので、 直接座標指定はしない、画像も画面1ピクセルに対して1ドットに対応させません。

また、描画タイミング毎回、全画面再描画であり、DIB に基本画像を残しておいて、部分的に書き換えて描画、 みたいなことはしないようです。

ここでは、BMP 画像から2Dテクスチャを作成する方法について、検討しています。
なお、OpenGLES は3Dの描画にも対応しているようですが、ここでは2Dのみ扱います。

このページ、および開発関連ページは、PC向けデザインとなっております。 画面サイズの小さいスマホでは、快適な表示が得られませんので、ご了承ください。
ご利用に際しては、必ず
プライバシーポリシー(免責事項等)をご参照ください。
また、本サイトが初めての方は、まずこのページの注意事項をご覧ください。

2Dテクスチャ作成処理の流れ

投稿 October 31, 2018

2Dテクスチャを作成するための手順を、まずまとめておきます。
gl で始まる関数は OpenGLES の関数で、ここを参考にしています。

まず、システムから、未使用のテクスチャIDを取得します。
下記の m_textireID は GLuint 型、すなわち符号なし整数値で、未使用のテクスチャIDが設定されます。 1未満の値が設定されたときは、空きテクスチャIDがない、という意味です。

	glGenTextures(1, &m_textureID);

空きテクスチャIDを取得したら、それをバインド(アクティブに・選択状態に)します。

	glBindTexture(GL_TEXTURE_2D, m_textureID);

テクスチャの描画方法等を指定します。あとで実行でもいいのかも知れません。 詳しくは、setParamsRGBA 関数をご覧ください。

画像イメージを読み込みます。詳細は「OpenGLES:24 ビット BMP 画像を読み込む」に書いていますが、 m_width に画像の幅、m_height に画像の高さ、m_bits にビットデータが設定されているものとします。

glTexImage2D 関数を呼び出して、2次元のテクスチャを作成します。

	glTexImage2D(
		GL_TEXTURE_2D,			// GLenum、または GL_PROXY_TEXTURE_2D
		0,				// GLint、詳細レベル番号
		GL_RGBA,			// GLint、テクスチャ内のカラー要素数
		m_width,			// GLsizei、幅(ピクセル単位、2のべき乗であること)
		m_height,			// GLsizei、高さ(同)
		0,				// GLint、テクスチャの境界幅(0または1)
		GL_RGBA,			// GLenum、描画時のフォーマット?
		GL_UNSIGNED_BYTE,		// GLenum、データ型?
		m_bits				// const GLvoid*、テクスチャに読み込む画像データ
	);
▲ページ先頭へ

テクスチャ描画方法の指定

投稿 October 31, 2018

テクスチャ作成前に、以下の関数を、順次実行しています。
まずは、1ピクセルが4バイトであることを設定しています。

	glPixelStorei(GL_UNPACK_ALIGNMENT, 4);

テクスチャの拡大・縮小には、近似カラーを使用してもらいます。

	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);

テクスチャのサイズが不足している時は、リピート描画します(どう使われるのかはわかりません)。

	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);

透過処理を使用する設定を行います。
設定がよくわからず、いろいろな値の組み合わせを試しましたが、これで希望通りの描画になりました。

	glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);

bool CaxTexture::setParamsRGB(void) として、この一連の関数を実行するようにしています。

なお、glTexEnvi 関数だけ、version 1.1 に定義されているようでした。

▲ページ先頭へ

テクスチャ管理クラス

投稿 October 31, 2018

アプリ起動中ずっと使用する画像などは、起動時のスプラッシュ画面で読み込んでおけばいいのですが、 デバイスの限られたメモリを考慮すると、一時的に使う画像は、その都度読み込み、解放したほうがよいと思われます。

そこで、1テクスチャを管理するクラスを作成し、それを複数持って、テクスチャ全体を管理しようと考えました。
1テクスチャを管理するクラスのクラス名を CaxTexture として、次のような定義にしました。

class CaxTexture
{
public:

	/** コンストラクター */
	CaxTexture();

	/** デストラクター */
	~CaxTexture();

	/** イメージが読み込み済みなら true を返します。 */
	bool isImageLoaded(void);

	/** テクスチャ画像の幅を返します。 */
	int32_t getWidth(void) {
		return m_width;
	}

	/** テクスチャ画像の高さを返します。 */
	int32_t getHeight(void) {
		return m_height;
	}

	/**
	* テクスチャIDが未設定のとき、設定します。
	*    glGenTextures 関数を呼び出します。
	*    すでに設定済みのときは、何もしません。
	*    設定されたテクスチャIDを返します(0のとき失敗です)。
	*/
	GLuint generateID(void);

	/**
	* テクスチャIDをバインドします。
	*    glBindTexture( GL_TEXTURE_2D ) でバインドします。
	*    テクスチャIDが無効のときは、何もしません(falseを返します)。
	*/
	bool bind(void);

	/**
	* RGBA画像用パラメータを設定します。
	*    現在バインドされているテクスチャに対応します。
	*    テクスチャIDが無効のときは、何もしません(falseを返します)。
	*/
	bool setParamsRGBA(void);

	/** assets から指定のBMP画像を読み込み、RGBAデータとしてメモリに格納します。 */
	void loadBmpRGBA(AAssetManager* AssetManager, const char* filename);

	/** assets から指定のPNG画像を読み込み、RGBAデータとしてメモリに格納します。 */
	void loadPngRGBA(AAssetManager* AssetManager, const char* filename);

	/**
	* 読み込んだイメージで、2Dテクスチャを作成します。
	*    テクスチャIDが無効のときは、何もしません(falseを返します)。
	*    また、イメージが未設定のときも、何もしません(falseを返します)。
	*/
	bool createImage2D(void);

	/**
	* 読み込んだイメージデータを破棄します。
	*    作成されたテクスチャは破棄されません。
	*    テクスチャを破棄するには、invalidateTextureID を呼び出します。
	*/
	void deleteBits(void);

	/**
	* 管理しているテクスチャIDをクリアします。
	*    メモリにあるテクスチャが解放されます。
	*/
	void invalidateTextureID(void);

protected:

	/** テクスチャID */
	GLuint m_textureID;

	/** 画像データビット */
	GLubyte	*m_bits;

	/** 読み込み済みかどうか */
	bool m_loaded;

	/** テクスチャ画像のサイズ(ピクセル) */
	int32_t m_width, m_height;
};
▲ページ先頭へ

上位クラスからのアクセス

投稿 October 31, 2018

クラスの実装に触れる前に、どのように呼び出して使用するかについて、記述します。

CaxTexture クラスのオブジェクト texture に、assets から指定のビットマップファイルを読み込む上位の関数は、次のようになっています。

bool CScreen::loadBmpTexture(struct android_app* app, CaxTexture& texture, const char* filename)
{
	if (texture.generateID() < 1) {					// テクスチャIDを取得
		return false;
	}
	if (!texture.isImageLoaded()) {					// テクスチャ作成前の場合のみ
		texture.bind();
		texture.setParamsRGBA();

		AAssetManager* mgr = app->activity->assetManager;	// アセットマネージャーを取得
		texture.loadBmpRGBA(mgr, filename);

		texture.createImage2D();
	}
	return true;
}
▲ページ先頭へ

テクスチャ管理クラス:コンストラクターとデストラクター

投稿 October 31, 2018

コンストラクターでは、各パラメーターを初期化しています。

CaxTexture::CaxTexture()
	: m_textureID(0)				// テクスチャID
	, m_bits(NULL)					// 画像ビットデータ
	, m_loaded(false)				// 読み込み済みかどうか
	, m_width(0)
	, m_height(0)
{
}

デストラクターでは、テクスチャが解放されていることを保証しています。

CaxTexture::~CaxTexture()
{
	deleteBits();					// 念のため確保したメモリを解放
	invalidateTextureID();				// テクスチャを破棄
}
▲ページ先頭へ

テクスチャ管理クラス:テクスチャIDとメモリの管理

投稿 October 31, 2018

generateID 関数は、テクスチャID未取得の場合、システムから空きテクスチャIDを取得します。
コンストラクターで、m_textureID を0にしていますので、その場合に限り、空きテクスチャIDを要求します。 取得できたら、その値を m_textureID に設定します。すでにテクスチャIDを取得済みの場合は、何もしません。

GLuint CaxTexture::generateID(void)
{
	if (m_textureID < 1) {					// 未設定の時のみ
		glGenTextures(1, &m_textureID);			// 空きテクスチャIDを取得
		if (m_textureID < 1) {				// 空きがないとき
			return 0;				// 失敗します
		}
	}
	return m_textureID;
}

isImageLoaded 関数は、テクスチャがすでに作成されているかどうかを返します。
コンストラクターで、m_loaded を false に設定していますので、初期状態では false が返ります。 テクスチャが作成されたとき、m_loaded が true に設定され、この関数は true を返します。

bool CaxTexture::isImageLoaded(void)
{
	return m_loaded;
}

bind 関数は、クラスが保持しているテクスチャID m_texture をバインド(選択)します。

bool CaxTexture::bind(void)
{
	if (m_textureID < 1) {					// テクスチャIDが未設定?
		return false;
	}
	glBindTexture(GL_TEXTURE_2D, m_textureID);
	return true;
}

createImage2D 関数は、画像イメージのビットデータ m_bits とサイズ情報 m_width, m_height を用いて、 2次元テクスチャを作成します。

bool CaxTexture::createImage2D(void)
{

	if (m_textureID < 1) {					// テクスチャIDが未設定?
		return false;
	}

	// イメージが読み込まれていない場合は、テクスチャを作成できません。
	if (!m_bits) {
		return false;
	}

	// 2次元のテクスチャを作成します。
	glTexImage2D(
		GL_TEXTURE_2D,			// GLenum、または GL_PROXY_TEXTURE_2D
		0,				// GLint、詳細レベル番号
		GL_RGBA,			// GLint、テクスチャ内のカラー要素数
		m_width,			// GLsizei、幅(ピクセル単位、2のべき乗であること)
		m_height,			// GLsizei、高さ(同)
		0,				// GLint、テクスチャの境界幅(0または1)
		GL_RGBA,			// GLenum、描画時のフォーマット?
		GL_UNSIGNED_BYTE,		// GLenum、データ型?
		m_bits				// const GLvoid*、テクスチャに読み込む画像データ
	);

	// テクスチャは作成されたと考えられます。
	m_loaded = true;

	// ビット情報はもう不要なので削除します。
	deleteBits();

	return true;
}

deleteBits 関数は、ビットデータのために確保した m_bits のメモリを解放します。

void CaxTexture::deleteBits(void)
{
	if (m_bits) {
		delete[] m_bits;
		m_bits = NULL;
	}
}

deleteBits 関数は、ビットデータを破棄しますが、テクスチャは破棄されません。 テクスチャを破棄するには、このinvalidateTextureID 関数を呼び出す必要があります。

void CaxTexture::invalidateTextureID(void)
{
	if (m_textureID > 0) {
		glDeleteTextures(1, &m_textureID);
		m_textureID = 0;
	}

	m_loaded = false;
}
▲ページ先頭へ

テクスチャ管理クラス:BMP 画像の読み込み

投稿 October 31, 2018

loadBmpRGBA 関数は、assets に入れた BMP ファイルをメモリに読み込み、解析して、CaxTexture クラスの m_width と m_height に画像のサイズを、 m_bits に(必要なサイズのメモリを確保して)画像のビットデータをセットするものです。

詳細は、「OpenGLES:24 ビット BMP 画像を読み込む」をご覧ください。

なお、CaxTexture クラスには loadPngRGBA という関数も用意しています。これは、BMP 画像ではなく PNG 画像を読み込んでテクスチャを作成するものです。 PNG 画像を読めないと、透過(アルファチャンネル)付き画像を扱えないので、必須と言っても過言ではないのですが・・・ これがなかなか厄介ですので、ここでは触れません。

▲ページ先頭へ


関連トピックス

OpenGLES:24 ビット BMP 画像を読み込む
assets に置いた BMP ファイルを読み込み、解析する方法を検討しています。


その他のおすすめ
おすすめ記事はありません。

JavaScriptが無効です
▲ページ先頭へ

© 2017-2018 StraightApps.com 無断転載を禁じます。No reproduction without permission.