朝晩はだいぶ冷え込むようになってきましたね。
心地よい秋はすぐに通り過ぎてしまいます;
季節の話題はさておき本題に入らせていただきますと、今回のトピックは「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;
}
}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;
}
}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();
}#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#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);
}
}
}試しに四隅だけ色を変えた画像ファイルでテストしてみました。

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