androidのopenglesをつかってみる。三角形の描画
色々サンプルはあるけど、無駄なものがおおい。
必要な部分をしぼって描いてみた。
activity。
import android.app.Activity; import android.os.Bundle; import android.opengl.GLSurfaceView; public class MainActivity extends Activity { private GLSurfaceView mGLView; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //setContentView(R.layout.main); mGLView = new GLSurfaceView(this); mGLView.setRenderer(new GLRenderer()); this.setContentView(mGLView); } @Override protected void onPause(){ super.onPause(); mGLView.onPause(); } @Override protected void onResume(){ super.onResume(); mGLView.onResume(); } }
レンダリング部分。
import android.opengl.GLSurfaceView; import javax.microedition.khronos.egl.*; import javax.microedition.khronos.opengles.*; public class GLRenderer implements GLSurfaceView.Renderer{ public GLRenderer(){ mTriangle = new Triangle(); } // Renderer implements //------------------------------- @Override public void onDrawFrame(GL10 gl){ // Display Clear gl.glClearColor(1.0f,1.0f,0, 0); gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); // model mode gl.glMatrixMode(GL10.GL_MODELVIEW); gl.glLoadIdentity(); // move view point gl.glTranslatef(0, 0, -1.5f); // draw triangle gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); gl.glEnableClientState(GL10.GL_COLOR_ARRAY); mTriangle.draw(gl); } @Override public void onSurfaceChanged(GL10 gl, int width, int height){ // set viewport gl.glViewport(0, 0, width, height); // frustum rendering area in perspective projection system float ratio = (float) width/ height; gl.glMatrixMode(GL10.GL_PROJECTION); gl.glLoadIdentity(); gl.glFrustumf(-ratio, ratio, -1, 1, 1, 10); } @Override public void onSurfaceCreated(GL10 gl, EGLConfig config){ } private Triangle mTriangle; }
三角形。
import java.nio.ByteBuffer; import java.nio.IntBuffer; import java.nio.ByteOrder; import javax.microedition.khronos.opengles.*; public class Triangle{ public Triangle(){ // set vertices and colors int one = 0x10000; int vertices[] ={ -one,0,0, // left one,0,0, // right 0,one,0 // top }; int colors[]={ one,0,0,one, // red 0,one,0,one, // green 0,0,one,one // blue }; byte indices[]={ 0,1,2 }; // prepare vertex ByteBuffer vertexBuffer = ByteBuffer.allocateDirect(vertices.length*4); vertexBuffer.order(ByteOrder.nativeOrder()); mVertexBuffer = vertexBuffer.asIntBuffer(); mVertexBuffer.put(vertices); mVertexBuffer.position(0); // prepare color ByteBuffer colorBuffer = ByteBuffer.allocateDirect(colors.length*4); colorBuffer.order(ByteOrder.nativeOrder()); mColorBuffer = colorBuffer.asIntBuffer(); mColorBuffer.put(colors); mColorBuffer.position(0); // prepare index mIndexBuffer = ByteBuffer.allocateDirect(indices.length); mIndexBuffer.put(indices); mIndexBuffer.position(0); } public void draw(GL10 gl){ // Culling gl.glFrontFace(GL10.GL_CW); // set Vertex gl.glVertexPointer(3, GL10.GL_FIXED, 0, mVertexBuffer); // set Color gl.glColorPointer(4, GL10.GL_FIXED, 0, mColorBuffer); // draw gl.glDrawElements(GL10.GL_TRIANGLES, 3, GL10.GL_UNSIGNED_BYTE, mIndexBuffer); } private IntBuffer mVertexBuffer; private IntBuffer mColorBuffer; private ByteBuffer mIndexBuffer; }
androidでopenglesをつかってみる。画面の表示
まずは、アクティビティの設定。
import android.app.Activity; import android.os.Bundle; import android.opengl.GLSurfaceView; public class MainActivity extends Activity { private GLSurfaceView mGLView; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //setContentView(R.layout.main); mGLView = new GLSurfaceView(this); mGLView.setRenderer(new GLRenderer()); this.setContentView(mGLView); } @Override protected void onPause(){ super.onPause(); mGLView.onPause(); } @Override protected void onResume(){ super.onResume(); mGLView.onResume(); } }
次にレンダラー。
import android.opengl.GLSurfaceView; import javax.microedition.khronos.egl.*; import javax.microedition.khronos.opengles.*; public class GLRenderer implements GLSurfaceView.Renderer{ // Renderer //------------------------------- @Override public void onDrawFrame(GL10 gl){ gl.glClearColor(1.0f, 1.0f, 0.0f, 1.0f);//背景色 gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); } @Override public void onSurfaceChanged(GL10 gl, int width, int height){ gl.glViewport(0, 0, width, height); } @Override public void onSurfaceCreated(GL10 gl, EGLConfig config){ } }
androidのopenglesをつかってみる。立方体の描画
ひきつづき、Cubeの描画。
activityは変更なし。
public class MainActivity extends Activity { private GLSurfaceView mGLView; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //setContentView(R.layout.main); mGLView = new GLSurfaceView(this); mGLView.setRenderer(new GLRenderer()); this.setContentView(mGLView); } @Override protected void onPause(){ super.onPause(); mGLView.onPause(); } @Override protected void onResume(){ super.onResume(); mGLView.onResume(); } }
レンダリング。
変わったのは gl.glEnable(GL10.GL_CULL_FACE) の部分で、カリングをonにして立方体の見えない部分を描画しないようにしている。
import android.opengl.GLSurfaceView; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.opengles.GL10; public class GLRenderer implements GLSurfaceView.Renderer{ public GLRenderer(){ mCube = new Cube(); mAngle = 0; } // Renderer implements //------------------------------- @Override public void onDrawFrame(GL10 gl){ // Display Clear gl.glClearColor(1.0f,1.0f,0, 0); gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); // model mode gl.glMatrixMode(GL10.GL_MODELVIEW); gl.glLoadIdentity(); // move view point gl.glTranslatef(0, 0, -5.0f); gl.glRotatef( mAngle, 1.0f, 1.0f, 0); // draw cube gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); gl.glEnableClientState(GL10.GL_COLOR_ARRAY); gl.glEnable(GL10.GL_CULL_FACE); //enable background culling mCube.draw(gl); mAngle+=1.2f; } @Override public void onSurfaceChanged(GL10 gl, int width, int height){ // set viewport gl.glViewport(0, 0, width, height); // frustum rendering area in perspective projection system float ratio = (float) width/ height; gl.glMatrixMode(GL10.GL_PROJECTION); gl.glLoadIdentity(); gl.glFrustumf(-ratio, ratio, -1, 1, 1, 10); } @Override public void onSurfaceCreated(GL10 gl, EGLConfig config){ } private Cube mCube; private float mAngle; }
Cube。
import java.nio.ByteBuffer; import java.nio.IntBuffer; import java.nio.ByteOrder; import javax.microedition.khronos.opengles.GL10; public class Cube{ public Cube(){ int one = 0x10000; int vertices[] = { -one, -one, -one, one, -one, -one, one, one, -one, -one, one, -one, -one, -one, one, one, -one, one, one, one, one, -one, one, one }; int colors[] = { 0, 0, 0, one, one, 0, 0, one, one, one, 0, one, 0, one, 0, one, 0, 0, one, one, one, 0, one, one, one, one, one, one, 0, one, one, one }; byte indices[] = { 0,4,5, 0,5,1, 1,5,6, 1,6,2, 2,6,7, 2,7,3, 3,7,4, 3,4,0, 4,7,6, 4,6,5, 3,0,1, 3,1,2 }; mVertexBuffer = setupIntBuffer(vertices); mColorBuffer = setupIntBuffer(colors); mIndexBuffer = setupByteBuffer(indices); } // draw //------------------------- public void draw(GL10 gl){ gl.glFrontFace(GL10.GL_CW); gl.glVertexPointer(3, GL10.GL_FIXED, 0, mVertexBuffer); gl.glColorPointer(4, GL10.GL_FIXED, 0, mColorBuffer); gl.glDrawElements(GL10.GL_TRIANGLES, mIndexBuffer.capacity(), GL10.GL_UNSIGNED_BYTE, mIndexBuffer); } // util //------------------------- private IntBuffer setupIntBuffer( int[] arrays ){ // prepare vertex ByteBuffer arrayBuffer = ByteBuffer.allocateDirect(arrays.length*4); arrayBuffer.order(ByteOrder.nativeOrder()); IntBuffer intBuffer = arrayBuffer.asIntBuffer(); intBuffer.put(arrays); intBuffer.position(0); return intBuffer; } private ByteBuffer setupByteBuffer( byte []arrays ){ ByteBuffer byteBuffer = ByteBuffer.allocateDirect(arrays.length); byteBuffer.put(arrays); byteBuffer.position(0); return byteBuffer; } private IntBuffer mColorBuffer; private IntBuffer mVertexBuffer; private ByteBuffer mIndexBuffer; }
android 2.2 (froyo) と1.6 (donut) でカメラプレビュー
HTC desire HD (2.2) でカメラのpreviewサイズを設定すると「予期せぬエラーで終了しました」がでる。
ぐぐってみるとどうやら2.1-1 以降でこの問題が起きるらしい。
http://code.google.com/p/android/issues/detail?id=7909
試してはないけど、sdk付属のカメラサンプルですら起動失敗する様子。
ここでの議論を要約すると、
- 2.1-1でsdkのカメラサンプルすら動かんorz
- setPreviewSizeをコメントアウトすればとりあえず動くよ。
- あと写真サイズの変更は問題なくできるよ。
- でもそれってなんかやだよね。
- このコード使ってちょ
private Size getOptimalPreviewSize(List<Size> sizes, int w, int h) { final double ASPECT_TOLERANCE = 0.05; double targetRatio = (double) w / h; if (sizes == null) return null; Size optimalSize = null; double minDiff = Double.MAX_VALUE; int targetHeight = h; // Try to find an size match aspect ratio and size for (Size size : sizes) { double ratio = (double) size.width / size.height; if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE) continue; if (Math.abs(size.height - targetHeight) < minDiff) { optimalSize = size; minDiff = Math.abs(size.height - targetHeight); } } // Cannot find the one match the aspect ratio, ignore the requirement if (optimalSize == null) { minDiff = Double.MAX_VALUE; for (Size size : sizes) { if (Math.abs(size.height - targetHeight) < minDiff) { optimalSize = size; minDiff = Math.abs(size.height - targetHeight); } } } return optimalSize; } public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) { // Now that the size is known, set up the camera parameters and begin // the preview. Camera.Parameters parameters = mCamera.getParameters(); List<Size> sizes = parameters.getSupportedPreviewSizes(); Size optimalSize = getOptimalPreviewSize(sizes, w, h); parameters.setPreviewSize(optimalSize.width, optimalSize.height); mCamera.setParameters(parameters); mCamera.startPreview(); }
という感じだった。
これだけだとgetSupportedPreviewSizes()がapi level 5以降でないと対応してないから、HT-03AとかXperiaだとエラー出る。
なので汎用的に1.6でも2.2でも動くようにするにはオーバーラップ関数を自分で作らなくちゃならない。
同じ問題に直面してリフレクションで実装してる例があったで参考にした。
http://labs.techfirm.co.jp/android/cho/1647
これでparameters.getSupportedPreviewSizes()の部分をReflect.getSupportedParameters(params)に変更してあげればダイジョブなはず。
実際に書いたコード
import android.hardware.Camera; import android.hardware.Camera.*; import android.view.*; import android.content.*; import android.util.*; import java.io.*; import java.util.*; public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback{ private final static String LOGTAG="CameraPreview"; protected SurfaceHolder holder; protected Camera camera; public CameraPreview(Context context){ super(context); holder=getHolder(); holder.addCallback(this); holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); } public void surfaceCreated(SurfaceHolder holder){ camera=camera.open(); try{ camera.setPreviewDisplay(holder); }catch(IOException e){ Log.d(LOGTAG,"setPreviewDisplay exception"); } } public void surfaceChanged(SurfaceHolder holder, int format, int width, int height){ camera.stopPreview(); Camera.Parameters params=camera.getParameters(); // params.setPreviewFormat(PixelFormat.JPEG); //if clear this comment, it cause setParameter Exception in api-level 5 emulator. List<Size> sizes=Reflect.getSupportedPreviewSizes(params); if(null != sizes){ //for(int i=0;i<sizes.size();i++){ // Size s=sizes.get(i); // Log.d(LOGTAG,"["+i+"]:"+s.width+","+s.height); //} Size optimalSize = getOptimalPreviewSize(sizes,width,height); //Log.d(LOGTAG,"optimalsize:"+optimalSize.width+","+optimalSize.height); //Log.d(LOGTAG,"defaultsize:"+width+","+height); params.setPreviewSize(optimalSize.width, optimalSize.height); }else{ //Log.d(LOGTAG,"set previewsize normally:"+width+","+height); params.setPreviewSize(width, height); } camera.setParameters(params); camera.startPreview(); } public void surfaceDestroyed(SurfaceHolder holder){ camera.stopPreview(); camera.release(); } private Size getOptimalPreviewSize(List<Size> sizes, int w, int h) { final double ASPECT_TOLERANCE = 0.05; double targetRatio = (double) w / h; if (sizes == null) return null; Size optimalSize = null; double minDiff = Double.MAX_VALUE; int targetHeight = h; // Try to find an size match aspect ratio and size for (Size size : sizes) { double ratio = (double) size.width / size.height; if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE) continue; if (Math.abs(size.height - targetHeight) < minDiff) { optimalSize = size; minDiff = Math.abs(size.height - targetHeight); } } // Cannot find the one match the aspect ratio, ignore the requirement if (optimalSize == null) { minDiff = Double.MAX_VALUE; for (Size size : sizes) { if (Math.abs(size.height - targetHeight) < minDiff) { optimalSize = size; minDiff = Math.abs(size.height - targetHeight); } } } return optimalSize; } }
Reflect.javaはもらってきたものから特に変更なし
import android.hardware.Camera; import android.hardware.Camera.*; import android.util.Log; import java.lang.reflect.*; import java.util.*; public class Reflect{ private final static String LOGTAG="Reflect"; private static Method Parameters_getSupportedPreviewSizes; static{ initCompatibility(); } private static void initCompatibility(){ try{ Parameters_getSupportedPreviewSizes = Camera.Parameters.class .getMethod("getSupportedPreviewSizes",new Class[]{}); }catch(NoSuchMethodException e){ Log.d(LOGTAG,"no such method exception"); } } @SuppressWarnings("unchecked") public static List<Size> getSupportedPreviewSizes(Camera.Parameters p){ try{ if(Parameters_getSupportedPreviewSizes != null){ return (List<Size>)Parameters_getSupportedPreviewSizes.invoke(p); }else{ return null; } }catch(InvocationTargetException e){ Log.e(LOGTAG,"InvocationTargetException"); Throwable cause=e.getCause(); if(cause instanceof RuntimeException){ throw (RuntimeException)cause; }else if(cause instanceof Error){ throw (Error)cause; }else{ throw new RuntimeException(e); } }catch(IllegalAccessException e){ Log.e(LOGTAG,"IllegalAccessException"); return null; } } }
パーミッションの設定は
SDKのバージョンは4でテスト。
あとAndroidManifest.xmlのActivityに横向き、全表示追加。
android:screenOrientation="landscape"
android:theme="@android:style/Theme.Black.NoTitleBar.Fullscreen"
HT-03A,HTC desire HDで実機動作の確認しました。
emulatorは1.6と2.2でちゃんと動きます。