▼Androidメモ▼
3Dモデルの読み込み


OpenGL ES 2.0で3Dモデルの読み込みを行うプログラムを作成する。
HelloGL20_21.png

3Dモデルファイル
以下の2つのファイルをプロジェクトのassetsに配置。
objファイルの書式はこちら
mltファイルの書式はこちら

ソースコード
GL20ModelEx1.java
package net.npaka.gl20modelex1;
import android.app.Activity;
import android.opengl.GLSurfaceView;
import android.os.Bundle;

//3Dモデルの読み込み
public class GL20ModelEx1 extends Activity {
private GLSurfaceView glView;

//アクティビティ生成時に呼ばれる
@Override
public void onCreate(Bundle bundle) {
super.onCreate(bundle);

//GLサーフェイスビュー
glView=new GLSurfaceView(this);
glView.setEGLContextClientVersion(2);
glView.setRenderer(new GLRenderer(this));
setContentView(glView);
}

//アクティビティレジューム時に呼ばれる
@Override
public void onResume() {
super.onResume();
glView.onResume();
}

//アクティビティポーズ時に呼ばれる
@Override
public void onPause() {
super.onPause();
glView.onPause();
}
}

GLRenderer.java
package net.npaka.gl20modelex1;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
import net.npaka.gles.GLES;
import net.npaka.gles.ObjLoader;
import net.npaka.gles.Object3D;
import android.content.Context;
import android.opengl.GLES20;
import android.opengl.GLSurfaceView;
import android.opengl.Matrix;

//レンダラー
public class GLRenderer implements
GLSurfaceView.Renderer {
//システム
private float aspect;//アスペクト比
private int angle; //回転角度

//モデル
private Object3D model=new Object3D();

//コンストラクタ
public GLRenderer(Context context) {
GLES.context=context;
}

//サーフェイス生成時に呼ばれる
@Override
public void onSurfaceCreated(GL10 gl10,EGLConfig eglConfig) {
//プログラムの生成
GLES.makeProgram();

//デプスバッファの有効化
GLES20.glEnable(GLES20.GL_DEPTH_TEST);

//光源の有効化
GLES20.glUniform1i(GLES.useLightHandle,1);

//光源色の指定
GLES20.glUniform4f(GLES.lightAmbientHandle,0.2f,0.2f,0.2f,1.0f);
GLES20.glUniform4f(GLES.lightDiffuseHandle,0.7f,0.7f,0.7f,1.0f);
GLES20.glUniform4f(GLES.lightSpecularHandle,0.9f,0.9f,0.9f,1.0f);

//モデルの読み込み
try {
model.figure=ObjLoader.load("droid.obj");
} catch (Exception e) {
android.util.Log.e("debug",e.toString());
for (StackTraceElement ste:e.getStackTrace()) {
android.util.Log.e("debug"," "+ste);
}
}
}

//画面サイズ変更時に呼ばれる
@Override
public void onSurfaceChanged(GL10 gl10,int w,int h) {
//ビューポート変換
GLES20.glViewport(0,0,w,h);
aspect=(float)w/(float)h;
}

//毎フレーム描画時に呼ばれる
@Override
public void onDrawFrame(GL10 gl10) {
//画面のクリア
GLES20.glClearColor(1.0f,1.0f,1.0f,1.0f);
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT|
GLES20.GL_DEPTH_BUFFER_BIT);

//射影変換
Matrix.setIdentityM(GLES.pMatrix,0);
GLES.gluPerspective(GLES.pMatrix,
45.0f, //Y方向の画角
aspect, //アスペクト比
0.01f, //ニアクリップ
100.0f);//ファークリップ

//光源位置の指定
GLES20.glUniform4f(GLES.lightPosHandle,5.0f,5.0f,5.0f,1.0f);

//ビュー変換
Matrix.setIdentityM(GLES.mMatrix,0);
GLES.gluLookAt(GLES.mMatrix,
0.0f,0.8f,5.0f, //カメラの視点
0.0f,0.8f,0.0f, //カメラの焦点
0.0f,1.0f,0.0f);//カメラの上方向

//モデル変換
Matrix.rotateM(GLES.mMatrix,0,angle++,0,1,0);

//モデルの描画
model.draw();
}
}

モデル変換用ライブラリ
読み込んだ3Dモデルにモデル変換を適用するクラス郡を用意。
クラス名 説明
Object3D モデル変換を適用する3Dモ デル。
形状データ(Figure)とモデル変換のベクトル(Vector3D)を保持。
Vector3D 3要素のベクトル。
加算、減算、内積、外積、正規化といった計算メソッドを保持。

