Android OpenGL ES顯示3D模型
阿新 • • 發佈:2019-01-28
這篇文章我們來來看如何將一個STL檔案顯示出來,把STL檔案顯示出來,那麼我們就可以顯示任意的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;
}
}
歡迎關注公眾號,有什麼問題可以交流。