1. 程式人生 > >Android OpenGL ES顯示3D模型

Android OpenGL ES顯示3D模型

這篇文章我們來來看如何將一個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;
    }
}

歡迎關注公眾號,有什麼問題可以交流。
這裡寫圖片描述