Object3D.java
package net.npaka.gles;
import java.util.ArrayList;
import android.opengl.Matrix;

//3Dオブジェクト
public class Object3D {
public Figure figure; //フィギュア
public Vector3 position=new Vector3(); //位置
public Vector3 rotate =new Vector3(); //回転
public Vector3 scale =new Vector3(1,1,1);//拡縮
public ArrayList<Object3D> childs=new ArrayList<Object3D>();//子

//描画
public void draw() {
GLES.glPushMatrix();
Matrix.translateM(GLES.mMatrix,0,position.x,position.y,position.z);
Matrix.rotateM(GLES.mMatrix,0,rotate.z,0,0,1);
Matrix.rotateM(GLES.mMatrix,0,rotate.y,0,1,0);
Matrix.rotateM(GLES.mMatrix,0,rotate.x,1,0,0);
Matrix.scaleM(GLES.mMatrix,0,scale.x,scale.y,scale.z);
GLES.updateMatrix();
figure.draw();
for (int i=0;i<childs.size();i++) childs.get(i).draw();
GLES.glPopMatrix();
}
}

Vector3.java
package net.npaka.gles;

//3要素ベクトル
public class Vector3 {
public float x=0.0f;//X座標
public float y=0.0f;//Y座標
public float z=0.0f;//Z座標

//コンストラクタ
public Vector3() {
}

//コンストラクタ
public Vector3(float x,float y,float z) {
set(x,y,z);
}

//コンストラクタ
public Vector3(Vector3 origin) {
set(origin);
}

//値の指定
public void set(Vector3 origin) {
x=origin.x;
y=origin.y;
z=origin.z;
}

//値の指定
public void set(float x,float y,float z) {
this.x=x;
this.y=y;
this.z=z;
}

//内積の計算
public float dot(Vector3 v) {
return (x*v.x)+(y*v.y)+(z*v.z);
}

//内積の計算
public float dot(float x,float y,float z) {
return (this.x*x)+(this.y*y)+(this.z*z);
}

//外積の計算
public Vector3 cross(Vector3 v,Vector3 result) {
result.set((y*v.z)-(z*v.y),(z*v.x)-(x*v.z),(x*v.y)-(y*v.x));
return result;
}

//外積の計算
public void cross(float x,float y,float z) {
set((this.y*z)-(this.z*y),(this.z*x)-(this.x*z),(this.x*y)-(this.y*x));
}

//和の計算
public void add(Vector3 v0,Vector3 v1) {
x=v0.x+v1.x;
y=v0.y+v1.y;
z=v0.z+v1.z;
}

//差の計算
public void sub(Vector3 v0,Vector3 v1) {
x=v0.x-v1.x;
y=v0.y-v1.y;
z=v0.z-v1.z;
}

//ベクトルの長さの取得
public float length() {
return (float)Math.sqrt((double)((x*x)+(y*y)+(z*z)));
}

//ベクトルの長さの正規化
public void normalize() {
final float len=length();
x/=len;
y/=len;
z/=len;
}

//値の比較
@Override
public boolean equals(Object o) {
Vector3 v=(Vector3)o;
return v.x==x && v.y==y && v.z==z;
}
}


3Dモデル読み込み用ライブラリ
3Dモデルを読み込むクラス郡を用意。
クラス名 説明
ObjLoader OBJファイルを読み込む ローダー。
ファイル名を指定し、Figureオブジェクトを取得。
GLES GLライブラリ全体で利用す る変数とメソッドを保持。
Figure 読み込み跡の形状データ。
メッシュ郡とマテリアル郡を保持。
Mesh メッシュ。
頂点バッファとインデックスバッファとマテリアルを保持。


GLObject OpenGLで bind/unbindを行うオブジェクト。
VertexBuffer/IndexBuffer/Material/Textureの親。
VertexBuffer
頂点バッファ。
IndexBuffer
インデックスバッファ。
Material マテリアル。
テクスチャとマテリアル色を保持。
Texture テクスチャ

ObjLoader.java
package net.npaka.gles;
import java.io.DataInputStream;
import java.util.ArrayList;
import java.util.HashMap;

