2,265 views
この記事は最終更新から 2260日 が経過しています。
1. やりたいこと
(13) OpenGLで画像を表示する。 では実機上にボールを表示した。
(15) 加速度センサーを使ってみる。 では加速度センサーの出力値を見た。
よって、今回は…
実機上に OpenGLで表示したボールを、実機を傾けて転がしてみたい。
2. やってみる!
(1) 実装
能書きを抜きにしてソースコードを貼り付けておく。
センサーからの入力値を Activity → View → Renderer とずるずると引き渡している。
何かスマートな方法はないものか?
1) MainActivity
import android.hardware.Sensor; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; import android.hardware.SensorManager; import android.os.Handler; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; public class MainActivity extends AppCompatActivity implements SensorEventListener { MyGLView m_glView; private SensorManager m_sensorManager; private float m_sensor_val_x, m_sensor_val_y; private Runnable m_runnable; private final Handler m_handler = new Handler(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // OpenGL View生成 m_glView = new MyGLView(this); setContentView(m_glView); // センサー初期化 m_sensor_val_x = m_sensor_val_y = 0.0f; m_sensorManager = (SensorManager)getSystemService(SENSOR_SERVICE); StartCyclicHandler(); // 周期ハンドラ開始 } @Override protected void onResume() { super.onResume(); m_glView.onResume(); // Event Listener登録 Sensor accel = m_sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); m_sensorManager.registerListener(this, accel, SensorManager.SENSOR_DELAY_NORMAL); StartCyclicHandler(); // 周期ハンドラ開始 } @Override protected void onPause() { super.onPause(); m_glView.onPause(); // Event Listener登録解除 m_sensorManager.unregisterListener(this); StoptCyclicHandler(); // 周期ハンドラ停止 } protected void StartCyclicHandler(){ m_runnable = new Runnable() { @Override public void run() { m_glView.AccelerometerNotify(m_sensor_val_x, m_sensor_val_y); m_handler.postDelayed(this, 30); // 30msスリープ } }; m_handler.post(m_runnable); // スレッド起動 } protected void StoptCyclicHandler() { m_handler.removeCallbacks(m_runnable); } @Override public void onSensorChanged(SensorEvent event) { // センサー通知 if(event.sensor.getType() == Sensor.TYPE_ACCELEROMETER){ m_sensor_val_x = event.values[0]; m_sensor_val_y = event.values[1]; } } @Override public void onAccuracyChanged(Sensor sensor, int accuracy) { } }
2) MyGLView
import android.content.Context; import android.opengl.GLSurfaceView; public class MyGLView extends GLSurfaceView { private MyGLRenderer m_renderer; public MyGLView(Context context){ super(context); // レンダラー生成 m_renderer = new MyGLRenderer(context); setRenderer(m_renderer); } public void AccelerometerNotify( float x, float y ){ // 加速度センサー通知 m_renderer.AccelerometerNotify(x, y); } }
3) MyGLRenderer
import android.content.Context; import android.opengl.GLSurfaceView; import android.util.Log; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.opengles.GL10; public class MyGLRenderer implements GLSurfaceView.Renderer { private Context m_context; // アプリのコンテキスト private MyGLPic m_pic = null; // 表示画像 private int m_view_w, m_view_h; // View表示領域の幅と高さ public MyGLRenderer( Context context ){ m_context = context; } @Override public void onSurfaceCreated(GL10 gl, EGLConfig config) { gl.glDisable(GL10.GL_DITHER); // 機能無効化 : ディザリング gl.glEnable(GL10.GL_DEPTH_TEST); // 機能有効化 : 隠面消去用 Depth Buffer更新 gl.glEnable(GL10.GL_TEXTURE_2D); // 機能有効化 : 2Dテクスチャ gl.glEnable(GL10.GL_ALPHA_TEST); // 機能有効化 : アルファテスト(αチャネルによる画素の有効/無効判定) gl.glEnable(GL10.GL_BLEND); // 機能有効化 : ブレンド(画像の重ね合わせ) gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA); // カラーブレンド(画像重ね合わせ)モード : アルファブレンド // 描画する画像をリソースから取得 m_pic = new MyGLPic(); m_pic.SetTexture(gl, m_context.getResources(), R.drawable.ball); } @Override public void onSurfaceChanged(GL10 gl, int width, int height) { m_view_w = width; m_view_h = height; } @Override public void onDrawFrame(GL10 gl) { // 描画用バッファをクリア gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); m_pic.Draw(gl); // 画像を描画 } public void AccelerometerNotify( float x, float y ){ // 加速度センサー通知 if(m_pic == null){ return; } int iX = (int)(x * 3.0); // X方向移動量 : センサー出力値 x 3[pixel]動かす int iY = (int)(y * 3.0); // Y方向移動量 : センサー出力値 x 3[pixel]動かす // 制御対象画像の位置とサイズを取得 int pic_w = m_pic.get_w(); int pic_h = m_pic.get_h(); int pic_x = m_pic.get_pos_x(); int pic_y = m_pic.get_pos_y(); // 移動後の位置を算出 int new_x = pic_x; int new_y = pic_y; // X軸 new_x = pic_x - iX; if(iX > 0) { // 左回転? if(new_x < 0){ new_x = 0; } } else if(iX < 0) { // 右回転? int lim_x = m_view_w - pic_w; if(new_x > lim_x){ new_x = lim_x; } } // Y軸 new_y = pic_y - iY; if(iY > 0) { // 手前回転? ※画面下方向(-方向)へ移動 if(new_y < 0){ new_y = 0; } } else if(iY < 0){ // 奥回転 ※画面上方向(+方向)へ移動 int lim_y = m_view_h - pic_h; if(new_y > lim_y){ new_y = lim_y; } } // 画像の位置を変更 m_pic.SetPosXY(new_x, new_y); } }
4) MyGLPic
import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.opengl.GLUtils; import android.util.Log; import java.io.IOException; import java.io.InputStream; import javax.microedition.khronos.opengles.GL10; import javax.microedition.khronos.opengles.GL11; import javax.microedition.khronos.opengles.GL11Ext; public class MyGLPic { protected int m_textureNo; protected float m_width, m_height; // 画像の表示幅と高さ protected int m_crop_x, m_crop_y; // テクスチャ切り出し左上オフセット座標 protected int m_crop_w, m_crop_h; // テクスチャ切り出し幅と高さ protected float m_pos_x, m_pos_y, m_pos_z; // テクスチャ表示位置 public MyGLPic(){ m_textureNo = 0; m_pos_x = m_pos_y = m_pos_z = 0.0f; m_width = m_height = 0.0f; } // 画像情報問い合わせ public int get_pos_x(){ return (int)m_pos_x; } public int get_pos_y(){ return (int)m_pos_y; } public int get_w(){ return (int)m_width; } public int get_h(){ return (int)m_height; } // 本インスタンスに画像をロード public void SetTexture( GL10 gl, Resources res, int id ) { InputStream is = res.openRawResource(id); Bitmap bitmap; try { bitmap = BitmapFactory.decodeStream(is); } finally { } try { is.close(); } catch (IOException e) { Log.d("MyGLPic", "Can't load resource."); } gl.glEnable(GL10.GL_ALPHA_TEST); gl.glEnable(GL10.GL_BLEND); gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA); // 下地色とテクスチャ色との合成方法 : 乗算 gl.glTexEnvf(GL10.GL_TEXTURE_ENV, GL10.GL_TEXTURE_ENV_MODE, GL10.GL_MODULATE); // テクスチャIDを割り当て int[] ary_textureNo = new int[1]; gl.glGenTextures(1, ary_textureNo, 0); m_textureNo = ary_textureNo[0]; // テクスチャIDをバインドし、データとIDを関連付ける。 gl.glBindTexture(GL10.GL_TEXTURE_2D, m_textureNo); GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0); // ポリゴン内の画像の繰り返し指定 S軸、T軸ともに繰り返しなし。 gl.glTexParameterx(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S, GL10.GL_CLAMP_TO_EDGE ); gl.glTexParameterx(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T, GL10.GL_CLAMP_TO_EDGE ); // テクスチャのリサンプリングアルゴリズム指定 gl.glTexParameterx(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR ); gl.glTexParameterx(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LINEAR ); // 表示座標設定 int img_w = bitmap.getWidth(); int img_h = bitmap.getHeight(); SetPosAll(0, img_h, img_w, -img_h,0, 0, img_w, img_h); } //テクスチャ表示サイズ、表示位置情報をセット public void SetPosAll( int crop_x,int crop_y,int crop_w,int crop_h, float pos_x, float pos_y, float disp_w, float disp_h ){ m_crop_x = crop_x; m_crop_y = crop_y; m_crop_w = crop_w; m_crop_h = crop_h; m_pos_x = pos_x; m_pos_y = pos_y; m_width = disp_w; m_height = disp_h; } public void SetPosXY( float pos_x, float pos_y ) { m_pos_x = pos_x; m_pos_y = pos_y; } public void Draw( GL10 gl ){ gl.glDisable(GL10.GL_DEPTH_TEST); // デプステストOFF // 白色 // gl.glColor4x(0x10000, 0x10000, 0x10000, 0x10000); // 表示するテクスチャをバインド gl.glActiveTexture(GL10.GL_TEXTURE0); gl.glBindTexture(GL10.GL_TEXTURE_2D, m_textureNo); // テクスチャから指定部分を切り出して表示 int rect[] = {m_crop_x, m_crop_y, m_crop_w, m_crop_h}; ((GL11)gl).glTexParameteriv(GL10.GL_TEXTURE_2D, GL11Ext.GL_TEXTURE_CROP_RECT_OES, rect, 0); ((GL11Ext) gl).glDrawTexfOES(m_pos_x, m_pos_y, m_pos_z, m_width, m_height); gl.glEnable(GL10.GL_DEPTH_TEST); // デプステストON } }
(2) 実行結果
実機を傾けるとボールがコロコロと転がる。
ボール回転のアニメーションをしていないので、実際にはボールが画面上を滑っている。
3. 所感
・MyGLPic::SetTexture() 内の res.openRawResource(id) で落ちた原因は、画像リソースを drawable v24 の方に登録していたため。
・30fpsをもくろんで描画スレッドを 30ms周期で起動するようにした。
・実際にはそんなレートが出ていないのか、ボール移動時に少しカクカクして見える。
・ボール画像が 200 x 200[pixel]と大きかったのもカクカクした原因か?
・実機(NEC Lavie Tab PC-TE508S1W)の性能の問題でカクカクするのか?
・描画スレッドではなく onSensorChanged で描画してみたら耐えられない遅さになった。
・スマホでも動かしてみたい。うちには Android 2.3の古いスマホしかないので、ヤフオクで 6.xぐらいを買うか?