Android OpenGL ES显示3D模型

时间:2022-01-03 03:55:04

这篇文章我们来来看如何将一个STL文件显示出来,把STL文件显示出来,那么我们就可以显示任意的3D模型了。

下面是显示一把狙击枪模型的效果图:
Android OpenGL ES显示3D模型

Android OpenGL ES显示3D模型

 什么是STL文件

网上的解释是这样的:.stl 文件是在计算机图形应用系统中,用于表示三角形网格的一种文件格式。 它的文件格式非常简单, 应用很广泛。STL是最多快速原型系统所应用的标准文件类型。STL是用三角网格来表现3D CAD模型。一般3D打印机都是支持打印STL文件。

OpenGL ES 支持绘制的基本几何图形分为三类:点,线段,三角形。也就是说OpenGL ES 只能绘制这三种基本几何图形。任何复杂的2D或是3D图形都是通过这三种几何图形构造而成的。STL使用三角网格来表现3D模型,因此我们可以使用Open GL绘制出STL所表示的模型。

 如何解析STL文件

首先把STL文件中的三角形的顶点信息提取出来。我们的主要目标就是把所有点信息读取出来。

同时,为了让在手机中心显示,我们必须对模型进行移动、放缩处理。使得任意大小、任意位置的模型都能在我们的GLSurfaceView中以“相同”的大小显示。因此,我们不仅仅要读取顶点信息,而且还要获取模型的边界信息。即我们要读取x、y、z三个方向上的最大值最小值。

下面看看GLRenderer.java这个类:

public class GLRenderer implements GLSurfaceView.Renderer {

private Model model;
private Point mCenterPoint;
private Point eye = new Point(0, 0, -3);
private Point up = new Point(0, 1, 0);
private Point center = new Point(0, 0, 0);
private float mScalef = 1;
private float mDegree = 0;

public GLRenderer(Context context) {
try {
model = new STLReader().parserBinStlInAssets(context, "jqr.stl");
} catch (IOException e) {
e.printStackTrace();
}
}

public void rotate(float degree) {
mDegree = degree;
}

@Override
public void onDrawFrame(GL10 gl) {
// 清除屏幕和深度缓存
gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
// 重置当前的模型观察矩阵
gl.glLoadIdentity();


//眼睛对着原点看
GLU.gluLookAt(gl, eye.x, eye.y, eye.z, center.x,
center.y, center.z, up.x, up.y, up.z);
//注意坐标轴也选择了(逆时针)
gl.glRotatef(90f, 0f, 1f, 0f);
gl.glRotatef(-90f, 1f, 0f, 0f);
gl.glRotatef(180f, 0f, 0f, 1f);
//为了能有立体感觉,通过改变mDegree值,让模型不断旋转
gl.glRotatef(mDegree, 0, 1, 0);

//将模型放缩到View刚好装下
gl.glScalef(mScalef, mScalef, mScalef);
//把模型移动到原点
gl.glTranslatef(-mCenterPoint.x, -mCenterPoint.y,
-mCenterPoint.z);


//===================begin==============================//

//允许给每个顶点设置法向量
gl.glEnableClientState(GL10.GL_NORMAL_ARRAY);
// 允许设置顶点
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
// 允许设置颜色

//设置法向量数据源
gl.glNormalPointer(GL10.GL_FLOAT, 0, model.getVnormBuffer());
//使用 glVertexPointer 通知 OpenGL ES 图形库顶点坐标。 设置三角形顶点数据源
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, model.getVertBuffer());

// 绘制三角形
gl.glDrawArrays(GL10.GL_TRIANGLES, 0, model.getFacetCount() * 3);

// 取消顶点设置
gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
//取消法向量设置
gl.glDisableClientState(GL10.GL_NORMAL_ARRAY);

//=====================end============================//
gl.glFinish();
mDegree++;
}


@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {

// 设置OpenGL场景的大小,(0,0)表示窗口内部视口的左下角,(width, height)指定了视口的大小
gl.glViewport(0, 0, width, height);

gl.glMatrixMode(GL10.GL_PROJECTION); // 设置投影矩阵
gl.glLoadIdentity(); // 设置矩阵为单位矩阵,相当于重置矩阵
GLU.gluPerspective(gl, 45.0f, ((float) width) / height, 1f, 100f);// 设置透视范围

//以下两句声明,以后所有的变换都是针对模型(即我们绘制的图形)
gl.glMatrixMode(GL10.GL_MODELVIEW);
gl.glLoadIdentity();


}

