这个小程序需要的三个txt文本文件在被粘在示例图后面, 此变形效果程序分为以下四个类
(1)Morphing 除主功能外在本程序中还有按键处理及返回InputStream
(2)MyRenderer 负责绘制
(3)OBJECT 三维物体类
(4)VERTEX 三维顶点类
从最简单的VERTEX 类开始,可见此类主要映射三维各顶点
package sim.feel;
/**
* 三维顶点类
* @author Sim
*/
public class VERTEX {
float x, y, z;
public VERTEX(float x, float y, float z) {
this.x = x;
this.y = y;
this.z = z;
}
}
接下来是OBJECT类此声明了顶点数及将x,y,z保存到一个List中
package sim.feel;
import java.util.ArrayList;
import java.util.List;
/**
* 三维物体类
*
* @author Sim
*/
public class OBJECT {
// 顶点数
int verts;
List<VERTEX> points = new ArrayList<VERTEX>();
}
接下来是Morphing类,此类则是按键处理与返回InputStream
package sim.feel;
import java.io.IOException;
import java.io.InputStream;
import android.app.Activity;
import android.content.res.AssetManager;
import android.content.res.Resources;
import android.opengl.GLSurfaceView;
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.Window;
import android.view.WindowManager;
public class Morphing extends Activity {
private MyRenderer myRenderer = new MyRenderer();
private GLSurfaceView glSurfaceView;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 无标题栏
requestWindowFeature(Window.FEATURE_NO_TITLE);
// 全屏
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
new LoadFile(getResources());
glSurfaceView = new GLSurfaceView(this);
glSurfaceView.setRenderer(myRenderer);
setContentView(glSurfaceView);
}
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
return myRenderer.onKeyUp(keyCode, event);
}
}
// 返回File的InputStream
class LoadFile {
public static Resources res;
public LoadFile(Resources resources) {
LoadFile.res = resources;
}
public static InputStream getFile(String name) {
AssetManager am = LoadFile.res.getAssets();
try {
return am.open(name);
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
}
最后就是我们的MyRenderer类,它是最复杂的一个类,主要包含了加载txt文本文件,按键,及三个要实现的方法
package sim.feel;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.util.Random;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
import android.opengl.GLSurfaceView.Renderer;
import android.view.KeyEvent;
public class MyRenderer implements Renderer {
// X,Y,Z轴的旋转角度
private float xrot, yrot, zrot;
// X,Y,Z 轴的旋转速度
private float xspeed, yspeed, zspeed;
// 物体的位置
private float cx, cy, cz = -15;
// 随机
private Random random = new Random();
// 变换的步数
private int step = 0, steps = 200;
// 是否使用变形
private boolean morph = false;
// verticesBuffer
FloatBuffer verticesBuffer;
// colorsBuffer
FloatBuffer colorsBuffer;
// vertices
private float[] vertices = new float[486];
// colors
private float[] colors = new float[486];
// 我们的四个物体,帮助物体,原物体,目标物体
OBJECT morph1 = new OBJECT();
OBJECT morph2 = new OBJECT();
OBJECT morph3 = new OBJECT();
OBJECT morph4 = new OBJECT();
OBJECT helper = new OBJECT();
OBJECT sour = new OBJECT();
OBJECT dest = new OBJECT();
// LoadBuffer
public void LoadBuffer() {
ByteBuffer verticesByteBuffer = ByteBuffer
.allocateDirect(vertices.length * 9 * 4);
verticesByteBuffer.order(ByteOrder.nativeOrder());
verticesBuffer = verticesByteBuffer.asFloatBuffer();
verticesBuffer.put(vertices);
verticesBuffer.position(0);
ByteBuffer colorsByteBuffer = ByteBuffer
.allocateDirect(colors.length * 4 * 12);
colorsByteBuffer.order(ByteOrder.nativeOrder());
colorsBuffer = colorsByteBuffer.asFloatBuffer();
colorsBuffer.put(colors);
colorsBuffer.position(0);
}
@Override
public void onDrawFrame(GL10 gl) {
LoadBuffer();
// 清除屏幕和深度缓存
gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
// 重置模型变换矩阵
gl.glLoadIdentity();
gl.glTranslatef(cx, cy, cz);
// 旋转
gl.glRotatef(xrot, 1, 0, 0);
gl.glRotatef(yrot, 0, 1, 0);
gl.glRotatef(zrot, 0, 0, 1);
// 根据旋转速度,增加旋转角度
xrot += xspeed;
yrot += yspeed;
zrot += zspeed;
// 顶点临时变量
float tx, ty, tz;
// 保存中间计算的临时顶点
VERTEX q = new VERTEX(0, 0, 0);
// 启用顶点及颜色
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
gl.glEnableClientState(GL10.GL_COLOR_ARRAY);
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, verticesBuffer);
gl.glColorPointer(2, GL10.GL_FLOAT, 0, colorsBuffer);
// 绘制模型中的点
colorsBuffer.clear();
verticesBuffer.clear();
// 循环绘制模型1中的每一个顶点
for (int i = 0; i < morph1.verts; i++) {
// 如果启用变形,则计算中间模型
if (morph) {
q = calculate(i);
} else {
q.x = q.y = q.z = 0;
}
helper.points.get(i).x -= q.x;
helper.points.get(i).y -= q.y;
helper.points.get(i).z -= q.z;
// 保存计算结果到tx,ty,tz变量中
tx = helper.points.get(i).x;
ty = helper.points.get(i).y;
tz = helper.points.get(i).z;
colorsBuffer.put(0.0f);
colorsBuffer.put(1.0f);
colorsBuffer.put(1.0f);
colorsBuffer.put(1.0f);
verticesBuffer.put(tx);
verticesBuffer.put(ty);
verticesBuffer.put(tz);
colorsBuffer.put(0.0f);
colorsBuffer.put(0.5f);
colorsBuffer.put(1.0f);
colorsBuffer.put(1.0f);
// 如果启用变形,则绘制2步后的顶点
tx -= 2 * q.x;
ty -= 2 * q.y;
ty -= 2 * q.y;
verticesBuffer.put(tx);
verticesBuffer.put(ty);
verticesBuffer.put(tz);
colorsBuffer.put(0.0f);
colorsBuffer.put(0.0f);
colorsBuffer.put(1.0f);
colorsBuffer.put(1.0f);
tx -= 2 * q.x;
ty -= 2 * q.y;
ty -= 2 * q.y;
verticesBuffer.put(tx);
verticesBuffer.put(ty);
verticesBuffer.put(tz);
}
gl.glDrawArrays(GL10.GL_POINTS, 0, morph1.verts * 3);
// 禁用顶点及颜色
gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
gl.glDisableClientState(GL10.GL_COLOR_ARRAY);
if (morph && step <= steps) {
step++;
} else {
morph = false;
sour = dest;
step = 0;
}
gl.glFinish();
}
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
float ratio;
if (height == 0) {
height = 1;
}
ratio = (float) width / (float) height;
gl.glViewport(0, 0, width, height);
gl.glMatrixMode(GL10.GL_PROJECTION);
gl.glLoadIdentity();
gl.glFrustumf(-ratio, ratio, -1, 1, 2.0f, 100.0f);
gl.glMatrixMode(GL10.GL_MODELVIEW);
gl.glLoadIdentity();
}
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
// 设置半透明模式
gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE);
// 设置清除色为黑色
gl.glClearColor(0, 0, 0, 0);
// 设置深度缓存中值为1
gl.glClearDepthf(1.0f);
// 设置深度测试函数
gl.glDepthFunc(GL10.GL_LESS);
// 启用深度测试
gl.glEnable(GL10.GL_DEPTH_TEST);
// 设置着色模式为光滑着色
gl.glShadeModel(GL10.GL_SMOOTH);
// 精细修正
gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_NICEST);
objload("sphere.txt", morph1);
objload("torus.txt", morph2);
objload("tube.txt", morph3);
for (int i = 0; i < 486; i++) {
float xx = ((float) (rand() % 14000) / 1000) - 7;
float yy = ((float) (rand() % 14000) / 1000) - 7;
float zz = ((float) (rand() % 14000) / 1000) - 7;
morph4.points.add(new VERTEX(xx, yy, zz));
}
objload("sphere.txt", helper);
sour = dest = morph1;
}
// 随机数
public int rand() {
return Math.abs(random.nextInt());
}
// 计算第i个顶点每次变换的位移
public VERTEX calculate(int i) {
VERTEX a = new VERTEX(0, 0, 0);
a.x = (sour.points.get(i).x - dest.points.get(i).x) / steps;
a.y = (sour.points.get(i).y - dest.points.get(i).y) / steps;
a.z = (sour.points.get(i).z - dest.points.get(i).z) / steps;
return a;
}
// 读取文件顶点数
public String readstr(BufferedReader br) {
String str = "";
// 因为第一行为注释,所以此方法读取的是第二行
do {
try {
str = br.readLine();
} catch (IOException e) {
e.printStackTrace();
}
} while ((str.charAt(0) == '/') || (str.charAt(0) == '/n'));
return str;
}
// 将所有顶点装载到List就是points中
public void objload(String name, OBJECT k) {
int ver = 0;
String oneline;
int i;
BufferedRead