Android NDK + OpenGL ES でのテクスチャ描画

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
black_cat

Android NDK + OpenGL ES でのテクスチャ描画

#1

投稿記事 by black_cat » 13年前

こんにちは。
朝晩はだいぶ冷え込むようになってきましたね。
心地よい秋はすぐに通り過ぎてしまいます;

季節の話題はさておき本題に入らせていただきますと、今回のトピックは「Android NDKでOpenGLを使ったテクスチャの描画がどうしてもうまくいかない」という問題の解決方法をご教授いただきたく、立てさせていただきました。

まず、おおまかな処理の流れとしては、
yspngというライブラリでpng画像をSDから読み込み→OpenGLのテクスチャ生成→描画
といった感じです。
※yspngはPNGファイルのデコーダーとして使用します。

次に現在のコードを掲載します。(Javaのimport等は省略しています)
################################################################################
# Java側
################################################################################
・MainActivity.java

コード:

public class MainActivity extends Activity {
	//MyGLViewのインスタンス
	private MyGLView myGLView;
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		//ウィンドウタイトルバーを非表示に
		requestWindowFeature(Window.FEATURE_NO_TITLE);
		//GLSurfaceViewを生成&セット
		myGLView = new MyGLView(this);
		setContentView(myGLView);
	}

	@Override
	protected void onStart() {
		super.onStart();
	}
	@Override
	protected void onResume(){
		super.onResume();
		myGLView.onResume();
	}
	@Override
	protected void onPause(){
		super.onPause();
		myGLView.onPause();
	}
	@Override
	protected void onDestroy() {
		super.onDestroy();
		myGLView.dispose();
		myGLView = null;
    }
}
・MyGLView.java

コード:

public class MyGLView extends GLSurfaceView {
	//GLRendererのインスタンス
	public GLRenderer glRenderer;

	/**
	 * コンストラクタ
	 */
	public MyGLView(Context context) {
		super(context);
		//Rendererを生成&セット
		glRenderer = new GLRenderer(context);
		setRenderer(glRenderer);
	}

	/**
	 * メモリ解放
	 */
	public void dispose() {
		glRenderer.dispose();
		glRenderer = null;
	}
}
・GLRenderer.java

コード:

public class GLRenderer implements Renderer {
	private Context context;
	static {
		System.loadLibrary("xxxx");
	}
	//ネイティブ側の関数
	public native void NDKonSurfaceCreated();
	public native void NDKonSurfaceChanged(int width, int height);
	public native void NDKonDrawFrame();
	public native void NDKonDestroy();

	/**
	 * コンストラクタ
	 */
	public GLRenderer(Context context){
		this.context = context;
	}
	/**
	 * メモリ解放
	 */
	public void dispose() {
		context = null;
		NDKonDestroy();
	}

	@Override
	public void onSurfaceCreated(GL10 gl, EGLConfig config) {
		NDKonSurfaceCreated();
	}
	@Override
	public void onSurfaceChanged(GL10 gl, int width, int height) {
		NDKonSurfaceChanged(width, height);
	}
	@Override
	public void onDrawFrame(GL10 gl) {
		NDKonDrawFrame();
	}
}
################################################################################
# C++側
################################################################################
・xxxx.cpp

コード:

#include <jni.h>
#include <GLES/gl.h>
#include <GLES/glext.h>
#include "GraphicUtils.h"

namespace xxxx {
	class GLRenderer
	{
	public:
		GraphicUtils *graphicUtils;	//描画関数クラス
		int tex;			//テクスチャ番号

		void onSurfaceCreated() {
			//インスタンスを生成
			graphicUtils = new GraphicUtils();
			//テクスチャ読み込み
			tex = graphicUtils->loadTexture("/sdcard/path/to/image.png");
		}

		void onSurfaceChanged(int width, int height) {
			//ディザを無効化
			glDisable(GL_DITHER);
			//透過可能に
			glEnable(GL_ALPHA_TEST);
			//ブレンド可能に
			glEnable(GL_BLEND);
			glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
			//ViewPortの指定
			glViewport(0, 0, width, height);
			//プロジェクションモード
			glMatrixMode(GL_PROJECTION);
			//頂点配列の使用を許可
			glEnableClientState(GL_VERTEX_ARRAY);
			//テクスチャ配列の使用を許可
			glEnableClientState(GL_TEXTURE_COORD_ARRAY);
			//背景色を指定(黒)
			glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
			//画面クリア
			glClear(GL_COLOR_BUFFER_BIT);
			//座標系をリセット
			glLoadIdentity();
		}

