今天要做的是实现绘制立方体,这个算是我们做的第一个3D图,先看下效果吧。
这个实现的代码是我在网上找的,代码中也附加了很多注释,没有注释的地方也可以在我之前文章中的代码中找到相应注释,所以表面上完全可以看得懂,当然想要知道实现原理可以查看相应关键方法的实现方式以及图像学原理。方法实现在我的gl10方法解析中都可以找到,图形学原理可以在我的相关文章中可以找到,当然这些都在更新中。
下面是代码了:
cube.java
package test.cube;
import android.app.Activity;
import android.opengl.GLSurfaceView;
import android.os.Bundle;
public class cube extends Activity {
private GLSurfaceView mGLSurfaceView;
/** Calledwhen the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mGLSurfaceView=new GLSurfaceView(this);
mGLSurfaceView.setRenderer(new CubeRenderer());
setContentView(mGLSurfaceView);
}
@Override
protected void onResume() {
// Ideally a game should implement onResume() andonPause()
// to take appropriate action when the activity loosesfocus
super.onResume();
mGLSurfaceView.onResume();
}
@Override
protected void onPause() {
// Ideally a game should implement onResume() andonPause()
// to take appropriate action when the activity loosesfocus
super.onPause();
mGLSurfaceView.onPause();
}
}
CubeRenderer.java
package test.cube;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
import android.opengl.GLSurfaceView;
import android.opengl.GLU;
public class CubeRenderer implements GLSurfaceView.Renderer{
public void onDrawFrame(GL10 gl) {
gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
//一般的opengl程序,首先要做的就是清屏
gl.glMatrixMode(GL10.GL_MODELVIEW);//紧着这设置模型视图矩阵
gl.glLoadIdentity();
//视点变换,将相机位置设置为(0.0.3),同时指向(0.0.0)点,竖直向量为(0.1.0)指向正上方
GLU.gluLookAt(gl, 0, 0, 3, 0, 0,0, 0, 1,0);
//设置模型位置旋转及缩放信息
gl.glTranslatef(0, 0.0f, -1.0f);//将模型位置设置为(0.0.-1)
float angle=30.0f;
gl.glRotatef(angle,0,1, 0);//绕模型自身y轴旋转30度。
gl.glRotatef(angle,1,0, 0);//绕模型自身x轴旋转30度。
gl.glScalef(1.2f, 1.2f, 1.2f);//设置三方向的缩放系数
gl.glColor4f(0.0f,0.0f, 0.0f, 1.0f);
mCube.draw(gl,gl.GL_LINES);//渲染立方体
mCube.draw(gl,gl.GL_TRIANGLES);//渲染立方体
}
//当屏幕 改变时候,比如手机横着拿,变成竖着拿,先onSurfaceCreated在两次onSurfaceChanged 。
public void onSurfaceChanged(GL10 gl, int width, int height) {
//设置视口
gl.glViewport(0,0, width, height);
float ratio = (float) width / height;
gl.glMatrixMode(GL10.GL_PROJECTION);//设置投影矩阵为透视投影
gl.glLoadIdentity();
gl.glFrustumf(-ratio, ratio, -1, 1, 1,10);//正交投影
}
private MyCube mCube;
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT,GL10.GL_FASTEST);
gl.glClearColor(1,1,1,1);
gl.glDisable(GL10.GL_CULL_FACE);
//gl.glEnable(GL10.GL_CULL_FACE);
gl.glShadeModel(GL10.GL_SMOOTH);
//gl.glShadeModel(GL10.GL_FLAT);
gl.glEnable(GL10.GL_DEPTH_TEST);
gl.glLineWidth(4.0f);
mCube=new MyCube();
}
}
MyCube.java
package test.cube;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import javax.microedition.khronos.opengles.GL10;
public class MyCube {
private FloatBuffer mVertexBuffer;
/**
* 顶点颜色Buffer对象
*/
privateFloatBuffer mColorBuffer;
/**
* 实体模式下顶点索引对象
*/
privateByteBuffer mIndexBuffer;
/**
* 线框模式下顶点索引对象
*/
private ByteBuffer mLineIndexBuffer;
public MyCube()
{
float one = 1.0f;
/**
* 顶点位置坐标数组
* 立方体有8个顶点
*/
float vertices[]= {
-one, -one, -one,//Vertex 0
one, -one, -one,//1
one, one, -one,//2
-one, one, -one,//3
-one, -one, one,//4
one, -one, one,//5
one, one, one,//6
-one, one, one,//7
};
/**
* 顶点颜色数组
* 分别给8个顶点指定不同的颜色, RGBA 模式
*/
float colors[] ={
0, 0, 0, one,//Vertex 0
one, 0, 0, one,//1
one, one, 0, one,//2
0, one, 0, one,//3
0, 0, one, one,//4
one, 0, one, one,//5
one, one, one, one,//6
0, one, one, one,//7
};
/**
* 实体模式下,立方体有6个面,每个面由两个三角形组成
* 这里分别指定每个面的索引。
*/
byte indices[] ={
0, 4, 5, 0, 5, 1,//Face 0
1, 5, 6, 1, 6, 2,// 1
2, 6, 7, 2, 7, 3,//2
3, 7, 4, 3, 4, 0,//3
4, 7, 6, 4, 6, 5,//4
3, 0, 1, 3, 1, 2//5
};
/**
* 线框模式下,立方体有8个顶点,12条边缘,
* 这里指定画线模式所用到的12条线段
*/
bytelineIndices[] = {
0, 1,//Line 0
0, 3,//1
0, 4,//2
1, 2,//3
1, 5,//4
2, 3,//5
2, 6,//6
3, 7,//7
4, 5,//8
4, 7,//9
5, 6,//10
6, 7//11
};
//根据数组来生成用于直接渲染的java.nio.Buffer
//顶点位置Buffer
ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length*4);
vbb.order(ByteOrder.nativeOrder());
mVertexBuffer =vbb.asFloatBuffer();
mVertexBuffer.put(vertices);
mVertexBuffer.position(0);
//顶点颜色Buffer
ByteBuffer cbb = ByteBuffer.allocateDirect(colors.length*4);
cbb.order(ByteOrder.nativeOrder());
mColorBuffer =cbb.asFloatBuffer();
mColorBuffer.put(colors);
mColorBuffer.position(0);
//实体模式下顶点索引Buffer
mIndexBuffer =ByteBuffer.allocateDirect(indices.length);
mIndexBuffer.put(indices);
mIndexBuffer.position(0);
//线框模式下顶点索引Buffer
mLineIndexBuffer =ByteBuffer.allocateDirect(lineIndices.length);
mLineIndexBuffer.put(lineIndices);
mLineIndexBuffer.position(0);
}
/**
* 根据传入的模式来分别渲染实体模式立方体以及线框模式立方体
* @param gl - OpenGL ES渲染对象
* @param mode - 渲染模式,GL10.GL_TRIANGLES 表示实体模式,GL10.GL_LINES 表示线框模式
*/
public void draw(GL10 gl, int mode) {
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
if(mode ==GL10.GL_TRIANGLES) {
//如果是实体模式,则启用颜色,给每一个顶点指定一个颜色
gl.glEnableClientState(GL10.GL_COLOR_ARRAY);
gl.glVertexPointer(3,GL10.GL_FLOAT, 0, mVertexBuffer);
gl.glColorPointer(4,GL10.GL_FLOAT, 0, mColorBuffer);
gl.glDrawElements(GL10.GL_TRIANGLES, 36, GL10.GL_UNSIGNED_BYTE, mIndexBuffer);
}
else if(mode == GL10.GL_LINES) {
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, mVertexBuffer);
gl.glDrawElements(GL10.GL_LINES, 24, GL10.GL_UNSIGNED_BYTE, mLineIndexBuffer);
}
}
}
其实我觉的代码中的注释已经很好了,那我下面在简单解释一下。
这个程序中开了4个缓冲区,分别用来保存顶点坐标,顶点颜色,索引,线段索引。
顶点坐标其实就是这个立方体的8个顶点的坐标,顶点颜色是这8个顶点的颜色,值得注意的是,这个顶点的坐标与其颜色是相对应的,在绘制的时候,绘制一个顶点就会填涂相应的颜色了。而索引矩阵中的值是对应的顶点矩阵中的相应角标,线段所以中也是对应顶点矩阵中的相应角标。
还要注意的是glDrawElements是绘制三角形的,立方体的面都是由两个三角形,当然绘制顺序也是有讲究的,我的相关文章中有讲解,这里的代码是没有开启消隐功能的。