//OBJローダー
public class ObjLoader {
//ロード
public static Figure load(String path) throws Exception {
DataInputStream in=new DataInputStream(
GLES.context.getAssets().open(path));

//頂点バッファとインデックスバッファ
ArrayList<float[]> positions=new ArrayList<float[]>();
ArrayList<float[]> normals =new ArrayList<float[]>();
ArrayList<float[]> uvs =new ArrayList<float[]>();
ArrayList<float[]> vertexs =new ArrayList<float[]>();
ArrayList<short[]> indexs =new ArrayList<short[]>();

//メッシュとマテリアル
ArrayList<Mesh> meshs=new ArrayList<Mesh>();
HashMap<String,Material> materials=new HashMap<String,Material>();

//パース
Material material=null;
boolean meshFlag=false;
String line=in.readLine();

while (line!=null) {
String[] word=line.split(" ",0);

//MTLファイルの読み込み
if (word[0].equals("mtllib")) {
loadMtl(materials,word[1]);
}
//マテリアル
else if (word[0].equals("usemtl")) {
material=materials.get(word[1]);
}
//頂点
else if (word[0].equals("v")) {
float[] position=new float[]{
Float.parseFloat(word[1]),
Float.parseFloat(word[2]),
Float.parseFloat(word[3])
};
positions.add(position);
}
//UV
else if (word[0].equals("vt")) {
float[] uv=new float[]{
Float.parseFloat(word[1]),
1.0f-Float.parseFloat(word[2])
};
uvs.add(uv);
}
//法線
else if (word[0].equals("vn")) {
float[] normal=new float[]{
Float.parseFloat(word[1]),
Float.parseFloat(word[2]),
Float.parseFloat(word[3])
};
normals.add(normal);
}
//インデックス
else if (word[0].equals("f")) {
meshFlag=true;
short[] index=new short[word.length-1];
for (int i=0;i<index.length;i++) {
String[] w=word[i+1].split("/",0);
index[i]=(short)addVertex(vertexs,
positions.get(Integer.parseInt(w[0])-1),
uvs.get(Integer.parseInt(w[1])-1),
normals.get(Integer.parseInt(w[2])-1));
}
indexs.add(index);
}
//メッシュの生成
else if (meshFlag) {
meshFlag=false;
Mesh mesh=new Mesh();
mesh.vertexBuffer=new VertexBuffer(vertexs);
mesh.indexBuffer =new IndexBuffer(indexs);
mesh.material =material;
meshs.add(mesh);
}
line=in.readLine();
}
in.close();

//フィギュアの生成
Figure figure=new Figure();
figure.materials=materials;
figure.meshs=meshs;
return figure;
}

//頂点の追加
private static int addVertex(ArrayList<float[]> vertexs,
float[] position,float[] uv,float[] normal) {
float[] vertex=new float[3+2+3];
System.arraycopy(position,0,vertex,0,3);
System.arraycopy(uv,0,vertex,3,2);
System.arraycopy(normal,0,vertex,3+2,3);

//既存の頂点
for (int i=0;i<vertexs.size();i++) {
float[] v=vertexs.get(i);
int j=0;
for (;j<8;j++) {
if (v[j]!=vertex[j]) break;
}
if (j==8) return i;
}

//新規の頂点
vertexs.add(vertex);
return vertexs.size()-1;
}

//MTLの読み込み
public static void loadMtl(HashMap<String,Material> materials,
String path) throws Exception {
DataInputStream in=new DataInputStream(
GLES.context.getAssets().open(path));
Material material=null;
materials.clear();
String line=in.readLine();
while (line!=null) {
String[] word=line.split(" ",0);
//マテリアル名
if (word[0].equals("newmtl")) {
material=new Material();
materials.put(word[1],material);
}
//環境光
else if (word[0].equals("Ka")) {
material.ambient=new float[]{
Float.parseFloat(word[1]),
Float.parseFloat(word[2]),
Float.parseFloat(word[3]),
1.0f};
}
//拡散光
else if (word[0].equals("Kd")) {
material.diffuse=new float[]{
Float.parseFloat(word[1]),
Float.parseFloat(word[2]),
Float.parseFloat(word[3]),
1.0f};
}
//鏡面光
else if (word[0].equals("Ks")) {
material.specular=new float[]{
Float.parseFloat(word[1]),
Float.parseFloat(word[2]),
Float.parseFloat(word[3]),
1.0f};
}
//鏡面反射角度
else if (word[0].equals("Ns")) {
material.shininess=Float.parseFloat(word[1]);
}
//テクスチャ
else if (word[0].equals("map_Kd")) {
material.texture=
Texture.createTextureFromAsset(word[1]);
}
line=in.readLine();
}
in.close();
}
}

GLES.java
package net.npaka.gles;
import java.util.ArrayList;
import android.content.Context;
import android.opengl.GLES20;
import android.opengl.Matrix;