@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
gl.glEnable(GL10.GL_DEPTH_TEST); // 启用深度缓存
gl.glClearDepthf(1.0f); // 设置深度缓存值
gl.glDepthFunc(GL10.GL_LEQUAL); // 设置深度缓存比较函数
gl.glShadeModel(GL10.GL_SMOOTH);// 设置阴影模式GL_SMOOTH
float r = model.getR();
//r是半径,不是直径,因此用0.5/r可以算出放缩比例
mScalef = 0.5f / r;
mCenterPoint = model.getCentrePoint();
//开启光
openLight(gl);
//添加材质属性
enableMaterial(gl);
}

float[] ambient = {0.9f, 0.9f, 0.9f, 1.0f,};
float[] diffuse = {0.5f, 0.5f, 0.5f, 1.0f,};
float[] specular = {1.0f, 1.0f, 1.0f, 1.0f,};
float[] lightPosition = {0.5f, 0.5f, 0.5f, 0.0f,};

public void openLight(GL10 gl) {
gl.glEnable(GL10.GL_LIGHTING);
gl.glEnable(GL10.GL_LIGHT0);
gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_AMBIENT, Util.floatToBuffer(ambient));
gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_DIFFUSE, Util.floatToBuffer(diffuse));
gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_SPECULAR, Util.floatToBuffer(specular));
gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_POSITION, Util.floatToBuffer(lightPosition));
}

float[] materialAmb = {0.5f, 0.5f, 0.5f, 1.0f};
float[] materialDiff = {1.0f, 0.5f, 0.0f, 1.0f};//漫反射
float[] materialSpec = {1.0f, 0.5f, 0.0f, 1.0f};

public void enableMaterial(GL10 gl) {

//材料对环境光的反射情况
gl.glMaterialfv(GL10.GL_FRONT_AND_BACK, GL10.GL_AMBIENT, Util.floatToBuffer(materialAmb));
//散射光的反射情况
gl.glMaterialfv(GL10.GL_FRONT_AND_BACK, GL10.GL_DIFFUSE, Util.floatToBuffer(materialDiff));
//镜面光的反射情况
gl.glMaterialfv(GL10.GL_FRONT_AND_BACK, GL10.GL_SPECULAR, Util.floatToBuffer(materialSpec));

}
}

Model类中存放模型的所有相关信息以及相应的辅助类解析模型STL文件。

Model.java文件:

public class Model {
//三角面个数
private int facetCount;
//顶点坐标数组
private float[] verts;
//每个顶点对应的法向量数组
private float[] vnorms;
//每个三角面的属性信息
private short[] remarks;

//顶点数组转换而来的Buffer
private FloatBuffer vertBuffer;

//每个顶点对应的法向量转换而来的Buffer
private FloatBuffer vnormBuffer;
//以下分别保存所有点在x,y,z方向上的最大值、最小值
float maxX;
float minX;
float maxY;
float minY;
float maxZ;
float minZ;


//返回模型的中心点
public Point getCentrePoint() {
float cx = minX + (maxX - minX) / 2;
float cy = minY + (maxY - minY) / 2;
float cz = minZ + (maxZ - minZ) / 2;
return new Point(cx, cy, cz);
}

//包裹模型的最大半径
public float getR() {
float dx = (maxX - minX);
float dy = (maxY - minY);
float dz = (maxZ - minZ);
float max = dx;
if (dy > max)
max = dy;
if (dz > max)
max = dz;
return max;
}


//设置顶点数组的同时,设置对应的Buffer
public void setVerts(float[] verts) {
this.verts = verts;
vertBuffer = Util.floatToBuffer(verts);
}

//设置顶点数组法向量的同时,设置对应的Buffer
public void setVnorms(float[] vnorms) {
this.vnorms = vnorms;
vnormBuffer = Util.floatToBuffer(vnorms);
}


public int getFacetCount() {
return facetCount;
}

public void setFacetCount(int facetCount) {
this.facetCount = facetCount;
}

public short[] getRemarks() {
return remarks;
}

public void setRemarks(short[] remarks) {
this.remarks = remarks;
}

public FloatBuffer getVnormBuffer() {
return vnormBuffer;
}

public void setVnormBuffer(FloatBuffer vnormBuffer) {
this.vnormBuffer = vnormBuffer;
}

public FloatBuffer getVertBuffer() {
return vertBuffer;
}

public void setVertBuffer(FloatBuffer vertBuffer) {
this.vertBuffer = vertBuffer;
}
}

相关代码下载地址:http://download.csdn.net/detail/zxc123e/9710746

欢迎关注公众号,有什么问题可以交流。
Android OpenGL ES显示3D模型