		void onDrawFrame() {
			graphicUtils->makeWorld();
			//描画
			graphicUtils->drawTextureRectangle(1, 1, 0.25, 0.25, 0, tex, 0, 0, 0, 1);
		}

		void onDestroy() {
			delete graphicUtils;
		}
	};
}

extern "C" {
	JNIEXPORT void JNICALL Java_com_example_xxxx_GLRenderer_NDKonSurfaceCreated(JNIEnv* env);
	JNIEXPORT void JNICALL Java_com_example_xxxx_GLRenderer_NDKonSurfaceChanged(JNIEnv* env, jobject obj, jint w, jint h);
	JNIEXPORT void JNICALL Java_com_example_xxxx_GLRenderer_NDKonDrawFrame(JNIEnv* env);
	JNIEXPORT void JNICALL Java_com_example_xxxx_GLRenderer_NDKonDestroy(JNIEnv* env);
};
GLRenderer* glRenderer;
JNIEXPORT void JNICALL Java_com_example_xxxx_GLRenderer_NDKonSurfaceCreated(JNIEnv* env)
{
	glRenderer = new GLRenderer();
	glRenderer -> onSurfaceCreated();
}
JNIEXPORT void JNICALL Java_com_example_xxxx_GLRenderer_NDKonSurfaceChanged(JNIEnv* env, jobject obj, jint w, jint h)
{
	glRenderer -> onSurfaceChanged((int)w, (int)h);
}
JNIEXPORT void JNICALL Java_com_example_xxxx_GLRenderer_NDKonDrawFrame(JNIEnv* env)
{
	glRenderer -> onDrawFrame();
}
JNIEXPORT void JNICALL Java_com_example_xxxx_GLRenderer_NDKonDestroy(JNIEnv* env)
{
	glRenderer -> onDestroy();
}
・GraphicUtils.h

コード:

#ifndef GRAPHICUTILS_H
#define GRAPHICUTILS_H

#include <GLES/gl.h>
#include <GLES/glext.h>

namespace xxxx {
	class GraphicUtils {
	public:
		GraphicUtils();
		~GraphicUtils();
		//テクスチャ読み込み
		int loadTexture(const char file[]);
		//毎フレーム呼ぶ関数
		void makeWorld();
		//描画
		void drawTextureRectangle(float x, float y, float width, float height, float rotation, int textureId,
				float red, float green, float blue, float alpha);
	private:
		//OpenGLの座標軸の長さ
		float glx, gly;
		//テクスチャの個数の最大値
		int numOfTextures;
		//テクスチャ番号の配列(glDeleteTextures()で使うため)
		unsigned int *textureIDs;
		//テクスチャ番号の配列のインデックスのカウントに使う
		int texture_num;

		static const int ONE;
		static const int ONE_POINT_DATA_NUM;	//頂点を構成するデータの数(x,yの2つ)
		static const int POLYGON_NUM;		//四角形のポリゴン
	};
}
#endif
・GraphicUtils.cpp

コード:

#include <stdio.h>
#include <GLES/gl.h>
#include <GLES/glext.h>
#include "yspng.h"
#include "GraphicUtils.h"

namespace xxxx{
	//################################################################################
	// public;
	//################################################################################
	/**
	 * コンストラクタ
	 */
	GraphicUtils::GraphicUtils() {
		glx = 2.0f;
		gly = 3.0f;
		numOfTextures = 32;
		textureIDs = new unsigned int[numOfTextures];
		texture_num = 0;
	};
	/**
	 * デストラクタ
	 */
	GraphicUtils::~GraphicUtils() {
		glDeleteTextures(numOfTextures, textureIDs);
		delete textureIDs;
	};