//GLES
public class GLES {
//頂点シェーダのコード
private final static String vertexShaderCode=
//光源
"uniform vec4 u_LightAmbient;"+ //光源の環境光色
"uniform vec4 u_LightDiffuse;"+ //光源の拡散光色
"uniform vec4 u_LightSpecular;"+//光源の鏡面光色
"uniform vec4 u_LightPos;"+ //光源の位置
"uniform int u_UseLight;"+ //光源の利用

//マテリアル
"uniform vec4 u_MaterialAmbient;"+ //マテリアルの環境光色
"uniform vec4 u_MaterialDiffuse;"+ //マテリアルの拡散光色
"uniform vec4 u_MaterialSpecular;"+ //マテリアルの鏡面光色
"uniform float u_MaterialShininess;"+//マテリアルの鏡面反射角度

//行列
"uniform mat4 u_MMatrix;"+ //モデルビュー行列
"uniform mat4 u_PMatrix;"+ //射影行列
"uniform mat4 u_NormalMatrix;"+//モデルビュー行列の逆転置行列

//頂点
"uniform vec4 u_Color;"+ //色
"attribute vec4 a_Position;"+//位置
"attribute vec3 a_Normal;"+ //法線
"attribute vec2 a_UV;"+ //UV

//テクスチャ
"uniform mat4 u_TexMatrix;"+//テクスチャ行列

//出力
"varying vec4 v_Color;"+//色
"varying vec2 v_UV;"+ //UV
"void main(){"+
"if (u_UseLight==1){"+
//環境光の計算
"vec4 ambient=u_LightAmbient*u_MaterialAmbient;"+

//拡散光の計算
"vec3 P=vec3(u_MMatrix*a_Position);"+
"vec3 L=normalize(vec3(u_LightPos)-P);"+
"vec3 N=normalize(mat3(u_NormalMatrix)*a_Normal);"+
"vec4 diffuseP=vec4(max(dot(L,N),0.0));"+
"vec4 diffuse=diffuseP*u_LightDiffuse*u_MaterialDiffuse;"+

//鏡面光の計算
"vec3 S=normalize(L+vec3(0.0,0.0,1.0));"+
"float specularP=pow(max(dot(N,S),0.0),u_MaterialShininess);"+
"vec4 specular=specularP*u_LightSpecular*u_MaterialSpecular;"+

//色の指定
"v_Color=ambient+diffuse+specular;"+
"}else{"+
//色の指定
"v_Color=u_Color;"+
"}"+

//位置の指定
"gl_Position=u_PMatrix*u_MMatrix*a_Position;"+

//UVの指定
"v_UV=vec2(u_TexMatrix*vec4(a_UV,0.0,1.0));"+
"}";

//フラグメントシェーダのコード
private final static String fragmentShaderCode=
"precision mediump float;"+

//テクスチャ
"uniform sampler2D u_Tex;"+//テクスチャ
"uniform int u_UseTex;"+ //テクスチャ利用

//入力
"varying vec2 v_UV;" + //UV
"varying vec4 v_Color;"+//色
"void main(){"+
"if (u_UseTex==1){"+
"gl_FragColor=texture2D(u_Tex,v_UV)*v_Color;"+
"}else {"+
"gl_FragColor=v_Color;"+
"}"+
"}";

//システム
public static Context context;//コンテキスト
private static int program;//プログラムオブジェクト
private static ArrayList<float[]> mMatrixs=new ArrayList<float[]>();

//光源のハンドル
public static int lightAmbientHandle; //光源の環境光色ハンドル
public static int lightDiffuseHandle; //光源の拡散光色ハンドル
public static int lightSpecularHandle;//光源の鏡面光ハンドル
public static int lightPosHandle; //光源の位置ハンドル
public static int useLightHandle; //光源の利用ハンドル

//マテリアルのハンドル
public static int materialAmbientHandle; //マテリアルの環境光色ハンドル
public static int materialDiffuseHandle; //マテリアルの拡散光色ハンドル
public static int materialSpecularHandle; //マテリアルの鏡面光ハンドル
public static int materialShininessHandle;//マテリアルの鏡面反射角度ハンドル

//行列のハンドル
public static int mMatrixHandle; //モデルビュー行列ハンドル
public static int pMatrixHandle; //射影行列ハンドル
public static int normalMatrixHandle;//モデルビュー行列の逆転置行列ハンドル

//頂点のハンドル
public static int colorHandle; //色ハンドル
public static int positionHandle;//位置ハンドル
public static int normalHandle; //法線ハンドル
public static int uvHandle; //UVハンドル

//テクスチャのハンドル
public static int texMatrixHandle;//テクスチャ行列ハンドル
public static int texHandle; //テクスチャハンドル
public static int useTexHandle; //テクスチャの利用ハンドル

//行列
public static float[] mMatrix =new float[16];//モデルビュー行列
public static float[] pMatrix =new float[16];//射影行列
public static float[] texMatrix=new float[16];//テクスチャ行列

//プログラムの生成
public static void makeProgram() {
//シェーダーオブジェクトの生成
int vertexShader =loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode);
int fragmentShader=loadShader(GLES20.GL_FRAGMENT_SHADER,fragmentShaderCode);

//プログラムオブジェクトの生成
program=GLES20.glCreateProgram();
GLES20.glAttachShader(program,vertexShader);
GLES20.glAttachShader(program,fragmentShader);
GLES20.glLinkProgram(program);

//光源のハンドルの取得
lightAmbientHandle=GLES20.glGetUniformLocation(program,"u_LightAmbient");
lightDiffuseHandle=GLES20.glGetUniformLocation(program,"u_LightDiffuse");
lightSpecularHandle=GLES20.glGetUniformLocation(program,"u_LightSpecular");
lightPosHandle=GLES20.glGetUniformLocation(program,"u_LightPos");
useLightHandle=GLES20.glGetUniformLocation(program,"u_UseLight");

//マテリアルのハンドルの取得
materialAmbientHandle=GLES20.glGetUniformLocation(program,"u_MaterialAmbient");
materialDiffuseHandle=GLES20.glGetUniformLocation(program,"u_MaterialDiffuse");
materialSpecularHandle=GLES20.glGetUniformLocation(program,"u_MaterialSpecular");
materialShininessHandle=GLES20.glGetUniformLocation(program,"u_MaterialShininess");

//行列のハンドルの取得
mMatrixHandle=GLES20.glGetUniformLocation(program,"u_MMatrix");
pMatrixHandle=GLES20.glGetUniformLocation(program,"u_PMatrix");
normalMatrixHandle=GLES20.glGetUniformLocation(program,"u_NormalMatrix");

//頂点のハンドルの取得
colorHandle=GLES20.glGetUniformLocation(program,"u_Color");
positionHandle=GLES20.glGetAttribLocation(program,"a_Position");
normalHandle=GLES20.glGetAttribLocation(program,"a_Normal");
uvHandle=GLES20.glGetAttribLocation(program,"a_UV");

//テクスチャのハンドルの取得
texMatrixHandle=GLES20.glGetUniformLocation(program,"u_TexMatrix");
texHandle=GLES20.glGetUniformLocation(program,"u_Tex");
useTexHandle=GLES20.glGetUniformLocation(program,"u_UseTex");

//プログラムオブジェクトの利用開始
GLES20.glUseProgram(program);

//行列の正規化
Matrix.setIdentityM(mMatrix,0);
Matrix.setIdentityM(pMatrix,0);
Matrix.setIdentityM(texMatrix,0);

//初期値
GLES20.glUniform4fv(lightAmbientHandle,1,new float[]{0,0,0,1},0);
GLES20.glUniform4fv(lightDiffuseHandle,1,new float[]{1,1,1,1},0);
GLES20.glUniform4fv(lightSpecularHandle,1,new float[]{1,1,1,1},0);
GLES20.glUniform4fv(lightPosHandle,1,new float[]{0,0,1,0},0);
GLES20.glUniform1i(useLightHandle,0);
GLES20.glUniform4fv(colorHandle,1,new float[]{1,1,1,1},0);
GLES20.glUniformMatrix4fv(GLES.texMatrixHandle,1,false,texMatrix,0);
GLES20.glUniform1f(useTexHandle,0);
}

