2.3棋类作品:3d魔方
2.3.1 Android为OpenGL介绍
Android为OpenGL ES支持提供了GLSurfaceView组件,这个组件用于显示3D图形。GLSurfaceView本身并不提供绘制3D图形的功能,而是由GLSurfaceView.Renderer来完成了SurfaceView中3D图形的绘制。
归纳起来,在Android中使用OpenGL ES需要三个步骤:
1、创建GLSurfaceView组件,使用Activity来显示GLSurfaceView组件。
2、为GLSurfaceView组件创建GLSurfaceView.Renderer实例,实现GLSurfaceView.Renderer类时需要实现该接口里的三个方法:
abstract void onDrawFrame(GL10 gl):Renderer对象调用该方法绘制GLSurfaceView的当前帧。
abstract void onSurfaceChanged(GL10 gl , int width ,int height):当GLSurfaceView的大小改变时回调该方法。
abstract void onSurfaceCreated(GL10 gl , EGLConfig config):当GLSurfaceView被创建时回调该方法。
3、调用GLSurfaceView组件的setRenderer()方法指定Renderer对象,该Renderer对象将会完成GLSurfaceView里3D图像的绘制。
从上面的介绍不难看出,实际上绘制3D图形的难点不是如何使用GLSurfaceView组件,而是如何实现Renderer类。实现Renderer类时需要实现三个方法,这三个方法都有一个GL10形参,它就代表了GLOpen ES的“绘制画笔”,我们可以把它想象成Swing 2D绘图中的Graphics,也可以想象成Android 2D绘图中的Canvas组件-----当我们希望Renderer绘制3D图形时,实际上是调用GL10的方法来进行绘制的。
publicvoid onSurfaceChanged(GL10 gl, int width, int height) {
// TODOAuto-generated method stub
//设置OpenGL的场景大小
gl.glViewport(0, 0,width, height);
gl.glMatrixMode(GL10.GL_PROJECTION); // 设置投影矩阵
gl.glLoadIdentity(); //设置矩阵为单位矩阵,相当于重置矩阵
GLU.gluPerspective(gl, 45.0f, ((float) width) / height, 0.1f, 10f);//设置透视范围
}
//当窗口被创建是调用
public void onSurfaceCreated(GL10gl, EGLConfig config) {
// TODOAuto-generated method stub
gl.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);//设置清屏时背景的颜色,R,G,B,A
gl.glEnable(GL10.GL_DEPTH_TEST); //启用深度缓存
gl.glEnable(GL10.GL_CULL_FACE); //启用背面剪裁
gl.glClearDepthf(1.0f); // 设置深度缓存值
gl.glDepthFunc(GL10.GL_LEQUAL); // 设置深度缓存比较函数,GL_LEQUAL表示新的像素的深度缓存值小于等于当前像素的深度缓存值(通过gl.glClearDepthf(1.0f)设置)时通过深度测试
gl.glShadeModel(GL10.GL_SMOOTH);// 设置阴影模式GL_SMOOTH
}
2.3.2绘制一个3d正方体和一个参照体
class MyRendererimplements Renderer{
float box[] = new float[] {
// FRONT
-0.2f, -0.2f, 0.2f,
0.2f, -0.2f, 0.2f,
-0.2f, 0.2f, 0.2f,
0.2f, 0.2f, 0.2f,
// BACK
-0.2f, -0.2f, -0.2f,
-0.2f, 0.2f, -0.2f,
0.2f, -0.2f, -0.2f,
0.2f, 0.2f, -0.2f,
// LEFT
-0.2f, -0.2f, 0.2f,
-0.2f, 0.2f, 0.2f,
-0.2f, -0.2f, -0.2f,
-0.2f, 0.2f, -0.2f,
// RIGHT
0.2f, -0.2f, -0.2f,
0.2f, 0.2f, -0.2f,
0.2f, -0.2f, 0.2f,
0.2f, 0.2f, 0.2f,
// TOP
-0.2f, 0.2f, 0.2f,
0.2f, 0.2f, 0.2f,
-0.2f, 0.2f, -0.2f,
0.2f, 0.2f, -0.2f,
// BOTTOM
-0.2f, -0.2f, 0.2f,
-0.2f, -0.2f, -0.2f,
0.2f, -0.2f, 0.2f,
0.2f, -0.2f, -0.2f,
};
FloatBuffercubeBuff, cubeBuff1;
float xrot = 0.0f;
float yrot = 0.0f;
/**
* 将float数组转换存储在字节缓冲数组
* @param arr
* @return
*/
public FloatBuffermakeFloatBuffer(float[] arr) {
ByteBuffer bb = ByteBuffer.allocateDirect(arr.length * 4);//分配缓冲空间,一个float占4个字节
bb.order(ByteOrder.nativeOrder());//设置字节顺序, 其中ByteOrder.nativeOrder()是获取本机字节顺序
FloatBuffer fb = bb.asFloatBuffer(); //转换为float型
fb.put(arr); //添加数据
fb.position(0); //设置数组的起始位置
returnfb;
}
public MyRenderer(){
cubeBuff =makeFloatBuffer(box);//转换float数组
cubeBuff1 =makeFloatBuffer(box);
}
//所有的绘图操作都在此方法中执行
public void onDrawFrame(GL10 gl) {
// TODOAuto-generated method stub
gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);// 清除屏幕和深度缓存
gl.glMatrixMode(GL10.GL_MODELVIEW); //切换至模型观察矩阵
gl.glLoadIdentity();// 重置当前的模型观察矩阵
GLU.gluLookAt(gl, 0, 0, 3, 0, 0, 0, 0, 1, 0);//设置视点和模型中心位置
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, cubeBuff);//设置顶点数据
// gl.glLoadIdentity();// 重置当前的模型观察矩阵
// GLU.gluLookAt(gl, 0, 0,3, 0, 0, 0, 0, 1, 0);//设置视点和模型中心位置
gl.glRotatef(rotate, 1, 0, 0); //绕着(0,0,0)与(1,0,0)即x轴旋转
gl.glRotatef(rotate, 0, 1, 0);
gl.glTranslatef(-0.6f, -0.2f, 0f);
gl.glVertexPointer(3,GL10.GL_FLOAT, 0, cubeBuff1);//设置顶点数据
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
//六个面一个面绘制一种颜色
gl.glColor4f(1.0f, 0, 0, 1.0f); //设置颜色,红色
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 4); //绘制正方型FRONT面
gl.glColor4f(1.0f, 1.0f, 0, 1.0f);
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 4, 4);
gl.glColor4f(0, 1.0f, 0, 1.0f);
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 8, 4);
gl.glColor4f(0, 1.0f, 1.0f, 1.0f);
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 12, 4);
gl.glColor4f(0, 0, 1.0f, 1.0f);
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 16, 4);
gl.glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 20, 4);
// gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 20,40);
xrot+= 1.0f;
yrot+= 0.5f;
gl.glMatrixMode(GL10.GL_MODELVIEW); //切换至模型观察矩阵
gl.glLoadIdentity();// 重置当前的模型观察矩阵
GLU.gluLookAt(gl, 0, 0, 3, 0, 0, 0, 0, 1, 0);//设置视点和模型中心位置
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, cubeBuff);//设置顶点数据
// gl.glLoadIdentity();// 重置当前的模型观察矩阵
// GLU.gluLookAt(gl, 0, 0,3, 0, 0, 0, 0, 1, 0);//设置视点和模型中心位置
// gl.glTranslatef(-0.6f, -0.2f,0f);
gl.glVertexPointer(3,GL10.GL_FLOAT, 0, cubeBuff1);//设置顶点数据
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
//gl.glRotatef(rotate, 1, 0, 0); //绕着(0,0,0)与(1,0,0)即x轴旋转
// gl.glRotatef(rotate, 0, 1, 0);
//六个面一个面绘制一种颜色
gl.glColor4f(1.0f, 0, 0, 1.0f); //设置颜色,红色
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 4); //绘制正方型FRONT面
gl.glColor4f(1.0f, 1.0f, 0, 1.0f);
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 4, 4);
gl.glColor4f(0, 1.0f, 0, 1.0f);
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 8, 4);
gl.glColor4f(0, 1.0f, 1.0f, 1.0f);
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 12, 4);
gl.glColor4f(0,0, 1.0f, 1.0f);
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 16, 4);
gl.glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 20, 4);
}
2.3.3 触屏改变旋转参数
public boolean onTouchEvent(final MotionEvent event) {
// rotate+=1;
setTitle("成功啦!!");
if (event.getAction() == MotionEvent.ACTION_MOVE) {
final float xdiff = (xAngle - event.getX());
final float ydiff = (yAngle - event.getY());
{
xAngle = xAngle+ ydiff;
yAngle = yAngle + xdiff;
}
rotate+=1;
xAngle = event.getX();
yAngle = event.getY();
}
return true;
}
2.3.4绘制27个3d正方体组成一个三阶魔方
调用Draw1(gl, i - 1, j - 1, k - 1,
rx[3 * k + 9 * j + i + 1], ry[3 * k + 9 * j + i
+ 1], rz[3 * k + 9 * j + i + 1]);
27次,每次在onDrawFrame(GL10 gl)中刷新位置与旋转角度。
public void Draw1(GL10 gl, int x, int y, int z, int rx, int ry, int rz) {
gl.glLoadIdentity();// 重置当前的模型观察矩阵
GLU.gluLookAt(gl, 0, 0, 3, 0, 0,0, 0, 1, 0);// 设置视点和模型中心位置
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, cubeBuff);// 设置顶点数据
gl.glRotatef(30, 1, 0, 0); // 绕着(0,0,0)与(1,0,0)即x轴旋转
gl.glRotatef(-30, 0, 1, 0); // 绕着(0,0,0)与(1,0,0)即x轴旋转
for (int k = e; k >= 1; k--) {
if (c[3 * z + 13 + 9 * y + x + 1][k] == 1) {
gl.glRotatef(90, 1, 0, 0);
}
if (c[3 * z + 13 + 9 * y + x + 1][k] == 2) {
gl.glRotatef(90, 0, 1, 0);
}
if (c[3 * z + 13 + 9 * y + x + 1][k] == 3) {
gl.glRotatef(90, 0, 0, 1);
}
}
// gl.glRotatef(rotate, 1 , 0, 0);
// gl.glRotatef(rotate,0 , 1, 0);
// gl.glRotatef(rotate, ox[-1][1][1][0],ox[-1][1][1][1],
// ox[-1][1][1][2]);
float zoom = .6f;
gl.glTranslatef(zoom * .41f * x, zoom *.41f * y, zoom * .41f * z);
gl.glScalef(zoom, zoom, zoom);
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
// 六个面一个面绘制一种颜色
gl.glColor4f(1.0f, 0, 0, 1.0f); // 设置颜色,红色
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 4); // 绘制正方型FRONT面
gl.glColor4f(1.0f, 1.0f, 0, 1.0f);
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 4, 4);
gl.glColor4f(0, 1.0f, 0, 1.0f);
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 8, 4);
gl.glColor4f(0, 1.0f, 1.0f, 1.0f);
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 12, 4);
gl.glColor4f(0, 0, 1.0f, 1.0f);
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 16, 4);
gl.glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 20, 4);
}
public void onDrawFrame(GL10 gl) {
// TODO Auto-generated method stub
gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);// 清除屏幕和深度缓存
gl.glScalef(.8f, .8f, .8f);
gl.glMatrixMode(GL10.GL_MODELVIEW); // 切换至模型观察矩阵
for (int k = 0; k < 3; k++) {
for (int j = 0; j < 3; j++) {
for (int i = 0; i < 3; i++) {
Draw1(gl, i - 1, j - 1, k -1,
rx[3 * k + 9 * j + i + 1], ry[3 * k + 9 * j + i
+ 1], rz[3 * k + 9 * j + i + 1]);
// Draw1(gl,zb[k-1][j-1][i-1][0],zb[k][j][i][1],zb[k][j][i][2]);
}
}
}
// Draw1(gl, -1, -2, 1, 0, 0, 0);
}
2.3.5绘制3d三阶魔方XYZ轴转动算法
void rxo(int k) {
for (int i = 0; i < 8; i++) {
if (k <4) {
x0[i] = x00[i]+k-1;
}
else if (k <7) {
x0[i] = y0[i]+(k-4)*9;
}
else if (k <10) {
x0[i] = z0[i] + (k-7)*3;
}
}
int b[] = { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1 };
for (int i = 0; i < 8; i++) {
for (int p = 1; p < 28; p++) {
if ((a0[p] == x0[i]) && (b[p] == 1)) {
a0[p] = x0[(i + 6) % 8];
b[p] = 0;
}
}
}
}
public boolean onTouchEvent(final MotionEvent event) {
// rotate+=1; rotate1+=1;final
float xdiff = (xAngle - fx[0]);
final float ydiff = (yAngle - fy[0]);
if (event.getX() > 1) {
fx[0] = (int)(event.getX());
fy[0] = (int)(event.getY());
}
if (event.getAction() == MotionEvent.ACTION_DOWN) { // 按下
fx[1] = (int)(event.getX());
fy[1] = (int)(event.getY());
}
if (event.getAction() == MotionEvent.ACTION_UP) {
// rotate+=45; rotate1+=45;
if (fy[1] > 610) {
if (Math.abs(xdiff) * 1.2 < Math.abs(ydiff)){
if (fx[1] < 180) {
fx[2] = 1;
} else if (fx[1] < 290) {
fx[2] = 2;
} else if (fx[1] < 500) {
fx[2] = 3;
}
} else if (Math.abs(xdiff) * 1.2 >Math.abs(ydiff)){
if (fy[1] - (fx[1] - 190) / 2.5 < 670) {
fx[2] = 6;
} else if (fy[1] - (fx[1] - 190) / 2.5 < 770) {
fx[2] = 5;
} else if (fy[1] - (fx[1] - 190) / 2.5 < 880) {
fx[2] = 4;
}
}
}else {
if (fy[1] - (fx[1] - 190) / 2.5 < 460) {
fx[2] = 7;
} else if (fy[1] - (fx[1] - 190) / 2.5 < 500) {
fx[2] = 8;
} else {
fx[2] = 9;
}
}
if (fx[2] < 4) {
e++;
for (int k = 1; k < 28; k++) {
if (x[a0[k]] != fx[2]-2) {
c[k][e] = 0;
}
if (x[a0[k]] == fx[2]-2) {
aa = aa + k + "@";
c[k][e] = 1;
rx[k] += 30;
for (int k2 = 1; k2 < 280; k2++) {
for (int k1 = 1; k1 < 280; k1++)
;
}
rx[k] += 30;
for (int k2 = 1; k2 < 280; k2++) {
for (int k1 = 1; k1 < 280; k1++)
;
}
rx[k] += 30;
}
}
rxo(fx[2]);
setTitle(aa);
} else if (fx[2] > 6) {
e++;
for (int k = 1; k < 28; k++) {
if (z[a0[k]] == fx[2]-8) {
c[k][e] = 3;
for (int k2 = 1; k2 < 280; k2++) {
for (int k1 = 1; k1 < 280; k1++)
;
}
rz[k] += 30;
for (int k2 = 1; k2 < 280; k2++) {
for (int k1 = 1; k1 < 280; k1++)
;
}
rz[k] += 30;
rxo(fx[2]);
}
}
} else if (fx[2] < 7) {
e++;
aa = "";
for (int k = 1; k < 28; k++) {
if (y[a0[k]] == fx[2]-5) {
aa = aa + k + "@";
c[k][e] = 2;
for (int k2 = 1; k2 < 280; k2++) {
for (int k1 = 1; k1 < 280; k1++)
;
}
ry[k] += 30;
for (int k2 = 1; k2 < 280; k2++) {
for (int k1 = 1; k1 < 280; k1++)
;
}
ry[k] += 30;
}
}
rxo(fx[2]);
setTitle(aa);
}
// fx[0]=(int) (event.getX());
// rotate+=1;
xAngle = event.getX();
yAngle = event.getY();
}
setTitle(fx[1] + "@" + fy[1] + "@" + xdiff + "@" + ydiff + "@" + fx[0]
+ "@" + fx[2]);
return true;
}