Android OpenGL显示任意3D模型文件

来源:互联网 发布:知乎入门级古典音乐 编辑:程序博客网 时间:2024/05/16 12:10

转载请注明出处:【huachao1001的专栏:http://blog.csdn.net/huachao1001】

*本篇文章已授权微信公众号 guolin_blog (郭霖)独家发布

前面两篇文章我们介绍了OpenGL相关的基本知识,现在我们已经会绘制基本的图案了,但是还远远不能满足我们的需求。我们要做的是显示任意的模型,这也是本文所要做的事情。在阅读本文之前,请先确保你已经看过我前面两篇文章:

  • 【Android OpenGL入门 】
  • 【Android OpenGL 显示基本图形及相关概念解读 】

虽然标题是说显示任意3D文件,但是本文主要是以STL格式文件为例。其他的格式本质上都是一样的,只是解析部分的代码不同而已。接下来我们开始学习~

1 STL文件

它是标准的3D文件格式,一般3D打印机都是支持打印STL文件,关于STL文件的格式、以及相关介绍请参考百度百科:【stl格式】。当然了,我在代码的注释中也会进行相关解释。

1.1 解析准备

首先,在解析STL文件格式之前,我们需要进行构思。我们无非就是把STL文件中的三角形的顶点信息提取出来。因此我们的主要目标就是把所有点信息读取出来。

但是,3D模型的坐标位置很随机,大小也随机。而不同的模型所处的位置不同,为了能够让模型处于手机显示中心,我们必须对模型进行移动、放缩处理。使得任意大小、任意位置的模型都能在我们的GLSurfaceView中以“相同”的大小显示。

因此,我们不仅仅要读取顶点信息,而且还要获取模型的边界信息。我们想象成一个立方体,这个立方体刚好包裹住模型。即我们要读取x、y、z三个方向上的最大值最小值。

1.2 开始解析

首先,我们定义一个Model类,用于表示一个模型对象:

package com.hc.opengl;import java.nio.FloatBuffer;/** * Package com.hc.opengl * Created by HuaChao on 2016/7/28. */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);    }   //···   //其他属性对应的setter、getter函数   //···}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72

接下来就是将stl文件转换成Model对象,我们定义一个STLReader类:

package com.hc.opengl;import android.content.Context;import java.io.File;import java.io.FileInputStream;import java.io.IOException;import java.io.InputStream;/** * Package com.hc.opengl * Created by HuaChao on 2016/7/28. */public class STLReader {    private StlLoadListener stlLoadListener;    public Model parserBinStlInSDCard(String path)                             throws IOException {        File file = new File(path);        FileInputStream fis = new FileInputStream(file);        return parserBinStl(fis);    }    public Model parserBinStlInAssets(Context context, String fileName)                             throws IOException {        InputStream is = context.getAssets().open(fileName);        return parserBinStl(is);    }    //解析二进制的Stl文件    public Model parserBinStl(InputStream in) throws IOException {        if (stlLoadListener != null)            stlLoadListener.onstart();        Model model = new Model();        //前面80字节是文件头,用于存贮文件名;        in.skip(80);        //紧接着用 4 个字节的整数来描述模型的三角面片个数        byte[] bytes = new byte[4];        in.read(bytes);// 读取三角面片个数        int facetCount = Util.byte4ToInt(bytes, 0);        model.setFacetCount(facetCount);        if (facetCount == 0) {            in.close();            return model;        }        // 每个三角面片占用固定的50个字节        byte[] facetBytes = new byte[50 * facetCount];        // 将所有的三角面片读取到字节数组        in.read(facetBytes);        //数据读取完毕后,可以把输入流关闭        in.close();        parseModel(model, facetBytes);        if (stlLoadListener != null)            stlLoadListener.onFinished();        return model;    }    /**     * 解析模型数据,包括顶点数据、法向量数据、所占空间范围等     */    private void parseModel(Model model, byte[] facetBytes) {        int facetCount = model.getFacetCount();        /**         *  每个三角面片占用固定的50个字节,50字节当中:         *  三角片的法向量:(1个向量相当于一个点)*(3维/点)*(4字节浮点数/维)=12字节         *  三角片的三个点坐标:(3个点)*(3维/点)*(4字节浮点数/维)=36字节         *  最后2个字节用来描述三角面片的属性信息         * **/        // 保存所有顶点坐标信息,一个三角形3个顶点,一个顶点3个坐标轴        float[] verts = new float[facetCount * 3 * 3];        // 保存所有三角面对应的法向量位置,        // 一个三角面对应一个法向量,一个法向量有3个点        // 而绘制模型时,是针对需要每个顶点对应的法向量,因此存储长度需要*3        // 又同一个三角面的三个顶点的法向量是相同的,        // 因此后面写入法向量数据的时候,只需连续写入3个相同的法向量即可        float[] vnorms = new float[facetCount * 3 * 3];        //保存所有三角面的属性信息        short[] remarks = new short[facetCount];        int stlOffset = 0;        try {            for (int i = 0; i < facetCount; i++) {                if (stlLoadListener != null) {                    stlLoadListener.onLoading(i, facetCount);                }                for (int j = 0; j < 4; j++) {                    float x = Util.byte4ToFloat(facetBytes, stlOffset);                    float y = Util.byte4ToFloat(facetBytes, stlOffset + 4);                    float z = Util.byte4ToFloat(facetBytes, stlOffset + 8);                    stlOffset += 12;                    if (j == 0) {//法向量                         vnorms[i * 9] = x;                        vnorms[i * 9 + 1] = y;                        vnorms[i * 9 + 2] = z;                        vnorms[i * 9 + 3] = x;                        vnorms[i * 9 + 4] = y;                        vnorms[i * 9 + 5] = z;                        vnorms[i * 9 + 6] = x;                        vnorms[i * 9 + 7] = y;                        vnorms[i * 9 + 8] = z;                    } else {//三个顶点                        verts[i * 9 + (j - 1) * 3] = x;                        verts[i * 9 + (j - 1) * 3 + 1] = y;                        verts[i * 9 + (j - 1) * 3 + 2] = z;                        //记录模型中三个坐标轴方向的最大最小值                        if (i == 0 && j == 1) {                            model.minX = model.maxX = x;                            model.minY = model.maxY = y;                            model.minZ = model.maxZ = z;                        } else {                            model.minX = Math.min(model.minX, x);                            model.minY = Math.min(model.minY, y);                            model.minZ = Math.min(model.minZ, z);                            model.maxX = Math.max(model.maxX, x);                            model.maxY = Math.max(model.maxY, y);                            model.maxZ = Math.max(model.maxZ, z);                        }                    }                }                short r = Util.byte2ToShort(facetBytes, stlOffset);                stlOffset = stlOffset + 2;                remarks[i] = r;            }        } catch (Exception e) {            if (stlLoadListener != null) {                stlLoadListener.onFailure(e);            } else {                e.printStackTrace();            }        }        //将读取的数据设置到Model对象中        model.setVerts(verts);        model.setVnorms(vnorms);        model.setRemarks(remarks);    }    public static interface StlLoadListener {        void onstart();        void onLoading(int cur, int total);        void onFinished();        void onFailure(Exception e);    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157

注意到,我们需要频繁的将byte数组转为short、float类型,我们直接把这些函数装到一个工具类Util中:

package com.hc.opengl;import java.nio.ByteBuffer;import java.nio.ByteOrder;import java.nio.FloatBuffer;/** * Package com.hc.opengl * Created by HuaChao on 2016/7/28. */public class Util {    public static FloatBuffer floatToBuffer(float[] a) {        //先初始化buffer,数组的长度*4,因为一个float占4个字节        ByteBuffer bb = ByteBuffer.allocateDirect(a.length * 4);        //数组排序用nativeOrder        bb.order(ByteOrder.nativeOrder());        FloatBuffer buffer = bb.asFloatBuffer();        buffer.put(a);        buffer.position(0);        return buffer;    }    public static int byte4ToInt(byte[] bytes, int offset) {        int b3 = bytes[offset + 3] & 0xFF;        int b2 = bytes[offset + 2] & 0xFF;        int b1 = bytes[offset + 1] & 0xFF;        int b0 = bytes[offset + 0] & 0xFF;        return (b3 << 24) | (b2 << 16) | (b1 << 8) | b0;    }    public static short byte2ToShort(byte[] bytes, int offset) {        int b1 = bytes[offset + 1] & 0xFF;        int b0 = bytes[offset + 0] & 0xFF;        return (short) ((b1 << 8) | b0);    }    public static float byte4ToFloat(byte[] bytes, int offset) {        return Float.intBitsToFloat(byte4ToInt(bytes, offset));    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44

为了更好的表示三维坐标系下的一个点,我们定义Point类:

/** * Package com.hc.opengl * Created by HuaChao on 2016/7/28. */public class Point {    public float x;    public float y;    public float z;    public Point(float x, float y, float z) {        this.x = x;        this.y = y;        this.z = z;    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

2 编写Render

上一节我们只是拿数据而已,还没开始绘制,真正的大招现在才开始。因为我们目标是显示任意模型,因此,必须把模型移动到我们的“视野”中,才能看得到(当然了,如果图形本身就是在我们的视野中,那就不一定需要这样的操作了)。废话不多说,直接看源码:

 /** * Package com.hc.opengl * Created by HuaChao on 2016/7/28. */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, "huba.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);        //为了能有立体感觉,通过改变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());        // 设置三角形顶点数据源        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============================//    }    @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();    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105

在MainActivity中不断调用旋转函数:

package com.hc.opengl;public class MainActivity extends AppCompatActivity {    private boolean supportsEs2;    private GLSurfaceView glView;    private float rotateDegreen = 0;    private GLRenderer glRenderer;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        checkSupported();        if (supportsEs2) {            glView = new GLSurfaceView(this);            glRenderer = new GLRenderer(this);            glView.setRenderer(glRenderer);            setContentView(glView);        } else {            setContentView(R.layout.activity_main);            Toast.makeText(this, "当前设备不支持OpenGL ES 2.0!", Toast.LENGTH_SHORT).show();        }    }    public void rotate(float degree) {        glRenderer.rotate(degree);        glView.invalidate();    }    private Handler handler = new Handler() {        @Override        public void handleMessage(Message msg) {            rotate(rotateDegreen);        }    };    @Override    protected void onResume() {        super.onResume();        if (glView != null) {            glView.onResume();            //不断改变rotateDegreen值,实现旋转            new Thread() {                @Override                public void run() {                    while (true) {                        try {                            sleep(100);                            rotateDegreen += 5;                            handler.sendEmptyMessage(0x001);                        } catch (Exception e) {                            e.printStackTrace();                        }                    }                }            }.start();        }    }    private void checkSupported() {        ActivityManager activityManager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);        ConfigurationInfo configurationInfo = activityManager.getDeviceConfigurationInfo();        supportsEs2 = configurationInfo.reqGlEsVersion >= 0x2000;        boolean isEmulator = Build.VERSION.SDK_INT > Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1                && (Build.FINGERPRINT.startsWith("generic")                || Build.FINGERPRINT.startsWith("unknown")                || Build.MODEL.contains("google_sdk")                || Build.MODEL.contains("Emulator")                || Build.MODEL.contains("Android SDK built for x86"));        supportsEs2 = supportsEs2 || isEmulator;    }    @Override    protected void onPause() {        super.onPause();        if (glView != null) {            glView.onPause();        }    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92

3 最后一步

一切看起来都已经完成了,但似乎少了点什么。啊哈~,少了STL文件,其实网上有很多STL模型文件免费下载,大家可以随便搜索。我下载了一个胡巴的模型:

模型截图

模型截图

下载完成后,运行如下:

运行结果

看到结果是不是觉得很失望?貌似看不到轮廓,其实,主要是跟灯光有关,我们程序中没有设置灯光。我们知道,我们在真实世界中看到物体主要是物体表面发生漫反射。我们所看到的物体跟光源的位置、物体的材质等等有关。另外,也可以通过贴纹理来做到。但是到目前为止,我们还没有这些知识,代码里面也没有涉及到这些,因此我们这能看到当前这个样子。后面我们会继续深入学习相关知识,欢迎关注~。

好啦,最后献上源码吧~,注意,下载的源码中Model类的getCentrePoint函数需要修改,请以本文中的Model类为主。

源码地址:http://download.csdn.net/detail/huachao1001/9588619

原创粉丝点击