//シェーダーオブジェクトの生成
private static int loadShader(int type,String shaderCode) {
int shader=GLES20.glCreateShader(type);
GLES20.glShaderSource(shader,shaderCode);
GLES20.glCompileShader(shader);
return shader;
}

//モデルビュー射影行列の更新
public static void updateMatrix() {
//射影行列をシェーダに指定
GLES20.glUniformMatrix4fv(pMatrixHandle,1,false,pMatrix,0);

//モデルビュー行列をシェーダに指定
GLES20.glUniformMatrix4fv(mMatrixHandle,1,false,mMatrix,0);

//モデルビュー行列の逆転置行列の指定
float[] normalM=new float[16];
normalM(normalM,mMatrix);
GLES20.glUniformMatrix4fv(normalMatrixHandle,1,false,normalM,0);
}

//行列の逆転置行列の計算
public static void normalM(float[] rm,float[] m) {
float[] invertM=new float[16];
Matrix.invertM(invertM,0,m,0);
Matrix.transposeM(rm,0,invertM,0);
}

//透視変換の指定
public static void gluPerspective(float[] m,
float angle,float aspect,float near,float far) {
float top=near*(float)Math.tan(angle*(Math.PI/360.0));
float bottom=-top;
float left=bottom*aspect;
float right=top*aspect;
float[] frustumM=new float[16];
float[] resultM=new float[16];
Matrix.frustumM(frustumM,0,left,right,bottom,top,near,far);
Matrix.multiplyMM(resultM,0,m,0,frustumM,0);
System.arraycopy(resultM,0,m,0,16);
}