	/**
	 * テクスチャの読み込み
	 */
	int GraphicUtils::loadTexture(const char filename[]) {
		YsRawPngDecoder pngDecoder;
		//初期化
		pngDecoder.Initialize();
		if(pngDecoder.Decode(filename) != YSOK) {
			return -1;
		}
		//pngDecoder.Flip();
		GLuint textureID;				//テクスチャID
		glGenTextures(1, &textureID);			//テクスチャオブジェクトを生成
		glBindTexture(GL_TEXTURE_2D, textureID);	//テクスチャをバインド
		glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);	//縮小するとき最も近い色で補完
		glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);	//拡大するとき周りの色をから求めた色で補完
		glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);	//s座標の1を超える端処理をループにしない
		glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);	//t座標の1を超える端処理をループにしない

		glTexEnvx( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE );		//テクスチャとモデルの合成方法の指定(置き換え)
		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, pngDecoder.wid, pngDecoder.hei, 0, GL_RGBA, GL_UNSIGNED_BYTE, pngDecoder.rgba);	//テクスチャ生成

		textureIDs[texture_num++] = (unsigned int)textureID;	//glDeleteTextures()のために生成したテクスチャの番号を記憶
		return (int)textureID;
	}

	/**
	 * 毎フレーム呼ぶ
	 */
	void GraphicUtils::makeWorld(){
		glClear(GL_COLOR_BUFFER_BIT); 	//画面クリア
		glLoadIdentity();		//座標をリセット
		glOrthof(0.0f, glx, gly, 0.0f, 0.5f, -0.5f);		//左上中心
	}

	/**
	 * テクスチャを四角形で描画
	 */
	const int GraphicUtils::ONE = 0x10000;		//16進数
	const int GraphicUtils::ONE_POINT_DATA_NUM = 2;	//頂点を構成するデータの数(x,yの2つ)
	const int GraphicUtils::POLYGON_NUM = 4;	//四角形のポリゴン
	void GraphicUtils::drawTextureRectangle(float x, float y, float width, float height, float rotation, int textureId, float red, float green, float blue, float alpha) {

		//引数は10進数で指定できるように
		x *= ONE; y *= ONE; width *= ONE; height *= ONE; rotation *= ONE;
		red *= ONE; green *= ONE; blue *= ONE; alpha *= ONE;

		GLfixed squares[8] = {
			-0.5f * width + x,  0.5f * height + y,
			 0.5f * width + x,  0.5f * height + y,
			-0.5f * width + x, -0.5f * height + y,
			 0.5f * width + x, -0.5f * height + y
		};
		float textureCoords[8] = {
			0.0f, 1.0f,
			1.0f, 1.0f,
			0.0f, 0.0f,
			1.0f, 0.0f
		};

		glEnable(GL_TEXTURE_2D);								//テクスチャを有効に
		glActiveTexture(GL_TEXTURE0);								//テクスチャユニットを指定
		glBindTexture(GL_TEXTURE_2D, textureId);							//テクスチャをバインド
		//回転処理
		if(rotation != 0) {
			glTranslatex(x,y,0);
			glRotatex(rotation, 0.0f, 0.0f, 1.0f);
			glTranslatex(-x,-y,0);
		}
		glVertexPointer(ONE_POINT_DATA_NUM, GL_FIXED, 0, squares);		//表示座標を設定
		glTexCoordPointer(ONE_POINT_DATA_NUM, GL_FIXED, 0, textureCoords);	//テクスチャの表示領域を設定
		glDrawArrays(GL_TRIANGLE_STRIP, 0, POLYGON_NUM);			//描画
		glDisable(GL_TEXTURE_2D);						//テクスチャを無効に
		//回転を戻す
		if(rotation != 0) {
			glTranslatex(x,y,0);
			glRotatex(-rotation, 0.0f, 0.0f, 1.0f);
			glTranslatex(-x,-y,0);
		}
	}
}
次に症状ですが、いろいろなテクスチャを読み込ませて調べていると、どうやら右下の1pxのみが拡大されて描画されているようです。
試しに四隅だけ色を変えた画像ファイルでテストしてみました。
画像
このような感じです。

色々と値を変えて試してみているのですが、どの辺でバグが発生しているのかがなかなか分かりません。
どなたかヒントまたはアドバイスをくださる方、いらっしゃいましたらご教授よろしくお願いいたします。

ARCS

Re: Android NDK + OpenGL ES でのテクスチャ描画

#2

投稿記事 by ARCS » 13年前

テクスチャ座標がfloatなのに、 glTexCoordPointerでGL_FIXEDを指定してるからじゃないでしょうか?

black_cat

Re: Android NDK + OpenGL ES でのテクスチャ描画

#3

投稿記事 by black_cat » 13年前

ARCS様、返信ありがとうございます。

おっしゃる通りでした!
プログラムはGL_FIXEDで統一したいので、テクスチャ座標の方を

コード:

const int GraphicUtils::ZERO = 0x0;
const int GraphicUtils::ONE = 0x10000;
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~	
GLfixed textureCoords[8] = {
	ZERO, ONE,
	ONE, ONE,
	ZERO, ZERO,
	ONE, ZERO
};
こうすることで解決いたしました。

ありがとうございました!

閉鎖

“C言語何でも質問掲示板” へ戻る