Androidアプリ開発 OpenGL ポイントスプライト

本日は「ポイントスプライト」をやってみる。AREarthroidでは使用していないテクニックだが、うまくいけば組み込むかも知れない。

ポイントは「点」という意味。ポリゴンは面なので、頂点がいくつか集まって構成されている。OpenGL ESでは、三角形しか扱えないので、ポリゴンの頂点数は3となる。
OpenGLでは、面しか描けないかというとそうでもなく、GL_LINESなら線を描けるし、GL_POINTSなら点を描画することができる。OpenGL ESにも点、線の描画機能がある。
スプライトっていうのは、ゲーム用語なのかな。2Dゲーム中のキャラクタを、高速に表示させるためのしくみのことを言っていたと記憶している。
で、3DCGのポイントスプライトになると、表示する位置は、点で決めてやって、そこにいつもこちら向きにないように仮想的な面を作って、テクスチャマッピングして表示する。っていうことらしい。
パーティクルを使った粒子シミュレーションでは、多くの点を描画しないといけない。量が多くなれば、それだけ精密に見える。面より点の方がデータ量が少ないので、大量のパーティクルを作りたいときは点の方が有利。
うんちくはこれくらいにして、まずは、点を描画してみよう。
まじめにデータを作成していると日が暮れるので、乱数で生成することにした。

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import javax.microedition.khronos.opengles.GL10;
public class Points {
private FloatBuffer buffer;			// 頂点用バッファ
private final int numOfPoints = 1000;
public Points() {
ByteBuffer vb = ByteBuffer.allocateDirect(numOfPoints * 3 * 4);
vb.order(ByteOrder.nativeOrder());
buffer = vb.asFloatBuffer();
float x, y, z;
for ( int i = 0; i < numOfPoints; i++ ){
x = (float)(Math.random() * 8.0 - 4.0);
y = (float)(Math.random() * 8.0 - 4.0);
z = (float)(Math.random() * 8.0 - 6.0);
buffer.put(x);
buffer.put(y);
buffer.put(z);
}
buffer.position(0);
}
public void draw(GL10 gl) {
gl.glPushMatrix();		// マトリックス記憶
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, buffer);
gl.glDrawArrays(GL10.GL_POINTS, 0, numOfPoints);
gl.glPopMatrix();		// マトリックスを戻す
}
}

クラスを追加した。ソースファイルはPoints.javaである。
何をやっているかというと、1000個の点をランダムな位置に生成して描画いる。っていうだけ。
これをレンダラーで描画する。

public class SampleGLSurfaceView extends GLSurfaceView implements OnGestureListener {
private ArrayList models = new ArrayList();
private Points points = new Points();
private float eyepos[] = new float[3];

Pointsのインスタンスは、フィールドpointsで作成。
onDrawFrameで、ドロイド君を描画する前でpointsのdrawを呼び出して描画っと。

@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, yellow, 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_BLEND);
gl.glEnable(GL10.GL_TEXTURE_2D);
// スムースシェーディング
gl.glShadeModel(GL10.GL_SMOOTH);
// フォグ
gl.glEnable(GL10.GL_FOG);
gl.glFogfv(GL10.GL_FOG_COLOR, black, 0);
gl.glFogx(GL10.GL_FOG_MODE, GL10.GL_LINEAR);
gl.glFogf(GL10.GL_FOG_START, 3.0f);
gl.glFogf(GL10.GL_FOG_END, 10.0f);
// ポイントスプライトを表示
gl.glMaterialfv(GL10.GL_FRONT_AND_BACK, GL10.GL_AMBIENT, white, 0);
gl.glMaterialfv(GL10.GL_FRONT_AND_BACK, GL10.GL_DIFFUSE, white, 0);
gl.glMaterialfv(GL10.GL_FRONT_AND_BACK, GL10.GL_SPECULAR, white, 0);
gl.glMaterialf(GL10.GL_FRONT_AND_BACK, GL10.GL_SHININESS, 80f);
gl.glDisable(GL10.GL_TEXTURE_2D);
gl.glDisable(GL10.GL_BLEND);
points.draw(gl);
// ソート
Collections.sort(models, new ModelComparator());
for ( Model m : models ){
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);
gl.glEnable(GL10.GL_BLEND);
gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA);
m.draw(gl);
// 2回目の表示はハイライトを出すため
gl.glMaterialfv(GL10.GL_FRONT_AND_BACK, GL10.GL_AMBIENT, transparent, 0);
gl.glMaterialfv(GL10.GL_FRONT_AND_BACK, GL10.GL_DIFFUSE, half_white, 0);
gl.glMaterialfv(GL10.GL_FRONT_AND_BACK, GL10.GL_SPECULAR, half_white, 0);
gl.glMaterialf(GL10.GL_FRONT_AND_BACK, GL10.GL_SHININESS, 80f);
gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE);
m.draw(gl);
m.rotate();
}
}


おお、フォグも効いているので、宇宙空間的な感じになったではないか。
これは、AREarthroidにも組み込まないといけないか。
ここまでで、点を描画できたので、拡張して、テクスチャを貼ってやれば、ポイントスプライトの完成となる。

@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, yellow, 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_BLEND);
gl.glEnable(GL10.GL_TEXTURE_2D);
// スムースシェーディング
gl.glShadeModel(GL10.GL_SMOOTH);
// フォグ
gl.glEnable(GL10.GL_FOG);
gl.glFogfv(GL10.GL_FOG_COLOR, black, 0);
gl.glFogx(GL10.GL_FOG_MODE, GL10.GL_LINEAR);
gl.glFogf(GL10.GL_FOG_START, 3.0f);
gl.glFogf(GL10.GL_FOG_END, 10.0f);
// ポイントスプライトを表示
gl.glMaterialfv(GL10.GL_FRONT_AND_BACK, GL10.GL_AMBIENT, white, 0);
gl.glMaterialfv(GL10.GL_FRONT_AND_BACK, GL10.GL_DIFFUSE, white, 0);
gl.glMaterialfv(GL10.GL_FRONT_AND_BACK, GL10.GL_SPECULAR, white, 0);
gl.glMaterialf(GL10.GL_FRONT_AND_BACK, GL10.GL_SHININESS, 80f);
gl.glEnable(GL10.GL_TEXTURE_2D);
gl.glEnable(GL10.GL_BLEND);
gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA);
//ポイントスプライト
gl.glEnable(GL11.GL_POINT_SPRITE_OES);
gl.glActiveTexture(GL10.GL_TEXTURE0);
gl.glTexEnvf(GL11.GL_POINT_SPRITE_OES, GL11.GL_COORD_REPLACE_OES, GL10.GL_TRUE);
gl.glPointSize(32.0f);
points.draw(gl);


わわ、ドロイド君がいっぱい。

投稿者プロフィール

asai
asai
システムエンジニア
喋れる言語:日本語、C言語、SQL、JavaScript