//ビュー変換の指定
public static void gluLookAt(float[] m,
float eyeX,float eyeY,float eyeZ,
float focusX,float focusY,float focusZ,
float upX,float upY,float upZ) {
float[] lookAtM=new float[16];
float[] resultM=new float[16];
Matrix.setLookAtM(lookAtM,0,
eyeX,eyeY,eyeZ,focusX,focusY,focusZ,upX,upY,upZ);
Matrix.multiplyMM(resultM,0,m,0,lookAtM,0);
System.arraycopy(resultM,0,m,0,16);
}

//行列のプッシュ
public static void glPushMatrix() {
float[] m=new float[16];
System.arraycopy(mMatrix,0,m,0,16);
mMatrixs.add(m);
}

//行列のポップ
public static void glPopMatrix() {
if (mMatrixs.size()==0) return;
float[] m=mMatrixs.remove(mMatrixs.size()-1);
System.arraycopy(m,0,mMatrix,0,16);
}
}


Figure.java
package net.npaka.gles;
import java.util.ArrayList;
import java.util.HashMap;

//フィギュア
public class Figure {
public HashMap<String,Material> materials;//マテリアル群
public ArrayList<Mesh> meshs; //メッシュ群

//描画
public void draw() {
for (Mesh mesh:meshs) mesh.draw();
}
}

Mesh.java
package net.npaka.gles;

//メッシュ
public class Mesh {
public VertexBuffer vertexBuffer;//頂点バッファ
public IndexBuffer indexBuffer; //インデックスバッファ
public Material material; //マテリアル

//描画
public void draw() {
material.bind();
vertexBuffer.bind();
indexBuffer.draw();
vertexBuffer.unbind();
material.unbind();
}
}

GLObject.java
package net.npaka.gles;

//GLオブジェクト(VertexBuffer/IndexBuffer/Material/Textureの親)
public abstract class GLObject {

//破棄時に呼ばれる
@Override
protected void finalize() throws Throwable {
super.finalize();
dispose();
}

//バインド
public abstract void bind();

//アンバインド
public abstract void unbind();

//解放
public abstract void dispose();
}

VertexBuffer.java
package net.npaka.gles;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.util.ArrayList;
import android.opengl.GLES20;

//頂点バッファ
public class VertexBuffer extends GLObject {
private int vertexBufferId;//頂点バッファID

//コンストラクタ
public VertexBuffer(ArrayList<float[]> vertexs) {
float[] vertexArray=new float[8*vertexs.size()];
for (int i=0;i<vertexs.size();i++) {
float[] vertex=vertexs.get(i);
System.arraycopy(vertex,0,vertexArray,8*i,8);
}
int count=vertexArray.length/8;
int[] bufferIds=new int[1];
GLES20.glGenBuffers(1,bufferIds,0);
vertexBufferId=bufferIds[0];
FloatBuffer fb=ByteBuffer.allocateDirect(8*4*count).
order(ByteOrder.nativeOrder()).asFloatBuffer();
fb.put(vertexArray);
fb.position(0);
GLES20.glDisable(GLES20.GL_CULL_FACE);
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER,vertexBufferId);
GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER,fb.capacity()*4,
fb,GLES20.GL_STATIC_DRAW);
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER,0);
}

//バインド
@Override
public void bind() {
GLES20.glEnableVertexAttribArray(GLES.positionHandle);
GLES20.glEnableVertexAttribArray(GLES.normalHandle);
GLES20.glEnableVertexAttribArray(GLES.uvHandle);
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER,vertexBufferId);
GLES20.glVertexAttribPointer(GLES.positionHandle,3,
GLES20.GL_FLOAT,false,8*4,0);
GLES20.glVertexAttribPointer(GLES.uvHandle,2,
GLES20.GL_FLOAT,false,8*4,3*4);
GLES20.glVertexAttribPointer(GLES.normalHandle,3,
GLES20.GL_FLOAT,false,8*4,5*4);
}

//アンバインド
@Override
public void unbind() {
GLES20.glDisableVertexAttribArray(GLES.positionHandle);
GLES20.glDisableVertexAttribArray(GLES.normalHandle);
GLES20.glDisableVertexAttribArray(GLES.uvHandle);
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER,0);
}

//破棄
@Override
public void dispose() {
if (vertexBufferId!=0) {
GLES20.glDeleteBuffers(1,new int[]{vertexBufferId},0);
vertexBufferId=0;
}
}
}

