【Android】OpenGLでテクスチャ表示したかっただけなんや

まいど。ゴミ社員のKです。
ぐえっへっへへ(o´・∀・)y~

やめてー。
ガベージコレクトせんといてー。
要らん子やけどここに居たいんやー。

k = null;
System.gc();
ぎゃぁああああああ!

■どうしても避けて通りたかった3Dプログラミング。

”ワイのゲームはファミコン風ドット絵やから3Dなんか要らんズー”
”3Dってあれやろ、ハリウッドの。そんなんワイのゲームに要らんズー”
”つか、3Dってダサくね?HAHAHAHA・・・”

・・・なぞというカッチョわりぃ言い訳をして、3Dの勉強から逃げてきましたが、
このたび、3Dプログラミング(OpenGL ES)に手を染める運びとなりました。

逃げてた理由=それは難しいからです。
(行列演算・・・?うっ頭が・・・)

やろうと思った理由は簡単です。
オレのAndroidゲーム、もっとたくさんのキャラをもっと速く動かしたい。そんだけ。
そのためには、ゲームフレーム中の描画処理のコストを下げる…
というか、強力な描画機構を使うのがよかろうと。

なお、Androidアプリの描画の仕組みは、
View、SurfaceView、GLSurfaceViewと3種類あり、
左ほど作るのが簡単だけど遅くて、右ほど難しいけどパワフルで高速化が見込めるぜ…と、よく聞く
一番右のGLSurfaceViewというやつがOpenGL ESで3Dプログラミングできるやつです。

ちなみに、前回紹介した2Dゲーム「忍者盛りは、真ん中のやつ(SurfaceView)で実装しています。
今回の話は、これをGLSurfaceView(つまりOpenGL ES)に置き換えたらどうなんねんハゲコラ!泣かしたろか!というコスモが起源です。

ワンポイントアドバイス(ゝω・)
”忍者盛り”をGoogle検索すると、勝手に”忍者めし”に変換してくれるぞ!それチガウンヤケドナ…(2015年10月現在)

■板ポリゴンにテクスチャを表示

さて、3Dをどのように2Dゲームに転用するかというと、
それは当然、厚みをもたない四角い板(板ポリゴン)に、画像を貼りつけて表示するのがセオリーだろうと。

ははーん。板に絵をぶちまけるだけでヨゴザンスね?ニヤニヤ?
ということで、”オレは板ポリに絵(テクスチャ)貼っちゃる、それは簡単だと思いました。

ところが!これがもう・・・それはそれは苦難の道でしたとさ・・・。
例えるならば、固形の吐しゃ物を、老廃物と一緒に自由自在に体内からいつでも放出できる感じです。
これを嘔吐コントロール(Auto Control)と言います。

まず、何から手を付けていいかわからんのです!
正直な話、ネットに転がってるサンプルを見ても、何をしているのかいっこもワカラン。

頂点配列深度テストビューポート
フーム、ワカリマセンネ(o´・∀・)シゲシゲ?ワカリマセンヨ。

「ええから、ポリ公にオレの絵貼っつける方法、はよ。」

まぁ、やっぱり自分はゴミカスやったということです。
その事実を認めたくないがために、ただひたすらにやりました。

■―かくして1週間。

板ポリ助に貼った絵を簡単に操る仕組みを作ったドン!キター!
以下のコードのMySpriteというクラスがそれです。
こいつに絵を(リソースIDを)与えて、draw●●メソッドを呼べば、移動・反転・拡大・回転!自由に描画できるドン。
MyRendererのonDrawFrameメソッドを見れば、簡単に描画できることが分かると思います。
「忍者盛り」もスプライト単位でクラス化していたので、簡単に置き換えができるドン。

