转 Android中3D技术基础详解 ---制作一个可旋转的立方体
一:
先介绍下3D中一些专业术语:
1.视区:viewport.假如你从窗户往外看,若将窗户更换为高质量的照片,只要你部移动,看到的景象就是一样的。这个“窗户”就被称为视区。
2.视野:根据眼睛和窗户之间的距离和窗户的大小,可以看到的外面世界多少事不同的,这称为视野。
在3D计算机图形领域,计算机屏幕作为视区。你的工作就是让用户将其窗口想象成玻璃后面的另一个世界。OpenGL图形库就是用于完成此任务的API。
类一:主类
layout中的main可删掉,因为要自定义View
代码如下:
package org.example.opengl;
import android.app.Activity;
import android.os.Bundle;
public class OpenGL extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(new GLView(this));
}
}
类二:GLView
这个类是视图类,实现主类中的布局。其中定义了一个线程,所有功能都在线程里面来完成。
package org.example.opengl;
import android.content.Context;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
class GLView extends SurfaceView implements SurfaceHolder.Callback {
private GLThread glThread;
GLView(Context context) {
super(context);
getHolder().addCallback(this);
getHolder().setType(SurfaceHolder.SURFACE_TYPE_GPU);
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
glThread = new GLThread(this);
glThread.start();
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
glThread.requestExitAndWait();
glThread = null;
}
}
三:GLCube类。这个类中定义OpenGl句柄方面的东西。代码如下:
package org.example.opengl;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.IntBuffer;
import javax.microedition.khronos.opengles.GL10;
class GLCube {
private final IntBuffer mVertexBuffer;
public GLCube(){
int one = 65536;
int half = one / 2;
int vertices[] = {
//前
-half,-half,half,half,-half,half,
-half,half,half,half,half,half,
//后
-half,-half,-half,-half,half,-half,
half,-half,-half,half,half,-half,
//左
-half,-half,half,-half,half,half,
-half,-half,-half,-half,half,-half,
//右
half,-half,-half,half,half,-half,
half,-half,half,half,half,half,
//上
-half,half,half,half,half,half,
-half,half,-half,half,half,-half,
//下
-half,-half,half,-half,-half,-half,
-half,half,-half,half,half,-half,
};
ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length * 4);
vbb.order(ByteOrder.nativeOrder());
mVertexBuffer = vbb.asIntBuffer();
mVertexBuffer.put(vertices);
mVertexBuffer.position(0);
}
public void draw(GL10 gl){
gl.glVertexPointer(3,GL10.GL_FIXED,0,mVertexBuffer);
gl.glColor4f(1,1,1,1);
gl.glNormal3f(0, 0, 1);
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP,0,4);
gl.glNormal3f(0, 0, -1);
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP,4,4);
gl.glColor4f(1,1,1,1);
gl.glNormal3f(-1,0, 0);
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP,8,4);
gl.glNormal3f(1,0, 0);
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP,12,4);
gl.glColor4f(1,1,1,1);
gl.glNormal3f(0,1,0);
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP,16,4);
gl.glNormal3f(0,-1,0);
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP,20,4);
}
}
四:GLThread线程类。类中drawFrame方法中绘制图像。
package org.example.opengl;
import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGL11;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.egl.EGLContext;
import javax.microedition.khronos.egl.EGLDisplay;
import javax.microedition.khronos.egl.EGLSurface;
import javax.microedition.khronos.opengles.GL10;
import android.app.Activity;
import android.content.Context;
import android.opengl.GLU;
class GLThread extends Thread {
private final GLView view;
private boolean done = false;
private final GLCube cube = new GLCube();
private long startTime;
GLThread(GLView view) {
this.view = view;
}
@Override
public void run() {
EGL10 egl = (EGL10)EGLContext.getEGL();
EGLDisplay display = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
int[] version = new int[2];
egl.eglInitialize(display, version);
int[] configSpec = {
EGL10.EGL_RED_SIZE,5,EGL10.EGL_GREEN_SIZE,6,EGL10.EGL_BLUE_SIZE,5,EGL10.EGL_DEPTH_SIZE,16,EGL10.EGL_NONE
};
EGLConfig[] configs = new EGLConfig[1];
int[] numConfig = new int[1];
egl.eglChooseConfig(display, configSpec, configs, 1, numConfig);
EGLConfig config = configs[0];
EGLContext glc = egl.eglCreateContext(display, config, EGL10.EGL_NO_CONTEXT,null);
EGLSurface surface = egl.eglCreateWindowSurface(display, config, view.getHolder(),null);
egl.eglMakeCurrent(display, surface, surface, glc);
GL10 gl = (GL10) (glc.getGL()); //返回实际的OpenGL接口
init(gl);
while(!done){
drawFrame(gl);
egl.eglSwapBuffers(display, surface);
if(egl.eglGetError() == EGL11.EGL_CONTEXT_LOST){
Context c = view.getContext();
if(c instanceof Activity){
((Activity)c).finish();
}
}
}
//释放openGL资源
egl.eglMakeCurrent(display, EGL10.EGL_NO_SURFACE,EGL10.EGL_NO_SURFACE,EGL10.EGL_NO_CONTEXT);
egl.eglDestroySurface(display, surface);
egl.eglDestroyContext(display, glc);
egl.eglTerminate(display);
}
public void requestExitAndWait(){
done = true;
try {
join();
} catch (InterruptedException e) {
}
}
private void init(GL10 gl){
gl.glViewport(0, 0, view.getWidth(),view.getHeight());
gl.glMatrixMode(GL10.GL_PROJECTION);
gl.glLoadIdentity();
float ratio = (float)view.getWidth() / view.getHeight();
GLU.gluPerspective(gl, 45.0f,ratio,1,100f);
gl.glEnable(GL10.GL_DEPTH_TEST);
gl.glDepthFunc(GL10.GL_LEQUAL);
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
//定义光源
float lightAmbient[] = new float[]{0.2f,0.2f,0.2f,1};
float lightDiffuse[] = new float[]{1,1,1,1};
float[] lightPos = new float[]{1,1,1,1};
gl.glEnable(GL10.GL_LIGHTING);
gl.glEnable(GL10.GL_LIGHT0);
gl.glLightfv(GL10.GL_LIGHT0,GL10.GL_AMBIENT,lightAmbient,0);
gl.glLightfv(GL10.GL_LIGHT0,GL10.GL_DIFFUSE,lightDiffuse,0);
gl.glLightfv(GL10.GL_LIGHT0,GL10.GL_POSITION,lightPos,0);
//材料
float matAmbient[] = new float[]{1,1,1,1};
float matDiffuse[] = new float[]{1,1,1,1};
gl.glMaterialfv(GL10.GL_FRONT_AND_BACK,GL10.GL_AMBIENT,matAmbient,0);
gl.glMaterialfv(GL10.GL_FRONT_AND_BACK,GL10.GL_DIFFUSE,matDiffuse,0);
startTime = System.currentTimeMillis();
}
private void drawFrame(GL10 gl){
long elapsed = System.currentTimeMillis() - startTime;
gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); //把屏幕清理为黑
gl.glMatrixMode(GL10.GL_MODELVIEW);
gl.glLoadIdentity();
gl.glTranslatef(0, 0,-3.0f);
gl.glRotatef(elapsed * (30f/1000), 0,1,0);
gl.glRotatef(elapsed * (15f/1000), 1,0,0);
cube.draw(gl);
}
}
这是个最简单的OpenGL的例子。
提示两点:
1.用Android是实现了OpenGL ES接口。其中有很多是OpenGL中的函数,可能让大家感觉比较陌生,这个其实不难,可以找OpenGL方面的书来看看。
2.在这个例子中用的是绘图模式是:三角形条带(一种常见的OpenGL绘图模式),在这个模式中,制定两个起点,然后每个后续的点与前两个起点一起定义一个三角形