IndexBuffer.java
package net.npaka.gles;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.ShortBuffer;
import java.util.ArrayList;
import android.opengl.GLES20;

//インデックスバッファ
public class IndexBuffer extends GLObject {
private int indexBufferId; //インデックスバッファID
private int indexBufferSize;//インデックスバッファサイズ

//コンストラクタ
public IndexBuffer(ArrayList<short[]> indexs) {
short[] indexArray=new short[3*indexs.size()];
for (int i=0;i<indexs.size();i++) {
short[] index=indexs.get(i);
System.arraycopy(index,0,indexArray,3*i,3);
}
int[] bufferIds=new int[1];
GLES20.glGenBuffers(1,bufferIds,0);
indexBufferId=bufferIds[0];
bind();
ShortBuffer sb=ByteBuffer.allocateDirect(indexArray.length*2).
order(ByteOrder.nativeOrder()).asShortBuffer();
sb.put(indexArray);
sb.position(0);
indexBufferSize=indexArray.length;
GLES20.glBufferData(GLES20.GL_ELEMENT_ARRAY_BUFFER,
sb.capacity()*2,sb,GLES20.GL_STATIC_DRAW);
unbind();
}

//描画
public void draw() {
GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER,indexBufferId);
GLES20.glDrawElements(GLES20.GL_TRIANGLES,indexBufferSize,GLES20.GL_UNSIGNED_SHORT,0);
GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, 0);
}

//バインド
@Override
public void bind() {
GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER,indexBufferId);
}

//アンバインド
@Override
public void unbind() {
GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER,0);
}

//解放
@Override
public void dispose() {
if (indexBufferId!=0) {
GLES20.glDeleteBuffers(1,new int[]{indexBufferId},0);
indexBufferId=0;
}
}
}

Material.java
package net.npaka.gles;
import android.opengl.GLES20;

//マテリアル
public class Material extends GLObject {
public Texture texture; //テクスチャ
public float[] ambient =new float[4];//環境光
public float[] diffuse =new float[4];//拡散光
public float[] specular=new float[4];//鏡面光
public float shininess; //鏡面反射角度

//バインド
@Override
public void bind() {
GLES20.glUniform4fv(GLES.materialAmbientHandle,1,ambient,0);
GLES20.glUniform4fv(GLES.materialDiffuseHandle,1,diffuse,0);
GLES20.glUniform4fv(GLES.materialSpecularHandle,1,specular,0);
GLES20.glUniform1f(GLES.materialShininessHandle,shininess);
if (texture!=null) texture.bind();
}

//アンバインド
@Override
public void unbind() {
if (texture!=null) texture.unbind();
}

//破棄
@Override
public void dispose() {
if (texture!=null) {
texture.dispose();
texture=null;
}
}
}

Texture.java
package net.npaka.gles;
import java.io.IOException;
import java.io.InputStream;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.opengl.GLES20;
import android.opengl.GLUtils;

//テクスチャ
public class Texture extends GLObject {
public int textureId;//テクスチャID
public int width; //幅
public int height; //高さ

//バインド
@Override
public void bind() {
if (textureId!=0) {
GLES20.glUniform1i(GLES.useTexHandle,1);
GLES20.glEnable(GLES20.GL_TEXTURE_2D);
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D,textureId);
GLES20.glUniform1i(GLES.texHandle,0);
}
}

//アンバインド
@Override
public void unbind() {
GLES20.glDisable(GLES20.GL_TEXTURE_2D);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D,0);
GLES20.glUniform1i(GLES.useTexHandle,0);
}

//解放
@Override
public void dispose() {
if (textureId!=0) {
GLES20.glDeleteTextures(0,new int[]{textureId},0);
textureId=0;
}
}

//テクスチャの生成
public static Texture createInstance(Bitmap bmp) {
Texture result=new Texture();
int[] bufferIds=new int[1];
GLES20.glGenTextures(1,bufferIds,0);
result.textureId=bufferIds[0];
result.width=bmp.getWidth();
result.height=bmp.getHeight();
GLES20.glEnable(GLES20.GL_TEXTURE_2D);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D,result.textureId);
GLUtils.texImage2D(GLES20.GL_TEXTURE_2D,0,bmp,0);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,GLES20.GL_TEXTURE_MIN_FILTER,GLES20.GL_NEAREST);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,GLES20.GL_TEXTURE_MAG_FILTER,GLES20.GL_NEAREST);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D,0);
return result;
}

//テクスチャの生成
public static Texture createTextureFromAsset(
String assetFileName) throws IOException {
InputStream in=GLES.context.getAssets().open(assetFileName);
Bitmap bmp=BitmapFactory.decodeStream(in);
Texture result=Texture.createInstance(bmp);
bmp.recycle();
in.close();
return result;
}
}