天才やー。ワイ、ゴミカスやとおもたら天才やったんやー。
うん知ってた(´・∀・`)マァナ。オレってこう見えてけっこうナイスな男子なんよね。実際。
罪深いオレでまったく申し訳ないと思っているね。ここだけの話。マジで。

というわけでサモハンinチャンプル(サンプル)

↓MainActivity.java

[java collapse=”true”]</pre>
package com.syscube.openglsample;

import android.app.Activity;
import android.os.Bundle;

/**
* アクティビティ
*
* @author イケメンのオレ
*
*/
public class MainActivity extends Activity {

/**
* 生成
*/
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// GLSurfaceViewを設定します
setContentView(new MyGLView(this));
}
}
<pre>[/java]

↓MyGLView.java
[java collapse=”true”]</pre>
package com.syscube.openglsample;

import android.app.Activity;
import android.opengl.GLSurfaceView;

/**
* GLSurfaceView
*
* @author ワイ
*
* これを使うとOpenGL(ES)で3Dプログラミング結果を描画できます
*
*/
public class MyGLView extends GLSurfaceView {

/**
* コンストラクタ
*/
public MyGLView(Activity activity) {
super(activity);
this.getHolder().addCallback(this);

// レンダラ―をセット
this.setRenderer(new MyRenderer(activity.getApplicationContext()));
}
}
<pre>[/java]
↓MyRenderer.java
[java collapse=”true”]</pre>
package com.syscube.openglsample;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

import android.content.Context;
import android.opengl.GLSurfaceView.Renderer;

/**
* レンダラ―
*
* @author 拙者
*
* このクラスがアプリケーションの実質の描画担当者になります。
* 何もしなくても勝手にonDrawFrameメソッドが呼ばれます。
* 描画したいものをonDrawFrameメソッドに指定すればよいです。
*/
public class MyRenderer implements Renderer {
/**
* アプリケーションの幅(pixel)
*/
private static final int APP_SIZE_W = 480;
/**
* アプリケーションの高さ(pixel)
*/
private static final int APP_SIZE_H = 800;

/**
* アプリケーションコンテキスト
*/
private final Context context;
/**
* 板ポリで実現したスプライト(キャラクター)
*/
private MySprite ninja = new MySprite();

/**
* コンストラクタ
* @param context:アプリケーションコンテキスト
*/
public MyRenderer(Context context) {
this.context = context;
}

/**
* 生成(初期化)
*/
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// 画面のクリア時の色の指定
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
gl.glClearColor(0.0f, 0.0f, 0.0f, 1.0f); // 黒
//gl.glClearColor(1.0f, 1.0f, 1.0f, 1.0f); // 白

//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// ちょっぴり高速化対策(効果の程は知らないがたぶんそれなりに)
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// ディザ使いません(デフォルトでOFFになってると思うけど念のため)
gl.glDisable(GL10.GL_DITHER);
// ポリゴンの面に陰影をつけません
gl.glShadeModel(GL10.GL_FLAT);
// ポリゴンの輪郭がジャギジャギしてもかまいません
gl.glHint(GL10.GL_POINT_SMOOTH_HINT, GL10.GL_FASTEST);

//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// スプライト用のビットマップ(キャラクター画像)をロードします
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
this.ninja.load(gl, this.context.getResources(), R.drawable.ninja);
}

/**
* 向き変更
* このメソッド内でアプリケーションの描画スクリーンを設定する
*/
@Override
public void onSurfaceChanged(GL10 gl, int w, int h) {
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// ビューポート(表示領域)の指定
//
// 【引数の説明】
// 開始X座標, 開始Y座標, 幅, 高
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
gl.glViewport(0, 0, w, h);

//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// プロジェクターに投影するような感じにする
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// プロジェクションモードに設定
gl.glMatrixMode(GL10.GL_PROJECTION);
// スクリーン座標を初期化
gl.glLoadIdentity();

//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// ビューポート内の座標系を左上(0, 0)に設定
// こうしないと左下が(0, 0)になってややこしくなる
//
// 【引数の説明】
// 左X, 右X, 下Y, 上Y, 手前Z, 奥Z
// 2Dゲームなので手前Zと奥Zは気にしなくてよい
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
gl.glOrthof(0.0f, APP_SIZE_W, APP_SIZE_H, 0.0f, -1.0f, 1.0f);
}

/**
* 描画
* これは毎フレーム勝手に呼び出されます(メインループに相当する)
*/
@Override
public void onDrawFrame(GL10 gl) {

//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// キャラクターをお好きなように描画します
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// x=0, y=0の座標に普通に描画
this.ninja.draw(gl, 0, 0);

// x=0, y=64の座標にデブッチョ(横幅2倍)に描画
this.ninja.drawEx(gl, 0, 64, 2, 1);

// x=64, y=64の座標に左右反転に描画
this.ninja.drawTurnLR(gl, 64, 128);

// x=200, y=200の座標に40度傾けて描画
this.ninja.drawRota(gl, 200, 200, 40);
}
}
<pre>[/java]

↓MySprite.java
[java collapse=”true”]</pre>
package com.syscube.openglsample;

import javax.microedition.khronos.opengles.GL10;

import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.opengl.GLUtils;
import android.util.Log;
/**
* スプライトクラス
*
* @author 忍者マン
*
* ビットマップ(つまり画像)を、矩形の板ポリゴンにテクスチャとして貼りつけるクラスです。
* loadメソッドを呼んで初期化しておけば、いつでもdraw●●メソッドで自由に描画できます。
* ビットマップは、タテヨコが必ず2の2乗の正方形でなければいけません。
*
*/
public class MySprite {
/**
* テクスチャID
*/
private Integer textureID = null;
/**
* 板ポリゴン(矩形)
*/
private MyPorigon porigon = null;

/**
* リソースのビットマップをロードしてテクスチャ登録します
*
* @param gl:GL10
* @param res:コンテキストリソース
* @param id:ビットマップのリソースID
* @return true:成功
*/
public boolean load(GL10 gl, Resources res, int id) {
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// ビットマップをメモリにロード
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Bitmap bitmap = BitmapFactory.decodeResource(res, id);

//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// ビットマップのイレギュラーチェック
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// ロード正常チェック
if(bitmap == null) {
Log.e("Error!", "ビットマップロードに失敗ですドン!");
}
// サイズが2の2乗の正方形かどうかチェック
if(this.isNumberPowerOfTwo(bitmap.getWidth()) == false ||
this.isNumberPowerOfTwo(bitmap.getHeight()) == false) {
Log.e("Error!", "ビットマップが2の2乗の正方形じゃないですドン!");
}

//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// 板ポリゴンを生成
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
this.porigon = new MyPorigon(bitmap.getWidth(), bitmap.getHeight());

//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// ビットマップをテクスチャとして登録
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
this.registTexture(gl, bitmap);

//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// ビットマップをメモリから破棄
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// 上でテクスチャ登録してるのでもう要らない
if(bitmap != null) {
bitmap.recycle();
bitmap = null;
}

// いまのうちにガベージコレクト
System.gc();

return true;
}
/**
* 数値が2の乗数かどうかを返します
* @param n:数値
* @return true:2の乗数
*/
private boolean isNumberPowerOfTwo(int n) {
return (n & (n – 1)) == 0;
}
/**
* ビットマップをテクスチャとして登録します
* @param gl:GL10
* @param bitmap:ビットマップ
*/
private void registTexture(GL10 gl, Bitmap bitmap) {
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// 使用可能なテクスチャ識別番号を取得
// テクスチャ識別番号はOpenGLが管理しているので
// プログラマはOpenGLに問合わせて空き番号をもらいます
// glGenTexturesが問合せメソッド
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
int[] textures = new int[1];
gl.glGenTextures(1, textures, 0);
// テクスチャ管理番号の保存
this.textureID = textures[0];

//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// テクスチャ識別番号とビットマップの紐付
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// まず今回のテクスチャ管理番号をバインドする
gl.glBindTexture(GL10.GL_TEXTURE_2D, this.textureID);
// そのバインドした状態でビットマップを与えることで紐づく
GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);

//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// テクスチャを拡大・縮小する方法の指定
//
// 【引数の説明】
// GL_TEXTURE_2D(固定), 拡大時の指定 or 縮小時の指定, 伸縮方法
//
// 拡大時の指定 or 縮小時の指定は、
// GL_TEXTURE_MAG_FILTERで拡大時の指定
// GL_TEXTURE_MIN_FILTERで縮小時の指定
//
// 伸縮方法は、
// GL_NEARESTは軽いけど見た目が荒くなる
// GL_LINEARは重いけど見た目が滑らかになる
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_NEAREST);

//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// テクスチャの繰り返し方法の指定
//
// 【引数の説明】
// GL_TEXTURE_2D(固定), X方向の指定 or Y方向の指定, 繰り返し方法
//
// X方向の指定 or Y方向の指定は、
// GL_TEXTURE_WRAP_S:X方向の指定
// GL_TEXTURE_WRAP_T:Y方向の指定
//
// 繰り返し方法は、
// GL_REPEAT:繰り返しする
// GL_CLAMP:繰り返ししない(OpenGL ESでは使えないみたい)
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S, GL10.GL_REPEAT);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T, GL10.GL_REPEAT);
}

/**
* 描画します
* @param gl:GL10
* @param posX:X座標
* @param posY:Y座標
*/
public void draw(GL10 gl, float posX, float posY) {
// マトリックスを設定
this.porigon.setMatrix(gl, posX, posY, 1.0f, 1.0f, 0.0f, false, false);
// テクスチャを描画
this.porigon.draw(gl, this.textureID);
}
/**
* 左右反転描画します
* @param gl:GL10
* @param posX:X座標
* @param posY:Y座標
*/
public void drawTurnLR(GL10 gl, float posX, float posY) {
// マトリックスを設定
this.porigon.setMatrix(gl, posX, posY, 1.0f, 1.0f, 0.0f, true, false);
// テクスチャを描画
this.porigon.draw(gl, this.textureID);
}
/**
* 上下反転描画します
* @param gl:GL10
* @param posX:X座標
* @param posY:Y座標
*/
public void drawTurnTB(GL10 gl, float posX, float posY) {
// マトリックスを設定
this.porigon.setMatrix(gl, posX, posY, 1.0f, 1.0f, 0.0f, false, true);
// テクスチャを描画
this.porigon.draw(gl, this.textureID);
}
/**
* 拡大縮小描画します
* @param gl:GL10
* @param posX:X座標
* @param posY:Y座標
* @param exRateX:X方向拡大縮小率(1.0で等倍)
* @param exRateY:Y方向拡大縮小率(1.0で等倍)
*/
public void drawEx(GL10 gl, float posX, float posY, float exRateX, float exRateY) {
// マトリックスを設定
this.porigon.setMatrix(gl, posX, posY, exRateX, exRateY, 0.0f, false, false);
// テクスチャを描画
this.porigon.draw(gl, this.textureID);
}
/**
* 回転描画します
* @param gl:GL10
* @param posX:X座標
* @param posY:Y座標
* @param degree:角度(0~360)
*/
public void drawRota(GL10 gl, float posX, float posY, float degree) {
// マトリックスを設定
this.porigon.setMatrix(gl, posX, posY, 1.0f, 1.0f, degree, false, false);
// テクスチャを描画
this.porigon.draw(gl, this.textureID);
}
}
<pre>[/java]

↓MyPorigon.java
[java collapse=”true”]</pre>
package com.syscube.openglsample;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;

import javax.microedition.khronos.opengles.GL10;

/**
* (矩形)板ポリゴン
*
* @author オレがガンダムだ
*
*/
public class MyPorigon {

/**
* マトリックスのクラス
* マトリックスと名付けてますが、単に、座標とか諸々をまとめた構造体みたいなものです
*/
private class Graph2DMatrix {
/**
* X座標
*/
public float posX = 0.0f;
/**
* Y座標
*/
public float posY = 0.0f;
/**
* 拡大・縮小率X(1.0で等倍)
*/
public float exRateX = 1.0f;
/**
* 拡大・縮小率Y(1.0で等倍)
*/
public float exRateY = 1.0f;
/**
* 角度
*/
public float degree = 0.0f;

/**
* 設定します
* @param sizeW:画像1コマ分のサイズ(幅)px
* @param sizeH:画像1コマ分のサイズ(高)px
* @param posX:X座標(左)
* @param posY:Y座標(上)
* @param exRateX:拡大・縮小率X
* @param exRateY:拡大・縮小率Y
* @param degree:角度
* @param isLRReverse:trueで左右反転
* @param isTBReverse:trueで上下反転
*/
public void set(int sizeW, int sizeH, float posX, float posY, float exRateX, float exRateY, float degree, boolean isLRReverse, boolean isTBReverse) {
this.posX = posX;
this.posY = posY;
this.exRateX = exRateX;
this.exRateY = exRateY;
this.degree = degree;
// 左右反転するときのみ
if(isLRReverse) {
// 拡大・縮小率を負にすると反転する
this.exRateX *= -1.0f;
// 左右反転に伴ってサイズ(幅)の分だけ移動させる(そうしないと左に寄ってしまう)
this.posX += sizeW;
}
// 上下反転するときのみ
if(isTBReverse) {
// 拡大・縮小率を負にすると反転する
this.exRateY *= -1.0f;
// 左右反転に伴ってサイズ(高)の分だけ移動させる(そうしないと上に寄ってしまう)
this.posY += sizeH;
}
}
}

/**
* マトリックスのインスタンス
*/
private Graph2DMatrix matrix = new Graph2DMatrix();
/**
* UV配列オブジェクト(U:ヨコ、V:タテとする左上原点の画像切り出し範囲。画像全体をサイズW=1.0f、サイズH=1.0fとして表現される)
*/
private FloatBuffer uvArray = null;
/**
* 頂点配列オブジェクト
*/
private FloatBuffer vertexArray = null;

/**
* サイズ(幅)
*/
private final int sizeW;
/**
* サイズ(高)
*/
private final int sizeH;
/**
* 半分のサイズ(幅)
*/
private final int halfSizeW;
/**
* 半分のサイズ(高)
*/
private final int halfSizeH;

/**
* コンストラクタ
* @param sizeW:テクスチャの幅(pixel)
* @param sizeH:テクスチャの高さ(pixel)
*/
public MyPorigon(int sizeW, int sizeH) {
this.sizeW = sizeW;
this.sizeH = sizeH;
// 半分のサイズを設定
this.halfSizeW = this.sizeW / 2;
this.halfSizeH = this.sizeH / 2;

// UV配列オブジェクトを生成する
this.uvArray = this.createUVArray();
// 頂点配列オブジェクトを生成
this.vertexArray = this.createVertexArray();
}

/**
* マトリックスを設定します
* @param gl:GL10
* @param posX:X座標
* @param posY:Y座標
* @param exRateX:X方向拡大縮小率(1.0で等倍)
* @param exRateY:Y方向拡大縮小率(1.0で等倍)
* @param degree:角度(0~360)
* @param isLRReverse:左右反転するかどうか
* @param isTBReverse:上下反転するかどうか
*/
public void setMatrix(GL10 gl, float posX, float posY, float exRateX, float exRateY, float degree, boolean isLRReverse, boolean isTBReverse) {
this.matrix.set(this.sizeW, this.sizeH, posX, posY, exRateX, exRateY, degree, isLRReverse, isTBReverse);
}

/**
* 描画します
* @param gl:GL10
* @param textureID:テクスチャID(nullでテクスチャ無し)
*/
public void draw(GL10 gl, Integer textureID) {

//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// 頂点配列を有効にする
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);

//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// ブレンドを有効にする
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
gl.glEnable(GL10.GL_BLEND);
gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA);

//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// 色と透明度の指定
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// まっ白
gl.glColor4f(1.0f, 1.0f, 1.0f, 1.0f); // 最後の引数を0.0f~1.0fの範囲で透明度を表せます

if(textureID != null) {
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// テクスチャ、テクスチャ座標配列を有効にする
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
gl.glEnable(GL10.GL_TEXTURE_2D);
gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// テクスチャ画像の指定
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// テクスチャの指定
gl.glBindTexture(GL10.GL_TEXTURE_2D, textureID);
// 0番テクスチャを有効化(この板ポリゴンにテクスチャを重ねて貼る場合に0~Nで指定するが今回は0しかない)
gl.glActiveTexture(GL10.GL_TEXTURE0); // 0はデフォルトで有効のためこの行はコメントアウトしてもかまわない

//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// UV配列オブジェクトの指定(テクスチャのどの部分を切出して描画するか)
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, this.uvArray);
}

//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// 頂点配列オブジェクトの指定
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, this.vertexArray);

//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// マトリックスの有効化
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// マトリックス有効化
gl.glPushMatrix();
// まず移動(実際は最後に移動が適用される)
gl.glTranslatef(this.matrix.posX, this.matrix.posY, 0.0f);
// 次に回転
if(this.matrix.degree > 0 || this.matrix.degree < 0) {
// 画像中心点で回転させるために移動が必要
gl.glTranslatef(this.halfSizeW * this.matrix.exRateX, this.halfSizeH * this.matrix.exRateY, 0.0f);
// 回転させる
gl.glRotatef(this.matrix.degree, 0.0f, 0.0f, 1.0f);
// 移動を元に戻す
gl.glTranslatef(-this.halfSizeW * this.matrix.exRateX, -this.halfSizeH * this.matrix.exRateY, 0.0f);
}
// 最後に拡大・縮小
gl.glScalef(this.matrix.exRateX, this.matrix.exRateY, 1.0f);

//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// 描画
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 4);

//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// マトリックスの無効化
// これをしないと全ての描画オブジェクトに影響してしまいます
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// マトリックス無効化
gl.glPopMatrix();

//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// 頂点配列、テクスチャ座標配列を無効にする
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// テクスチャ、テクスチャ座標配列を無効にする
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
gl.glDisable(GL10.GL_TEXTURE_2D);
gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// ブレンドを無効にする
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
gl.glDisable(GL10.GL_BLEND);
}

/**
* UV配列オブジェクトを生成します
* @return UV配列オブジェクト
*/
private FloatBuffer createUVArray() {
// float配列で定義
float[] uv = {
0.0f, 0.0f, // 左上
0.0f, 1.0f, // 左下
1.0f, 0.0f, // 右上
1.0f, 1.0f, // 右下
};

//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// 画像全体を幅1.0、高さ1.0として扱われる
// 切出し範囲のWidth/Heightを0.0~1.0の間で指定することができる
// 今回は画像切り出しなしなので、1.0までを(全体を)指定をする
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

// FloatBufferに変換して返す
return this.getFloatBufferFromFloatArray(uv);
}
/**
* 頂点配列オブジェクトを生成します
*/
private FloatBuffer createVertexArray() {
float left = 0.0f;
float top = 0.0f;
float right = left + this.sizeW;
float bottom = top + this.sizeH;

//頂点バッファの生成
float[] vertexs = {
// x, y, z
left, top, 0.0f, // 頂点0
left, bottom, 0.0f, // 頂点1
right, top, 0.0f, // 頂点2
right, bottom, 0.0f, // 頂点3
};

// FloatBufferに変換して返す
return this.getFloatBufferFromFloatArray(vertexs);
}
/**
* float配列をFloatBufferに変換します
* @param fa:float配列
* @return 変換後のFloatBuffer
*/
public FloatBuffer getFloatBufferFromFloatArray(float[] fa) {

// このやり方はAndroidDocで紹介されているため通例(と思われる)
FloatBuffer fb = ByteBuffer.allocateDirect(fa.length * 4).order(ByteOrder.nativeOrder()).asFloatBuffer();
fb.put(fa).position(0);

return fb;
}
}[/java]


なげぇよ。なげーんだよ!
しかし、それがAndroid!OpenGL ES!

実行結果は↓こんなん。好きな位置に、好きな大きさで、好きな角度で絵を表示できてます。
GLSurfaceViewでテクスチャ表示
ちなみに使った画像は↓これ。忍者盛りの赤丸くん(64px*64px)

忍者盛りの忍者
resフォルダ直下に、drawable-nodpiというフォルダを作って、そこに入れるのですよ。

そういえば、このサンプルは、頂点配列を毎フレームGPUに転送しているよ。
なので、もっと高速化したい場合は頂点バッファで、あらかじめGPUに転送しておくとよいかもよ。

床井先生のサイトがかなり参考になった。OpenGLについては全部ここに書かれとる。
和歌山大学・床井先生のサイト(ゼミのサイトっぽい)

ちなみに、ちょっとだけ、自分のゲームを板ポリに置き換えてみたのですが、その良さはまだいっこもわかりません。
むしろ、なんか遅くなってる気がす・・・ゲフン!
たぶん、こういうゲームだとOpenGLの恩恵をそれほど受けられないのか、ワイのプログラムがゴミ過ぎるかのいずれかだろうと思います。

まとめると、ゴミ野郎はゴミ野郎らしく、毎日4時間睡眠で頑張れってことです。
ニキビが出来てもかまわねぇオレカッケェ(深刻)。

PAGE TOP ▲

お問い合わせフォームはこちら

お客様のビジネスの規模・内容に従い、最適なアプリケーションをご紹介します。
アプリケーションの導入・連携支援は、ぜひ、システムキューブにお任せください。

Copyright © 2022 System Cube Co.,Ltd. All Rights Reserved.