Androidアプリ開発 OpenGL アルファブレンド 物体のソート [Android OpenGL]
アルファブレンドで正しくドロイド君を表示させるため、視点からの距離でソートするように修正していく。ついでなので、四角形にしてちゃんと表示できるようにも変更しよう。
まず、Modelの頂点データを修正して、三角形をふたつ作る。また、物体の中心位置をフィールドx,y,zで覚えておくようにする。これでソートする。
次、視点との距離を計算するメソッドを作成する。
次、drawでマトリックスのプッシュ、移動、ポップを行うようにする。移動は、フィールドの座標値で行う。頂点が増えたので、glDrawArraysの頂点数を6に増加させる。
次、モデルのリストを作成する。
次、onSurfaceCreatedで、モデルを4つ作成してリストに入れる。
次、ソート時の比較用クラスを作成する。
そして最後、ソートして描画する。
ほえー、けっこう大変だったが、我ながら手際よくできたと思っている。
結果はごらんのとおり。
変更部分が結構多かったので、念のためソース全体を載せておく。
SampleGLSurfaceView.java
Model.java
サイト内を検索
まず、Modelの頂点データを修正して、三角形をふたつ作る。また、物体の中心位置をフィールドx,y,zで覚えておくようにする。これでソートする。
public class Model { private FloatBuffer buffer; // 頂点用バッファ private FloatBuffer normalBuffer; // 法線用バッファ private FloatBuffer textureBuffer; // テクスチャ用バッファ private float x, y, z; public Model(float x, float y, float z) { this.x = x; this.y = y; this.z = z; float vertex[] = { -1.0f, -1.0f, 0.0f, 1.0f, -1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, -1.0f, 1.0f, 0.0f, -1.0f, -1.0f, 0.0f, }; ByteBuffer vb = ByteBuffer.allocateDirect(vertex.length * 4); vb.order(ByteOrder.nativeOrder()); buffer = vb.asFloatBuffer(); buffer.put(vertex); buffer.position(0); float normal[] = { 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, }; ByteBuffer nb = ByteBuffer.allocateDirect(normal.length * 4); nb.order(ByteOrder.nativeOrder()); normalBuffer = nb.asFloatBuffer(); normalBuffer.put(normal); normalBuffer.position(0); float texture[] = { 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, }; ByteBuffer tb = ByteBuffer.allocateDirect(texture.length * 4); tb.order(ByteOrder.nativeOrder()); textureBuffer = tb.asFloatBuffer(); textureBuffer.put(texture); textureBuffer.position(0); }
次、視点との距離を計算するメソッドを作成する。
public float distance(float ex, float ey, float ez){ double dx = this.x - ex; double dy = this.y - ey; double dz = this.z - ez; return (float)Math.sqrt(dx * dx + dy * dy + dz * dz); }
次、drawでマトリックスのプッシュ、移動、ポップを行うようにする。移動は、フィールドの座標値で行う。頂点が増えたので、glDrawArraysの頂点数を6に増加させる。
public void draw(GL10 gl) { gl.glPushMatrix(); // マトリックス記憶 gl.glTranslatef(x, y, z); gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); gl.glVertexPointer(3, GL10.GL_FLOAT, 0, buffer); gl.glEnableClientState(GL10.GL_NORMAL_ARRAY); gl.glNormalPointer(GL10.GL_FLOAT, 0, normalBuffer); gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY); gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, textureBuffer); gl.glDrawArrays(GL10.GL_TRIANGLES, 0, 6); gl.glPopMatrix(); // マトリックスを戻す }
次、モデルのリストを作成する。
public class SampleGLSurfaceView extends GLSurfaceView implements OnGestureListener { private ArrayList<Model> models = new ArrayList<Model>(); private float angle = 0.0f; private float eyepos[] = new float[3]; private Context context;
次、onSurfaceCreatedで、モデルを4つ作成してリストに入れる。
@Override public void onSurfaceCreated(GL10 gl, EGLConfig config) { gl.glClearColor(0.0f, 0.0f, 0.0f, 1.0f); gl.glClearDepthf(1.0f); eyepos[0] = 0; eyepos[1] = 0; eyepos[2] = 3; Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.ic_launcher); // テクスチャを生成 int textures[] = new int[1]; gl.glGenTextures(1, textures, 0); gl.glBindTexture(GL10.GL_TEXTURE_2D, textures[0]); gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LINEAR); gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR); GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0); models.add(new Model(1, 0, 0)); models.add(new Model(-1, 0, 0)); models.add(new Model(1, 0, -2)); models.add(new Model(-1, 0, -2)); }
次、ソート時の比較用クラスを作成する。
class ModelComparator implements Comparator<Model> { public int compare(Model s, Model t) { float ds = s.distance(eyepos[0], eyepos[1], eyepos[2]); float dt = t.distance(eyepos[0], eyepos[1], eyepos[2]); if ( ds == dt ) return 0; else if ( ds < dt ) return 1; else return -1; } }
そして最後、ソートして描画する。
@Override public void onDrawFrame(GL10 gl) { gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); // ライティングをON gl.glEnable(GL10.GL_LIGHTING); // 光源を有効にして位置を設定 gl.glEnable(GL10.GL_LIGHT0); gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_POSITION, lightpos, 0); gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_AMBIENT, white, 0); gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_DIFFUSE, white, 0); gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_SPECULAR, white, 0); gl.glMatrixMode(GL10.GL_MODELVIEW); gl.glLoadIdentity(); // カメラ位置を設定 GLU.gluLookAt(gl, eyepos[0], eyepos[1], eyepos[2], 0, 0, 0, 0, 1, 0); /* // デプステスト gl.glEnable(GL10.GL_DEPTH_TEST); gl.glDepthFunc(GL10.GL_LEQUAL); gl.glDepthMask(true); */ // アルファブレンド gl.glEnable(GL10.GL_BLEND); gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA); /* // アルファテスト gl.glEnable(GL10.GL_ALPHA_TEST); gl.glAlphaFunc(GL10.GL_GEQUAL, 0.1f); */ // 陰面消去 gl.glEnable(GL10.GL_CULL_FACE); gl.glCullFace(GL10.GL_BACK); // スムースシェーディング gl.glShadeModel(GL10.GL_SMOOTH); // マテリアル gl.glMaterialfv(GL10.GL_FRONT_AND_BACK, GL10.GL_AMBIENT, gray, 0); gl.glMaterialfv(GL10.GL_FRONT_AND_BACK, GL10.GL_DIFFUSE, gray, 0); gl.glMaterialfv(GL10.GL_FRONT_AND_BACK, GL10.GL_SPECULAR, gray, 0); gl.glMaterialf(GL10.GL_FRONT_AND_BACK, GL10.GL_SHININESS, 80f); // テクスチャ gl.glEnable(GL10.GL_TEXTURE_2D); // ソートして描画 Collections.sort(models, new ModelComparator()); for ( Model m : models ){ m.draw(gl); } }
ほえー、けっこう大変だったが、我ながら手際よくできたと思っている。
結果はごらんのとおり。
変更部分が結構多かったので、念のためソース全体を載せておく。
SampleGLSurfaceView.java
package cx.fam.asai.TestOpenGL; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.opengles.GL10; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.opengl.GLSurfaceView; import android.opengl.GLU; import android.opengl.GLUtils; import android.view.GestureDetector.OnGestureListener; import android.view.MotionEvent; public class SampleGLSurfaceView extends GLSurfaceView implements OnGestureListener { private ArrayList<Model> models = new ArrayList<Model>(); private float angle = 0.0f; private float eyepos[] = new float[3]; private Context context; // レンダラークラス class OpenGLRenderer implements Renderer { float lightpos[] = {0.0f, 0.0f, 4.0f, 0.0f}; float red[] = {1.0f, 0.0f, 0.0f, 1.0f}; float green[] = {0.0f, 1.0f, 0.0f, 1.0f}; float blue[] = {0.0f, 0.0f, 1.0f, 1.0f}; float white[] = {1.0f, 1.0f, 1.0f, 1.0f}; float gray[] = {0.5f, 0.5f, 0.5f, 1.0f}; float yellow[] = {1.0f, 1.0f, 0.0f, 1.0f}; @Override public void onDrawFrame(GL10 gl) { gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); // ライティングをON gl.glEnable(GL10.GL_LIGHTING); // 光源を有効にして位置を設定 gl.glEnable(GL10.GL_LIGHT0); gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_POSITION, lightpos, 0); gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_AMBIENT, white, 0); gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_DIFFUSE, white, 0); gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_SPECULAR, white, 0); gl.glMatrixMode(GL10.GL_MODELVIEW); gl.glLoadIdentity(); // カメラ位置を設定 GLU.gluLookAt(gl, eyepos[0], eyepos[1], eyepos[2], 0, 0, 0, 0, 1, 0); /* // デプステスト gl.glEnable(GL10.GL_DEPTH_TEST); gl.glDepthFunc(GL10.GL_LEQUAL); gl.glDepthMask(true); */ // アルファブレンド gl.glEnable(GL10.GL_BLEND); gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA); /* // アルファテスト gl.glEnable(GL10.GL_ALPHA_TEST); gl.glAlphaFunc(GL10.GL_GEQUAL, 0.1f); */ // 陰面消去 gl.glEnable(GL10.GL_CULL_FACE); gl.glCullFace(GL10.GL_BACK); // スムースシェーディング gl.glShadeModel(GL10.GL_SMOOTH); // マテリアル gl.glMaterialfv(GL10.GL_FRONT_AND_BACK, GL10.GL_AMBIENT, gray, 0); gl.glMaterialfv(GL10.GL_FRONT_AND_BACK, GL10.GL_DIFFUSE, gray, 0); gl.glMaterialfv(GL10.GL_FRONT_AND_BACK, GL10.GL_SPECULAR, gray, 0); gl.glMaterialf(GL10.GL_FRONT_AND_BACK, GL10.GL_SHININESS, 80f); // テクスチャ gl.glEnable(GL10.GL_TEXTURE_2D); // ソートして描画 Collections.sort(models, new ModelComparator()); for ( Model m : models ){ m.draw(gl); } } class ModelComparator implements Comparator<Model> { public int compare(Model s, Model t) { float ds = s.distance(eyepos[0], eyepos[1], eyepos[2]); float dt = t.distance(eyepos[0], eyepos[1], eyepos[2]); if ( ds == dt ) return 0; else if ( ds < dt ) return 1; else return -1; } } @Override public void onSurfaceChanged(GL10 gl, int width, int height) { gl.glViewport(0, 0, width, height); gl.glMatrixMode(GL10.GL_PROJECTION); gl.glLoadIdentity(); GLU.gluPerspective(gl, 50f, (float)width / height, 0.01f, 100f); } @Override public void onSurfaceCreated(GL10 gl, EGLConfig config) { gl.glClearColor(0.0f, 0.0f, 0.0f, 1.0f); gl.glClearDepthf(1.0f); eyepos[0] = 0; eyepos[1] = 0; eyepos[2] = 3; Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.ic_launcher); // テクスチャを生成 int textures[] = new int[1]; gl.glGenTextures(1, textures, 0); gl.glBindTexture(GL10.GL_TEXTURE_2D, textures[0]); gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LINEAR); gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR); GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0); models.add(new Model(1, 0, 0)); models.add(new Model(-1, 0, 0)); models.add(new Model(1, 0, -2)); models.add(new Model(-1, 0, -2)); } } private OpenGLRenderer renderer; // サーフェースビューのコンストラクタ public SampleGLSurfaceView(Context context) { super(context); this.context = context; setEGLConfigChooser(8, 8, 8, 8, 16, 0); renderer = new OpenGLRenderer(); setRenderer(renderer); } @Override public boolean onDown(MotionEvent arg0) { return false; } @Override public boolean onFling(MotionEvent arg0, MotionEvent arg1, float arg2, float arg3) { return false; } @Override public void onLongPress(MotionEvent arg0) { } @Override public boolean onScroll(MotionEvent event1, MotionEvent event2, float distx, float disty) { eyepos[0] += distx * 0.01; eyepos[1] += disty * 0.01; return true; } @Override public void onShowPress(MotionEvent arg0) { } @Override public boolean onSingleTapUp(MotionEvent arg0) { return false; } }
Model.java
package cx.fam.asai.TestOpenGL; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.FloatBuffer; import javax.microedition.khronos.opengles.GL10; public class Model { private FloatBuffer buffer; // 頂点用バッファ private FloatBuffer normalBuffer; // 法線用バッファ private FloatBuffer textureBuffer; // テクスチャ用バッファ private float x, y, z; public Model(float x, float y, float z) { this.x = x; this.y = y; this.z = z; float vertex[] = { -1.0f, -1.0f, 0.0f, 1.0f, -1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, -1.0f, 1.0f, 0.0f, -1.0f, -1.0f, 0.0f, }; ByteBuffer vb = ByteBuffer.allocateDirect(vertex.length * 4); vb.order(ByteOrder.nativeOrder()); buffer = vb.asFloatBuffer(); buffer.put(vertex); buffer.position(0); float normal[] = { 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, }; ByteBuffer nb = ByteBuffer.allocateDirect(normal.length * 4); nb.order(ByteOrder.nativeOrder()); normalBuffer = nb.asFloatBuffer(); normalBuffer.put(normal); normalBuffer.position(0); float texture[] = { 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, }; ByteBuffer tb = ByteBuffer.allocateDirect(texture.length * 4); tb.order(ByteOrder.nativeOrder()); textureBuffer = tb.asFloatBuffer(); textureBuffer.put(texture); textureBuffer.position(0); } public float distance(float ex, float ey, float ez){ double dx = this.x - ex; double dy = this.y - ey; double dz = this.z - ez; return (float)Math.sqrt(dx * dx + dy * dy + dz * dz); } public void draw(GL10 gl) { gl.glPushMatrix(); // マトリックス記憶 gl.glTranslatef(x, y, z); gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); gl.glVertexPointer(3, GL10.GL_FLOAT, 0, buffer); gl.glEnableClientState(GL10.GL_NORMAL_ARRAY); gl.glNormalPointer(GL10.GL_FLOAT, 0, normalBuffer); gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY); gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, textureBuffer); gl.glDrawArrays(GL10.GL_TRIANGLES, 0, 6); gl.glPopMatrix(); // マトリックスを戻す } }
サイト内を検索
2012-04-13 09:57
nice!(0)
コメント(0)
Copyright Atsushi Asai Google+朝井淳
[データベースの気持ちがわかる]SQLはじめの一歩 (WEB+DB PRESS plus)
- 作者: 朝井 淳
- 出版社/メーカー: 技術評論社
- 発売日: 2015/03/03
- メディア: 単行本(ソフトカバー)
Access クエリ 徹底活用ガイド ~仕事の現場で即使える
- 作者: 朝井 淳
- 出版社/メーカー: 技術評論社
- 発売日: 2018/05/25
- メディア: 大型本
コメント 0