2D描画用ライブラリ

2D描画のクラス郡を用意。
クラス名 説明
Graphics 2D描画のクラス。

Graphics.java
package net.npaka.gles;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import android.opengl.GLES20;
import android.opengl.Matrix;

//2Dグラフィックス
public class Graphics {
public int screenW; //画面幅
public int screenH; //画面高さ
private FloatBuffer vertexBuffer;//頂点バッファ
private FloatBuffer uvBuffer; //UVバッファ

//コンストラクタ
public Graphics(int screenW,int screenH) {
this.screenW=screenW;
this.screenH=screenH;

//頂点バッファの生成
float[] vertexs={
-1.0f, 1.0f,0.0f,//頂点0
-1.0f,-1.0f,0.0f,//頂点1
1.0f, 1.0f,0.0f,//頂点2
1.0f,-1.0f,0.0f,//頂点3
};
vertexBuffer=makeFloatBuffer(vertexs);

//UVバッファの生成
float[] uvs={
0.0f,0.0f,//左上
0.0f,1.0f,//左下
1.0f,0.0f,//右上
1.0f,1.0f,//右下
};
uvBuffer=makeFloatBuffer(uvs);
}

//float配列→floatバッファ
private FloatBuffer makeFloatBuffer(float[] array) {
FloatBuffer fb=ByteBuffer.allocateDirect(array.length*4).order(
ByteOrder.nativeOrder()).asFloatBuffer();
fb.put(array).position(0);
return fb;
}

//2D描画の設定
public void setup2D() {
//頂点配列の有効化
GLES20.glEnableVertexAttribArray(GLES.positionHandle);
GLES20.glEnableVertexAttribArray(GLES.uvHandle);

//デプステストと光源の無効化
GLES20.glDisable(GLES20.GL_DEPTH_TEST);
GLES20.glUniform1i(GLES.useLightHandle,0);

//ブレンドの指定
GLES20.glEnable(GLES20.GL_BLEND);
GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA,GLES20.GL_ONE_MINUS_SRC_ALPHA);

//射影変換
Matrix.setIdentityM(GLES.pMatrix,0);
GLES20.glUniform4fv(GLES.colorHandle,1,new float[]{1,1,1,1},0);
GLES20.glVertexAttribPointer(GLES.uvHandle,2,
GLES20.GL_FLOAT,false,0,uvBuffer);
}

//イメージの描画
public void drawImage(Texture texture,int x,int y) {
drawImage(texture,x,y,texture.width,texture.height);
}

//イメージの描画
public void drawImage(Texture texture,int x,int y,int w,int h) {
drawImage(texture,x,y,w,h,0,0,texture.width,texture.height);
}

//イメージの描画
public void drawImage(Texture texture,int dx,int dy,int dw,int dh,
int sx,int sy,int sw,int sh) {
//ウィンドウ座標を正規化デバイス座標に変換
float tw=(float)sw/(float)texture.width;
float th=(float)sh/(float)texture.height;
float tx=(float)sx/(float)texture.width;
float ty=(float)sy/(float)texture.height;

//テクスチャのバインド
texture.bind();

//テクスチャ行列の移動・拡縮
Matrix.setIdentityM(GLES.texMatrix,0);
Matrix.translateM(GLES.texMatrix,0,tx,ty,0.0f);
Matrix.scaleM(GLES.texMatrix,0,tw,th,1.0f);
GLES20.glUniformMatrix4fv(GLES.texMatrixHandle,1,
false,GLES.texMatrix,0);

//ウィンドウ座標を正規化デバイス座標に変換
float mx=((float)dx/(float)screenW)*2.0f-1.0f;
float my=((float)dy/(float)screenH)*2.0f-1.0f;
float mw=((float)dw/(float)screenW);
float mh=((float)dh/(float)screenH);

//モデルビュー行列の移動・拡縮
Matrix.setIdentityM(GLES.mMatrix,0);
Matrix.translateM(GLES.mMatrix,0,mx+mw,-(my+mh),0.0f);
Matrix.scaleM(GLES.mMatrix,0,mw,mh,1.0f);
GLES20.glUniformMatrix4fv(GLES.mMatrixHandle,1,
false,GLES.mMatrix,0);
GLES.updateMatrix();

//四角形の描画
GLES20.glVertexAttribPointer(GLES.positionHandle,3,
GLES20.GL_FLOAT,false,0,vertexBuffer);
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP,0,4);

//テクスチャのアンバインド
texture.unbind();
}
}

−戻る−