精华内容
下载资源
问答
  • 解析obj文件
    千次阅读
    2020-11-25 16:58:33

    OBJ文件是一种文本文件,可以直接用写字板或是文档编辑器进行查看和编辑修改。
    1.OBJ文件特点
    (1)OBJ文件是一种3D模型文件,不包含动画、材质特性、贴图路径、动力学、粒子等信息。
    (2)OBJ文件主要支持多边形(Polygons)模型。
    (3)OBJ文件支持三个点以上的面。
    (4)OBJ文件支持法线和贴图坐标。

    2.OBJ文件结构:
    以一个立方体模型的test.obj文件为例,以下为文件内容:

    mtllib cube.mtl
    g default
    v -0.500000 -0.500000 0.500000
    v 0.500000 -0.500000 0.500000
    v -0.500000 0.500000 0.500000
    v 0.500000 0.500000 0.500000
    v -0.500000 0.500000 -0.500000
    v 0.500000 0.500000 -0.500000
    v -0.500000 -0.500000 -0.500000
    v 0.500000 -0.500000 -0.500000
    vt 0.001992 0.001992
    vt 0.998008 0.001992
    vt 0.001992 0.998008
    vt 0.998008 0.998008
    vt 0.001992 0.001992
    vt 0.998008 0.001992
    vt 0.001992 0.998008
    vt 0.998008 0.998008
    vt 0.001992 0.001992
    vt 0.998008 0.001992
    vt 0.001992 0.998008
    vt 0.998008 0.998008
    vt 0.001992 0.001992
    vt 0.998008 0.001992
    vt 0.001992 0.998008
    vt 0.998008 0.998008
    vt 0.001992 0.001992
    vt 0.998008 0.001992
    vt 0.001992 0.998008
    vt 0.998008 0.998008
    vt 0.998008 0.998008
    vt 0.001992 0.998008
    vt 0.998008 0.001992
    vt 0.001992 0.001992
    vn 0.000000 0.000000 1.000000
    vn 0.000000 0.000000 1.000000
    vn 0.000000 0.000000 1.000000
    vn 0.000000 0.000000 1.000000
    vn 0.000000 1.000000 0.000000
    vn 0.000000 1.000000 0.000000
    vn 0.000000 1.000000 0.000000
    vn 0.000000 1.000000 0.000000
    vn 0.000000 0.000000 -1.000000
    vn 0.000000 0.000000 -1.000000
    vn 0.000000 0.000000 -1.000000
    vn 0.000000 0.000000 -1.000000
    vn 0.000000 -1.000000 0.000000
    vn 0.000000 -1.000000 0.000000
    vn 0.000000 -1.000000 0.000000
    vn 0.000000 -1.000000 0.000000
    vn 1.000000 0.000000 0.000000
    vn 1.000000 0.000000 0.000000
    vn 1.000000 0.000000 0.000000
    vn 1.000000 0.000000 0.000000
    vn -1.000000 0.000000 0.000000
    vn -1.000000 0.000000 0.000000
    vn -1.000000 0.000000 0.000000
    vn -1.000000 0.000000 0.000000
    s 1
    g pCube1
    usemtl file1SG
    f 1/1/1 2/2/2 3/3/3
    f 3/3/3 2/2/2 4/4/4
    s 2
    f 3/13/5 4/14/6 5/15/7
    f 5/15/7 4/14/6 6/16/8
    s 3
    f 5/21/9 6/22/10 7/23/11
    f 7/23/11 6/22/10 8/24/12
    s 4
    f 7/17/13 8/18/14 1/19/15
    f 1/19/15 8/18/14 2/20/16
    s 5
    f 2/5/17 8/6/18 4/7/19
    f 4/7/19 8/6/18 6/8/20
    s 6
    f 7/9/21 1/10/22 5/11/23
    f 5/11/23 1/10/22 3/12/24
    

    3.OBJ文件分析
    从文件内容看,其内容比较简单,注释行以符号“#”为开头,空格和空行可以随意加到文件中以增加文件的可读性。有字的数据行都由关键字(Keyword)开头,简单解释了这一行的数据时是什么。而多行之间可以使用连接符反斜杠()将不同意义的数据逻辑地连接在一起表示一行(如果要自己对文件进行修改,特别需要注意连接符()后面不能出现空格或Tab格,否则文件将出错)。

    下面将逐一解释文件中每行数据代表的意义:
    简单来讲,
    以字母"v"开头的行是顶点数据,在v -0.500000 -0.500000 0.500000中左边第一个" -0.500000"是这个顶点的X轴坐标值,中间的"-0.500000"为Y轴坐标值,最后的"0.500000"为Z轴坐标值,另外它的索引号是1(索引号是在画图时规定了的,在这里不做讨论)。
    以字母"vt"开头的行是顶点数据的贴图/纹理坐标点 (Texture vertices),其值为u, v,如vt 0.001992 0.001992。纹理坐标指的是,纹理图片如果被放在屏幕上显示时,以屏幕左下角为原点的坐标。
    以字母"vn"开头的行是顶点数据之法线向量(normal),其值为x,y,z,如vn 0.000000 0.000000 1.000000。其实这个法向量是表示顶点的朝向,由顶点的xyz的坐标分别相加再除以3得到的。假设有三个顶点组成一个面,面是有两个朝向,向里或向外,所以可以通过顶点的朝向来确定面的朝向,特别的这三个顶点的法向量是相同的。
    以字母"f"开头的行是表示面(face),以3个"v/vt/vn"的索引形式组成。比如obj文件中
    f 3/13/5 4/14/6 5/15/7 ,表示由第2、第13、第5这三个顶点组成了一个三角平面,平面的纹理由第4、第14、第6这三个纹理坐标形成,而这个平面的朝向是第5、第15、第7这三个顶点的法向量的平均值决定的。

    4.对于索引数据而言,通常写的3D程序中一个索引值对应于一个顶点,这个顶点有固定的空间坐标,纹理坐标,法线等信息。
    而对于obj文件,一个索引值对应于一个顶点数据的空间坐标或者纹理坐标或法线,且均是由1开始计数。
    比如说,对于上述的立方体obj文件中,
    以字母"v"开始的第一行直到结束的第八行分别对应于索引1-8,
    以字母"vt"开始的第一行直到结束的第二十四行分别对应于索引1-24,
    以字母"vn"开始的第一行直到结束的第二十四行分别对应于索引1-24.....
    因此需要取特别的行数据时,可以以 "v"索引/"vt"索引/"vn"索引 的格式自由组合,描述顶点信息。如v3/vt24/vn19

    补充:名词解释
    (1)顶点数据(Vertex data):
    v 几何体顶点(Geometric vertices)
    vt 贴图坐标点(Texture vertices)
    vn 顶点法线(Vertex normals)
    vp 参数空格顶点 (Parameter space vertices)

    (2)元素(Elements):
    p 点(Point)
    l 线(Line)
    f 面(Face)
    curv 曲线(Curve)
    curv2 2D曲线(2D curve)
    surf 表面(Surface)

    (3)自由形态曲线(Free-form curve)/表面主体陈述(surface body statements):
    parm 参数值(Parameter values )
    trim 外部修剪循环(Outer trimming loop)
    hole 内部整修循环(Inner trimming loop)
    scrv 特殊曲线(Special curve)
    sp 特殊的点(Special point)
    end 结束陈述(End statement)

    (4)成组(Grouping):
    g 组名称(Group name)
    s 光滑组(Smoothing group)
    mg 合并组(Merging group)
    o 对象名称(Object name)

    更多相关内容
  • android读取obj文件

    2020-12-13 18:30:26
    android平台使用opengl读取obj文件的 2个例子,资源中带有obj文件,只需要拷贝到sdcard根目录中就能使用。
  • 此压缩包内为带纹理的obj文件,用win10自带3D查看器便可打开,解析见:https://blog.csdn.net/qq_41102371/article/details/108880797
  • Android OpenGL 解析obj文件绘制3D模型

    千次阅读 2019-01-16 10:38:08
    * 解析obj文件时,数据类型由开头的首字母决定 * switch(首字母){ * case "v": * 顶点坐标数据 * break; * case "vn": * 法向量坐标数据 * break; * case "vt": * 纹理坐标数据 * break; * case ...

    绘制3D模型一般我们都会用到OpenGL里面的一些api,看看如何使用这些api绘制3的模型。

    使用OpenGl绘制3D模型我主要用两种方式:

    1:glDrawArrays(int mode, int first,int count)  

    参数1:有三种取值

    1.GL_TRIANGLES:每三个顶之间绘制三角形,之间不连接

    2.GL_TRIANGLE_FAN:以V0V1V2,V0V2V3,V0V3V4,……的形式绘制三角形

    3.GL_TRIANGLE_STRIP:顺序在每三个顶点之间均绘制三角形。这个方法可以保证从相同的方向上所有三角形均被绘制。以V0V1V2,V1V2V3,V2V3V4……的形式绘制三角形

    参数2:从数组缓存中的哪一位开始绘制,一般都定义为0

    参数3:顶点的数量

    2:glDrawElements(int mode,int count,int type,java.nio.Buffer indices)

    mode——指明被渲染的是哪种图元,被允许的符号常量有GL_POINTS,GL_LINE_STRIP,GL_LINE_LOOP,GL_LINES,GL_TRIANGLE_STRIP, GL_TRIANGLE_FAN和GL_TRIANGLES

    count——指明被渲染的元素个数。

    type——指明索引指的类型,不是GL_UNSIGNED_BYTE就是GL_UNSIGNED_SHORT。

    indices——指明存储索引的位置

     

    第一种 glDrawArrays 绘制3D模型,主要分为两大部分。第一步,先将3D模型的3维数组坐标写好。第二步,将GLSurfaceView的渲染器配置好,将坐标数组转为FloatBuffer,直接开始绘制就OK。使用这种方式绘制3D模型,一定要将做表数组写好,坐标数组的元素顺序与后面你使用的绘制类型有关,我建议可以根据obj模型文件来确定数组。我的数组是直接从obj模型文件中提取的。

    /**
     * 解析3D Obj 文件
     * 解析obj文件时,数据类型由开头的首字母决定
     * switch(首字母){
     *     case "v":
     *      顶点坐标数据
     *     break; 
     *     case "vn":
     *      法向量坐标数据
     *     break; 
     *     case "vt":
     *      纹理坐标数据
     *     break; 
     *     case "usemtl":
     *      材质
     *     break; 
     *     case "f":
     *      索引
     *     break;
     * }
     */
    public class ObjLoaderUtil {
        // 存放解析出来的顶点的坐标
        private ArrayList<ObjVertexs> mVertexArrayList;
        //存放解析出来面的索引
        private ArrayList<Integer> mIndexArrayList;
        //存放解析出来的法线坐标
        private ArrayList<ObjVertexs> mNormalArrayList;
        //存放解析出来的法线索引
        private ArrayList<Integer> mIndexNormalArrayList;
        //存放3D模型的顶点坐标数据
        public float[] mSurfaceFloat;
        //存放顶点坐标的floatbuffer
        public FloatBuffer mVertexFloatBuffer;
        //顶点坐标索引buffer
        public FloatBuffer mIndexVertexBuffer;
        //存放法线坐标的floatbuffer
        public FloatBuffer mNormalFloatBuffer;
    
    
        public ObjLoaderUtil() {
            mVertexArrayList = new ArrayList();
            mIndexArrayList = new ArrayList<>();
            mNormalArrayList = new ArrayList<>();
            mIndexNormalArrayList = new ArrayList<>();
        }
    
        /**
         * 将obj文件中的顶点坐标与面索引解析出来,存放集合中
         *
         * @param mContext
         * @throws IOException
         */
        public void load(Context mContext) throws IOException {
            //获取assets文件夹下的obj文件的数据输入流
            InputStream mInputStream = mContext.getClass().getClassLoader().getResourceAsStream("assets/Lion_OBJ.obj");
            InputStreamReader mInputStreamReader = new InputStreamReader(mInputStream);
            BufferedReader mBufferedReader = new BufferedReader(mInputStreamReader);
            String temps = null;
            //利用buffer读取流将obj文件的内容读取出来存放在temps
            while ((temps = mBufferedReader.readLine()) != null) {
                String[] temp = temps.split(" ");
    //            Log.d("tag", "文件信息" + temps);
                if (temp[0].trim().equals("v")) {
                    mVertexArrayList.add(new ObjVertexs(Float.valueOf(temp[1]), Float.valueOf(temp[2]), Float.valueOf(temp[3])));
                }
                if (temp[0].trim().equals("vn")) {
                    mNormalArrayList.add(new ObjVertexs(Float.valueOf(temp[1]), Float.valueOf(temp[2]), Float.valueOf(temp[3])));
                }
    //            如果读取到的首元素时"f",则表示读取到的数据是面索引数据
                if (temp[0].trim().equals("f")) {
                    if (temp[1].indexOf("/") == -1) {
                        for (int i = 1; i < temp.length; i++) {
                            mIndexArrayList.add(Integer.valueOf(temp[i]));
                        }
                    } else {
                        for (int i = 1; i < temp.length; i++) {
                            mIndexArrayList.add(Integer.valueOf(temp[i].split("/")[0]));
                        }
                    }
                }
    
            }
    
            mSurfaceFloat = getSurfaceFloat(mVertexArrayList, mIndexArrayList);
            mVertexFloatBuffer = makeFloatBuffer(mSurfaceFloat);
            mNormalFloatBuffer = makeFloatBuffer(getSurfaceFloat(mNormalArrayList, mIndexNormalArrayList));
        }
    
        /**
         * 将解析出来的顶点与索引组合起来,形成一个新的float数组,用于绘制3D模型,并将其返回
         *
         * @param mObjVertexs 存放坐标点的集合
         * @param mIntegers   存放索引的集合
         * @return
         */
        public float[] getSurfaceFloat(ArrayList<ObjVertexs> mObjVertexs, ArrayList<Integer> mIntegers) {
            float[][] mFloats = new float[mIntegers.size()][3];
            float[] surfaceFloat = new float[mIntegers.size() * 3];
            for (int i = 0; i < mIntegers.size(); i++) {
                mFloats[i][0] = mObjVertexs.get(mIntegers.get(i) - 1).x;
                mFloats[i][1] = mObjVertexs.get(mIntegers.get(i) - 1).y;
                mFloats[i][2] = mObjVertexs.get(mIntegers.get(i) - 1).z;
            }
            int i = 0;
            for (float[] floats : mFloats) {
                for (float v : floats) {
                    surfaceFloat[i++] = v;
                }
            }
            return surfaceFloat;
        }
    
        /**
         * 将存放3D模型的float数组,转换为floatbuffer
         *
         * @param mFloats
         * @return
         */
        public FloatBuffer makeFloatBuffer(float[] mFloats) {
            //为float数组分配缓存空间,一个float大小为4个字节
            ByteBuffer mByteBuffer = ByteBuffer.allocateDirect(mFloats.length * 4);
            //规定缓存区的字节顺序为本机字节顺序
            mByteBuffer.order(ByteOrder.nativeOrder());
            //将bytebuffer转换为floatbuffer
            FloatBuffer loatBuffer = mByteBuffer.asFloatBuffer();
            //将float数组填充到floatbuffe中,完成转换
            loatBuffer.put(mFloats);
            //规定缓存区的起始索引
            loatBuffer.position(0);
            return loatBuffer;
        }
    }
    
    /**
     * 继承GLSurfaceView,实现Renderer接口,开始绘制
     */
    public class GlModeView extends GLSurfaceView implements GLSurfaceView.Renderer, GestureDetector.OnGestureListener {
        //obj文件解析类
        private ObjLoaderUtil mObjLoaderUtil;
        //手势工具类
        private GestureDetectorCompat mCompat;
        //模型沿x,y轴旋转的角度
        private float xrot, yrot;
    
        public GlModeView(Context context, AttributeSet attrs) throws IOException {
            super(context, attrs);
            setRenderer(this);
            mObjLoaderUtil = new ObjLoaderUtil();
            mObjLoaderUtil.load(context);
            mCompat = new GestureDetectorCompat(context, this);
        }
    
        @Override
        public void onSurfaceCreated(GL10 gl10, EGLConfig eglConfig) {
            //设置清屏色
            gl10.glClearColor(0.1f, 1f, 1f, 0);
            //用当前光线参数计算顶点颜色。否则仅仅简单将当前颜色与每个顶点关联。
            gl10.glEnable(GL10.GL_LIGHTING);
            //启用服务器端GL功能,允许(apply)引入的颜色与颜色缓冲区中的值进行逻辑运算
            gl10.glEnable(GL10.GL_BLEND);
            //这一行启用smooth shading(阴影平滑)。阴影平滑通过多边形精细的混合色彩,并对外部光进行平滑。
            gl10.glShadeModel(GL10.GL_SMOOTH);
            //当前活动纹理单元为二维纹理
            gl10.glEnable(GL10.GL_TEXTURE_2D);
            //做深度比较和更新深度缓存。值得注意的是即使深度缓冲区存在并且深度mask不是0,如果深度测试禁用的话,深度缓冲区也无法更新。
            gl10.glEnable(GL10.GL_DEPTH_TEST);
            // 法向量被计算为单位向量
            gl10.glEnable(GL10.GL_NORMALIZE);
            //启用面部剔除功能
            gl10.glEnable(GL10.GL_CULL_FACE);
            //指定哪些面不绘制
            gl10.glCullFace(GL10.GL_BACK);
            //开启定义多边形的正面和背面
            gl10.glEnable(GL10.GL_CULL_FACE);
            //改变每个顶点的散射和环境反射材质,可以使用颜色材质。
            gl10.glEnable(GL10.GL_COLOR_MATERIAL);
            // 开启抗锯齿
            gl10.glEnable(GL10.GL_POINT_SMOOTH);
            gl10.glHint(GL10.GL_POINT_SMOOTH_HINT, GL10.GL_NICEST);
            gl10.glEnable(GL10.GL_LINE_SMOOTH);
            gl10.glHint(GL10.GL_LINE_SMOOTH_HINT, GL10.GL_NICEST);
            gl10.glEnable(GL10.GL_POLYGON_OFFSET_FILL);
            gl10.glHint(GL10.GL_POLYGON_SMOOTH_HINT, GL10.GL_NICEST);
            //定义多边形的正面和背面,在一个完全由不透明的密闭surface组成的场景中,多边形的背面永远不会被看到。剔除这些不能显示出来的面可以加速渲染器渲染图像的时间。
            gl10.glFrontFace(GL10.GL_CCW);
            //指明深度缓冲区的清理值
            gl10.glClearDepthf(1.0f);
            gl10.glDepthFunc(GL10.GL_LEQUAL);
        }
    
        @Override
        public void onSurfaceChanged(GL10 gl10, int i, int i1) {
            //选择投影矩阵
            gl10.glMatrixMode(GL10.GL_PROJECTION);
            gl10.glViewport(0, 0, i, i1);
            gl10.glLoadIdentity();
            float x = (float) i / i1;
            gl10.glFrustumf(-x, x, -1, 1, 5, 200);
            GLU.gluLookAt(gl10, 0, 0, 50, 0, 0, 0, 0, 1, 0);
            //选择模型观察矩阵
            gl10.glMatrixMode(GL10.GL_MODELVIEW);
            gl10.glLoadIdentity();
        }
    
        @Override
        public void onDrawFrame(GL10 gl10) {
            //清除屏幕和深度缓存。
            gl10.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
            gl10.glLoadIdentity();
            //设置顶点坐标buffer
            gl10.glVertexPointer(3, GL10.GL_FLOAT, 0,
                    mObjLoaderUtil.mVertexFloatBuffer);
            //启用顶点数组
            gl10.glEnableClientState(GL10.GL_VERTEX_ARRAY);
            //沿X轴旋转
            gl10.glRotatef(xrot, 1, 0, 0);
            //沿Y轴旋转
            gl10.glRotatef(yrot, 0, 1, 0);
    
            gl10.glColor4f(1f, 0f, 0f, 0);
            //模型如果四边形化,i就是加4,模型如果三角化,i就是加3
            for (int i = 0; i <= (mObjLoaderUtil.mSurfaceFloat.length / 3 - 4); i += 4) {
                gl10.glDrawArrays(GL10.GL_TRIANGLE_FAN, i, 4);
            }
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    getParent().requestDisallowInterceptTouchEvent(true);
                    break;
                case MotionEvent.ACTION_UP:
                    getParent().requestDisallowInterceptTouchEvent(false);
                    break;
            }
            return mCompat.onTouchEvent(event);
        }
    
        @Override
        public boolean onDown(MotionEvent motionEvent) {
            return true;
        }
    
        @Override
        public void onShowPress(MotionEvent motionEvent) {
    
        }
    
        @Override
        public boolean onSingleTapUp(MotionEvent motionEvent) {
            return false;
        }
    
        @Override
        public boolean onScroll(MotionEvent motionEvent, MotionEvent motionEvent1, float v, float v1) {
            xrot -= v;
            yrot -= v1;
            return true;
        }
    
        @Override
        public void onLongPress(MotionEvent motionEvent) {
    
        }
    
        @Override
        public boolean onFling(MotionEvent motionEvent, MotionEvent motionEvent1, float v, float v1) {
            return false;
        }
    }
    

    第二种 glDrawElements绘制3D模型,使用这种方式绘制3D模型,需要将顶点坐标数据准备好,及顶点索引数组准备好。就ok。

    这两个数组也是直接从obj文件中解析。相对上面的方式而言,就简单一点。

    /**
     * 解析obj文件
     */
    public class ObjUtils {
        //存放顶点坐标
        private ArrayList<Point> mVertexArrayList;
        public float[] mVertexFloats;
        public FloatBuffer mVertexFloatBuffer;
        //存放顶点索引
        private ArrayList<Short> mIndexArrayList;
        public short[] mIndexShorts;
        public ShortBuffer mIndexShortBuffer;
        //存放读取的obj文件的信息
        private String temps;
    
    
        public ObjUtils() {
            mVertexArrayList = new ArrayList<>();
            mIndexArrayList = new ArrayList<>();
         }
    
        /**
         * 加载obj文件数据
         */
        public void loadObj(Context context, String path) throws IOException {
            //读取city.obj文件
            InputStream inputStream = context.getClass().getClassLoader().getResourceAsStream(path);
            InputStreamReader mInputStreamReader = new InputStreamReader(inputStream);
            BufferedReader mReader = new BufferedReader(mInputStreamReader);
            //如果读取的文本内容不为空,则一直读取,否则停止
            while ((temps = mReader.readLine()) != null) {
                String[] temp = temps.split(" ");
                //以空格为分割符,将读取的一行temps分裂为数组
                //如果读取到的首元素时"v",则表示读取到的数据是顶点坐标数据
                if (temp[0].trim().equals("v")) {
                    mVertexArrayList.add(new Point(Float.valueOf(temp[1]), Float.valueOf(temp[2]), Float.valueOf(temp[3])));
                }
    //            如果读取到的首元素时"f",则表示读取到的数据是面索引数据
                if (temp[0].trim().equals("f")) {
                    if (temp[1].indexOf("/") == -1) {
                        for (int i = 1; i < temp.length; i++) {
                            int t = Integer.valueOf(temp[i]);
                            mIndexArrayList.add((short) t);
                        }
                    } else {
                        for (int i = 1; i < temp.length; i++) {
                            int t = Integer.valueOf(temp[i].split("/")[0]) - 1;
                            mIndexArrayList.add((short) t);
                        }
                    }
                }
    
            }
            mReader.close();
            mVertexFloats = getVertex(mVertexArrayList);
            mIndexShorts = getIndex(mIndexArrayList);
            mVertexFloatBuffer = makeFloatBuffer(mVertexFloats);
            mIndexShortBuffer = getBufferFromArray(mIndexShorts);
             }
    
         /**
         * 获取顶点数组
         */
        private float[] getVertex(ArrayList<Point> pointArrayList) {
            float[][] mFloats = new float[pointArrayList.size()][3];
            float[] surfaceFloat = new float[pointArrayList.size() * 3];
            for (int i = 0; i < pointArrayList.size(); i++) {
                mFloats[i][0] = pointArrayList.get(i).x;
                mFloats[i][1] = pointArrayList.get(i).y;
                mFloats[i][2] = pointArrayList.get(i).z;
            }
            int i = 0;
            for (float[] floats : mFloats) {
                for (float v : floats) {
                    surfaceFloat[i++] = v;
                }
            }
            return surfaceFloat;
        }
    
        /**
         * 获取索引数组
         */
        private short[] getIndex(ArrayList<Short> integerArrayList) {
            short[] mFloats = new short[integerArrayList.size()];
            for (int i = 0; i < integerArrayList.size(); i++) {
                mFloats[i] = integerArrayList.get(i);
            }
            return mFloats;
        }
    
        /**
         * 将float数组转换为buffer
         */
        private FloatBuffer makeFloatBuffer(float[] floats) {
            ByteBuffer mByteBuffer = ByteBuffer.allocateDirect(floats.length * 4);
            mByteBuffer.order(ByteOrder.nativeOrder());
            FloatBuffer floatBuffer = mByteBuffer.asFloatBuffer();
            floatBuffer.put(floats);
            floatBuffer.position(0);
            return floatBuffer;
        }
    
        private static ShortBuffer getBufferFromArray(short[] array) {
            ByteBuffer buffer = ByteBuffer.allocateDirect(array.length * 2);
            buffer.order(ByteOrder.nativeOrder());
            ShortBuffer shortBuffer = buffer.asShortBuffer();
            shortBuffer.put(array);
            shortBuffer.position(0);
            return shortBuffer;
        }
    }
    
    public class ModeView extends GLSurfaceView implements GLSurfaceView.Renderer, GestureDetector.OnGestureListener {
        private ObjUtils mObjUtils;
        private GestureDetectorCompat mCompat;
        private float xrot, yrot;
        private FloatBuffer colorBuffer;
    
        public ModeView(Context context, AttributeSet attrs) throws IOException {
            super(context, attrs);
            mObjUtils = new ObjUtils();
            setRenderer(this);
            mObjUtils.loadObj(context, "assets/mode.obj");
            mCompat = new GestureDetectorCompat(context, this);
        }
    
        @Override
        public void onSurfaceCreated(GL10 gl10, EGLConfig eglConfig) {
            //设置清屏色
            gl10.glClearColor(1f, 1f, 1f, 0);
            //用当前光线参数计算顶点颜色。否则仅仅简单将当前颜色与每个顶点关联。
            gl10.glEnable(GL10.GL_LIGHTING);
            //启用服务器端GL功能,允许(apply)引入的颜色与颜色缓冲区中的值进行逻辑运算
            gl10.glEnable(GL10.GL_BLEND);
            //这一行启用smooth shading(阴影平滑)。阴影平滑通过多边形精细的混合色彩,并对外部光进行平滑。
            gl10.glShadeModel(GL10.GL_SMOOTH);
            //当前活动纹理单元为二维纹理
            gl10.glEnable(GL10.GL_TEXTURE_2D);
            //做深度比较和更新深度缓存。值得注意的是即使深度缓冲区存在并且深度mask不是0,如果深度测试禁用的话,深度缓冲区也无法更新。
            gl10.glEnable(GL10.GL_DEPTH_TEST);
            // 法向量被计算为单位向量
            gl10.glEnable(GL10.GL_NORMALIZE);
            //启用面部剔除功能
            gl10.glEnable(GL10.GL_CULL_FACE);
            //指定哪些面不绘制
            gl10.glCullFace(GL10.GL_BACK);
            //开启定义多边形的正面和背面
            gl10.glEnable(GL10.GL_CULL_FACE);
            //改变每个顶点的散射和环境反射材质,可以使用颜色材质。
            gl10.glEnable(GL10.GL_COLOR_MATERIAL);
            // 开启抗锯齿
            gl10.glEnable(GL10.GL_POINT_SMOOTH);
            gl10.glHint(GL10.GL_POINT_SMOOTH_HINT, GL10.GL_NICEST);
            gl10.glEnable(GL10.GL_LINE_SMOOTH);
            gl10.glHint(GL10.GL_LINE_SMOOTH_HINT, GL10.GL_NICEST);
            gl10.glEnable(GL10.GL_POLYGON_OFFSET_FILL);
            gl10.glHint(GL10.GL_POLYGON_SMOOTH_HINT, GL10.GL_NICEST);
            //定义多边形的正面和背面,在一个完全由不透明的密闭surface组成的场景中,多边形的背面永远不会被看到。剔除这些不能显示出来的面可以加速渲染器渲染图像的时间。
            gl10.glFrontFace(GL10.GL_CCW);
            //指明深度缓冲区的清理值
            gl10.glClearDepthf(1.0f);
            gl10.glDepthFunc(GL10.GL_LEQUAL);
        }
    
        @Override
        public void onSurfaceChanged(GL10 gl10, int i, int i1) {
            //选择投影矩阵
            gl10.glMatrixMode(GL10.GL_PROJECTION);
            gl10.glViewport(0, 0, i, i1);
            gl10.glLoadIdentity();
            float x = (float) i / i1;
            gl10.glFrustumf(-x, x, -1, 1, 4, 200);
            GLU.gluLookAt(gl10, 0, 0, 60, 0, 0, 0, 0, 1, 0);
            //选择模型观察矩阵
            gl10.glMatrixMode(GL10.GL_MODELVIEW);
            gl10.glLoadIdentity();
        }
    
        @Override
        public void onDrawFrame(GL10 gl10) {
            //启用顶点数组
            gl10.glEnableClientState(GL10.GL_VERTEX_ARRAY);
            gl10.glEnableClientState(GL10.GL_COLOR_ARRAY);
            //清除屏幕和深度缓存。
            gl10.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
            gl10.glLoadIdentity();
            //设置顶点坐标buffer
            gl10.glVertexPointer(3, GL10.GL_FLOAT, 0,
                    mObjUtils.mVertexFloatBuffer);
            gl10.glTranslatex(0, -20, 0);
            gl10.glRotatef(xrot, 1, 0, 0);
            gl10.glRotatef(yrot, 0, 1, 0);
            gl10.glPushMatrix();
            gl10.glDrawElements(GL10.GL_TRIANGLES, mObjUtils.mIndexShorts.length,
                    GL10.GL_UNSIGNED_SHORT, mObjUtils.mIndexShortBuffer);
            gl10.glDisableClientState(GL10.GL_COLOR_ARRAY);
            //关闭点坐标管道
            gl10.glDisableClientState(GL10.GL_VERTEX_ARRAY);
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    getParent().requestDisallowInterceptTouchEvent(true);
                    break;
                case MotionEvent.ACTION_UP:
                    getParent().requestDisallowInterceptTouchEvent(false);
                    break;
            }
            return mCompat.onTouchEvent(event);
        }
    
        @Override
        public boolean onDown(MotionEvent motionEvent) {
            return true;
        }
    
        @Override
        public void onShowPress(MotionEvent motionEvent) {
    
        }
    
        @Override
        public boolean onSingleTapUp(MotionEvent motionEvent) {
            return false;
        }
    
        @Override
        public boolean onScroll(MotionEvent motionEvent, MotionEvent motionEvent1, float v, float v1) {
            xrot -= v;
            yrot -= v1;
            return true;
        }
    
        @Override
        public void onLongPress(MotionEvent motionEvent) {
    
        }
    
        @Override
        public boolean onFling(MotionEvent motionEvent, MotionEvent motionEvent1, float v, float v1) {
            return false;
        }
    }
    

    这两种方式绘制3D模型的主要区别就是在 onDrawFrame这个方法中。

    一:

    for (int i = 0; i <= (mObjLoaderUtil.mSurfaceFloat.length / 3 - 4); i += 4) { gl10.glDrawArrays(GL10.GL_TRIANGLE_FAN, i, 4); }

    二:

    gl10.glDrawElements(GL10.GL_TRIANGLES, mObjUtils.mIndexShorts.length, GL10.GL_UNSIGNED_SHORT, mObjUtils.mIndexShortBuffer);

    在使用OpenGl绘制3D模型前,一定要把这两种方法研究下。

    欢迎各方大佬,指点评论!!!!!!

     

    展开全文
  • android平台使用libgdx库 解析obj文件
  • parse-obj:解析.OBJ文件

    2021-05-10 15:05:12
    .OBJ网格文件的简单解析器。 例子 var fs = require ( "fs" ) var parseOBJ = require ( "parse-obj" ) parseOBJ ( fs . createReadStream ( "mesh.obj" ) , function ( err , result ) { if ( err ) { throw new ...
  • Java将数据存储在obj文件中并读取出来,数据存储在label.obj,通过读取和生成文件流,得到对象输出流的方法来存储和读取文件对象,读取对象后关闭文件流并输出提示信息。。
  • MFC实现3D OBJ文件读取

    2021-03-27 08:00:20
    程序基于VC6.0利用MFC和OpenGL库实现了3d模型中标准格式OBJ文件的读取,是一个很好的二次开发源代码
  • opengl解析obj模型文件

    千次阅读 2017-07-26 15:34:37
    obj模型文件格式 # This file uses centimeters as units for non-parametric coordinates. mtllib Cube.mtl g default v -0.500000 -0.500000 0.500000 #顶点坐标 v 0.500000 -0.500000 0.500000 v -0.500000 0....

    obj模型文件格式

    # This file uses centimeters as units for non-parametric coordinates.
    
    mtllib Cube.mtl
    g default
    v -0.500000 -0.500000 0.500000  #顶点坐标
    v 0.500000 -0.500000 0.500000
    v -0.500000 0.500000 0.500000
    v 0.500000 0.500000 0.500000
    v -0.500000 0.500000 -0.500000
    v 0.500000 0.500000 -0.500000
    v -0.500000 -0.500000 -0.500000
    v 0.500000 -0.500000 -0.500000
    vt 0.375000 0.000000            #纹理坐标
    vt 0.625000 0.000000
    vt 0.375000 0.250000
    vt 0.625000 0.250000
    vt 0.375000 0.500000
    vt 0.625000 0.500000
    vt 0.375000 0.750000
    vt 0.625000 0.750000
    vt 0.375000 1.000000
    vt 0.625000 1.000000
    vt 0.875000 0.000000
    vt 0.875000 0.250000
    vt 0.125000 0.000000
    vt 0.125000 0.250000
    vn 0.000000 0.000000 1.000000     #法线坐标 
    vn 0.000000 0.000000 1.000000
    vn 0.000000 0.000000 1.000000
    vn 0.000000 0.000000 1.000000
    vn 0.000000 1.000000 0.000000
    vn 0.000000 1.000000 0.000000
    vn 0.000000 1.000000 0.000000
    vn 0.000000 1.000000 0.000000
    vn 0.000000 0.000000 -1.000000
    vn 0.000000 0.000000 -1.000000
    vn 0.000000 0.000000 -1.000000
    vn 0.000000 0.000000 -1.000000
    vn 0.000000 -1.000000 0.000000
    vn 0.000000 -1.000000 0.000000
    vn 0.000000 -1.000000 0.000000
    vn 0.000000 -1.000000 0.000000
    vn 1.000000 0.000000 0.000000
    vn 1.000000 0.000000 0.000000
    vn 1.000000 0.000000 0.000000
    vn 1.000000 0.000000 0.000000
    vn -1.000000 0.000000 0.000000
    vn -1.000000 0.000000 0.000000
    vn -1.000000 0.000000 0.000000
    vn -1.000000 0.000000 0.000000
    s 1
    g Cube
    usemtl initialShadingGroup
    f 1/1/1 2/2/2 3/3/3   #f 顶点索引/uv点索引/法线索引
    f 3/3/3 2/2/2 4/4/4
    s 2
    f 3/3/5 4/4/6 5/5/7
    f 5/5/7 4/4/6 6/6/8
    s 3
    f 5/5/9 6/6/10 7/7/11
    f 7/7/11 6/6/10 8/8/12
    s 4
    f 7/7/13 8/8/14 1/9/15
    f 1/9/15 8/8/14 2/10/16
    s 5
    f 2/2/17 8/11/18 4/4/19
    f 4/4/19 8/11/18 6/12/20
    s 6
    f 7/13/21 1/1/22 5/14/23
    f 5/14/23 1/1/22 3/3/24
    
    模型解析头文件

    #pragma once
    #include "glew.h"
    
    struct VertexData
    {
    	float position[3];
    	float texcoord[2];
    	float normal[3];
    };
    
    class ObjModel
    {
    public:
    	GLuint mVBO, mIBO;
    	unsigned int mIndexCount;
    public:
    	void Init(const char*modelFilePath);
    	void Bind(GLint posLoc,GLint texcoordLoc,GLint normalLoc);
    	void Draw();
    };

    模型解析实现类

    #include "ObjModel.h"
    #include "utils.h"
    #include <stdio.h>
    #include <sstream>
    #include <string>
    #include <vector>
    
    void ObjModel::Init(const char*modelFilePath)
    {
    	struct VertexInfo
    	{
    		float v[3];
    		VertexInfo()
    		{
    			memset(v,0,sizeof(float)*3);
    		}
    	};
    
    	struct VertexDefine
    	{
    		int positionIndex;
    		int texcoordIndex;
    		int normalIndex;
    	};
    
    
    	/*
    	
    	C++中,iostream类重载了运算符>>和<<
    
    	>>用于cin对象,表示从标准输入,输入数据到变量中
    	<<用于cout对象,表示将变量数据,输出到标准输出中	
    	*/
    
    	std::vector<VertexInfo> positions;//顶点坐标
    	std::vector<VertexInfo> texcoords;//纹理坐标
    	std::vector<VertexInfo> normals;//法线
    
    	std::vector<VertexDefine> vertices;
    	std::vector<unsigned int> faces;
    
    	//加载模型文件
    	char* fileContent = LoadFileContent(modelFilePath);
    	//解析模型文件
    	//stringstream是字符串流 ,它将流与存储在内存中的string对象绑定起来
    	std::stringstream ssFileContent(fileContent);
    	char szOneLine[256];
    	std::string temp;
    	while (!ssFileContent.eof())
    	{
    		memset(szOneLine,0,256);
    		ssFileContent.getline(szOneLine,256);
    		if (strlen(szOneLine)>0)
    		{
    			std::stringstream ssOneLine(szOneLine);
    			if (szOneLine[0]=='v')//解析顶点信息
    			{
    				
    				  if (szOneLine[1] == 't')//解析纹理坐标
    					{
    					  VertexInfo vi;
    					  ssOneLine >> temp;//vt
    					  ssOneLine >> vi.v[0];
    					  ssOneLine >> vi.v[1];
    					  texcoords.push_back(vi);
    					}
    					else if (szOneLine[1] == 'n')//解析法线坐标
    					{
    						VertexInfo vi;
    						ssOneLine >> temp;//vn
    						ssOneLine >> vi.v[0];
    						ssOneLine >> vi.v[1];
    						ssOneLine >> vi.v[2];
    						normals.push_back(vi);
    					}
    					else //解析顶点信息
    					{
    						VertexInfo vi;
    						ssOneLine >> temp;//v
    						ssOneLine >> vi.v[0];
    						ssOneLine >> vi.v[1];
    						ssOneLine >> vi.v[2];
    						positions.push_back(vi);
    					}
    			}
    			else if (szOneLine[0]=='f') //解析面信息
    			{
    				ssOneLine >> temp;//f
    				std::string vertexStr;
    				for (int i = 0; i < 3; ++i)
    				{
    					ssOneLine >> vertexStr;
    					size_t pos = vertexStr.find_first_of('/');
    					std::string positionIndexStr = vertexStr.substr(0, pos);
    					size_t pos2 = vertexStr.find_first_of('/', pos + 1);
    					std::string texcoordIndexStr = vertexStr.substr(pos + 1, pos2 - pos - 1);
    					std::string normalIndexStr = vertexStr.substr(pos2 + 1, vertexStr.length() - pos2 - 1);
    					VertexDefine vd;
    					vd.positionIndex = atoi(positionIndexStr.c_str()) - 1;
    					vd.texcoordIndex = atoi(texcoordIndexStr.c_str()) - 1;
    					vd.normalIndex = atoi(normalIndexStr.c_str()) - 1;
    					//trim the same vertice
    					int nCurrentVertexIndex = -1;
    					size_t nCurrentVerticeCount = vertices.size();
    					for (int j = 0; j<nCurrentVerticeCount; ++j)
    					{
    						if (vertices[j].positionIndex == vd.positionIndex&&
    							vertices[j].texcoordIndex == vd.texcoordIndex&&
    							vertices[j].normalIndex == vd.normalIndex)
    						{
    							nCurrentVertexIndex = j;
    							break;
    						}
    					}
    					if (nCurrentVertexIndex == -1)
    					{
    						nCurrentVertexIndex = (int)vertices.size();
    						vertices.push_back(vd);
    					}
    					faces.push_back(nCurrentVertexIndex);
    				}
    			}
    		
    		}
    	}
    	int vertexCount = (int)vertices.size();
    	VertexData*vertexes = new VertexData[vertexCount];
    	for (int  i = 0; i < vertexCount; ++i)
    	{
    		memcpy(vertexes[i].position,positions[vertices[i].positionIndex].v,sizeof(float)*3);
    		memcpy(vertexes[i].texcoord, texcoords[vertices[i].texcoordIndex].v, sizeof(float) * 2);
    		memcpy(vertexes[i].normal, normals[vertices[i].normalIndex].v, sizeof(float) * 3);
    	}
    
    	//创建VBO
    	glGenBuffers(1,&mVBO);//创建一个buffer
    	glBindBuffer(GL_ARRAY_BUFFER,mVBO);//指定BUFFER类型
    	//将顶点数据上传至GPU
    	glBufferData(GL_ARRAY_BUFFER,sizeof(VertexData)*vertexCount,vertexes,GL_STATIC_DRAW);
    	glBindBuffer(GL_ARRAY_BUFFER,0);
    	delete vertexes;
    
    	//创建ibo
    	mIndexCount = (int)faces.size();
    	unsigned int *indexes = new unsigned int[mIndexCount];
    	for (int  i = 0; i < mIndexCount; ++i)
    	{
    		indexes[i] = faces[i];
    	}
    	glGenBuffers(1,&mIBO);
    	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,mIBO);
    	glBufferData(GL_ELEMENT_ARRAY_BUFFER,sizeof(unsigned int)*mIndexCount,indexes,GL_STATIC_DRAW);
    	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,0);
    	delete indexes;
    
    	delete fileContent;
    }
    
    void ObjModel::Bind(GLint posLoc, GLint texcoordLoc, GLint normalLoc)
    {
    	glBindBuffer(GL_ARRAY_BUFFER,mVBO);
    	glEnableVertexAttribArray(posLoc);//启用顶点属性
    	/*
    	参数含义:
    	第一个参数:GPU中顶点属性的引用
    	第二个参数:因为顶点是一个向量,表示向量的维度
    	第三个参数:每个维度数据的类型
    	第四个参数:是否归一化
    	第五个参数:每个顶点间的偏移量
    	第六个参数:顶点数据的起始地址,因为用了VBO,所以起始地址为0
    	实际起始地址跟顶点属性在结构体VertexData中的位置有关
    	*/
    	glVertexAttribPointer(posLoc,3,GL_FLOAT,GL_FALSE,sizeof(VertexData),0);
    
    	glEnableVertexAttribArray(texcoordLoc);//启用纹理属性
    	glVertexAttribPointer(texcoordLoc,2,GL_FLOAT,GL_FALSE,sizeof(VertexData),
    		(void*)(sizeof(float)*3));//指定纹理坐标的起始地址
    
    	glEnableVertexAttribArray(normalLoc);//启用法线属性
    	glVertexAttribPointer(normalLoc, 3, GL_FLOAT, GL_FALSE, sizeof(VertexData), 
    		(void*)(sizeof(float) * 5));//指定法线坐标的起始地址
    	glBindBuffer(GL_ARRAY_BUFFER,0);
    }
    
    void ObjModel::Draw()
    {
    	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,mIBO);
    	glDrawElements(GL_TRIANGLES,mIndexCount,GL_UNSIGNED_INT,0);
    	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,0);
    }

    解析结果




    展开全文
  • 介绍 obj 文件以 txt 文本形式打开后,标签 v ,f, vn, vt的概念

    obj文件:obj文件用txt打开并且了解v,f,vn,vt的含义

    1. 来源:《Computer Graphics Programming in OpenGL Using C++ 》by V Scott Gordon John L Clevenger
    2. 内容:介绍obj文件以txt文本形式打开后的标签v,f,vn,vt,书P148页,PDF167/403
    3. 备注:因为很重要,所以想特意单独写一篇。完整内容可以看:C++/OpenGL 入门(18):读取obj文件并贴图

    笔记

    obj文件的格式介绍

    加载外部绘图的模型:复杂的3D模型一般是由建模软件产生的。3D模型的格式 .obj, .3ds, .ply, .mesh, 其中最简单是的obj文件。
    Obj文件比较简单,我们程序读取的时候也较为容易。Obj文件中,确定了点几何信息,纹理坐标,法向量和其他信息,当然也有一些限制,比如obj文件无法确定模型的运动。
    Obj文件中,开头是一系列的特征标签,提示存储的是什么样的数据。一些常用的标签有:
    V——顶点几何信息
    Vt——纹理坐标
    Vn——顶点法向量
    F——face 面(三角形的三个顶点)
    其他的标签可能存储的是对象的名字,所用的材料,曲率curves, 阴影 shadows和其他的细节。
    我们将讨论限制在以上四个标签,这足够展示一系列复杂模型。
    假设用Blender软件建立一个四面体,然后选择.obj格式导出文件包括纹理坐标和顶点法向量
    在obj文件用txt文件打开,头几行**“#“**是注释,编译器忽略注释的这几行。 “o“ 表示obj文件的名字,编译器也需要忽略
    “s“开头的语句,说明这些面不该被平滑,编译器也会忽略该行
    “v“开头的语句,表示四面体的5个点的相对于原点(0,0,0)的X,Y,Z坐标,原点在四面体的中心 以下是书中的例子给的obj文件
    在这里插入图片描述
    “vt“开头的红色行代表的是不同的纹理坐标,纹理坐标的数量比顶点数量多,是因为一个顶点可能参与多个三角形,在这些不同三角形中有不同的纹理坐标。
    “vn“绿色行,表示不同的法向量,法向量的行数通常比顶点的行数多,是因为有一些顶点同时在不同三角形中。
    “f“紫色行,表示三角形,每个面有三个元素,是用/分隔,比如下图:
    在这里插入图片描述
    暗示说,第2.5.3的顶点组成一个三角形
    每一组的第二个数 : 7 8 9 是 vt中的,也就是纹理坐标组成的三角形
    每一组的第三个数 3 3 3 是vn 中的,也就是第三行,这三个点组成的面有一样的法向量

    如果obj文件不包括纹理坐标和法向量,格式为: f 2 5 3
    如果obj文件有纹理坐标,没有法向量,格式为: f 2/7 5/8 3/9
    如果obj文件没有纹理坐标,有法向量,格式为: f 2//3 5//3 3//3

    可以根据标签 v,vt,vn,f自己写一个obj文件

    Obj文件的限制
    ①只支持全部表面都是三角形的模型,也就是说顶点位置,纹理坐标和法向量必须全部说明,并且以这样的形式: f #/#/# #/#/# #/#/#
    ②材料的标签被忽略后,纹理贴图的做法必须用第五章的方法完成
    ③只有全部是三角形网格组成的obj模型才能支持,其余复杂网格模型的obj文件不支持
    ④每一行的元素由空格分隔开

    下载 Blender 软件

    想要得到上述obj文件的格式,可将现成的obj文件用Blender 软件打开,重新生成一个obj的文件,这样就有比较整齐的格式了。
    Blender 软件下载网址:Blender下载网址
    选择下图的 Aliyun,即可下载。
    在这里插入图片描述
    软件界面中有新手教程自带正方体,右上角“文件”->“导出”->“obj”-
    在这里插入图片描述
    在这里插入图片描述
    在obj导出弹窗,选择存储位置和存储名称,选择包括在内的内容,包括顶点,法线和三角面
    在这里插入图片描述
    然后在相应位置会生成obj文件,可以右键选择用记事本打开
    在这里插入图片描述

    展开全文
  • obj文件解析

    2021-05-21 15:47:25
    obj文件解析 1 前言 OBJ文件是Alias|Wavefront公司为它的一套基于工作站的3D建模和动画软件"Advanced Visualizer"开发的一种标准3D模型文件格式,目前市面上大多数主流的软件平台都是支持OBJ格式,OBJ模型于是也就...
  • 如果你能够从你的原始文件夹中读取obj文件LoaderOBJ objParser = new LoaderOBJ(mContext.getResources(),mTextureManager,R.raw.camero_obj);而不是从您的SD卡,您必须在文件中进行以下更改:在你的obj文件中,必须在...
  • OBJ文件格式解析

    2020-11-27 19:45:49
    OBJ文件格式解析 OBJ文件作为一种标准的3D模型文件格式,例如CAD。文件其中主要存储了模型 的顶点、线和面等元素,还包括法向量和纹理贴图等。 主要元素 v 几何顶点 vt 贴图坐标 vn 顶点法向量(或者说三角形mesh的...
  • Unity中解析加载Obj文件(C#)

    千次阅读 2018-05-17 13:53:14
    Obj文件格式: mtllib master.mtl v -0.500000 -0.500000 0.500000 v 0.500000 -0.500000 0.500000 v -0.500000 0.500000 0.500000 v 0.500000 0.500000 0.500000 v -0.500000 0.500000 -0.500000 v 0.50000....
  • 三维模型obj文件解析

    千次阅读 2020-09-30 11:36:56
    3D模型obj文件解析,构造一个obj立方体并进行纹理映射实例讲解
  • wavefront_obj 这是wavefront .obj文件格式的解析器。 有行号报告的信息错误消息。 不支持完整文件格式,尽管非常欢迎添加支持该格式未实现部分的补丁。 这是尽力尝试解析由Blender导出的.obj文件的子集。
  • CoffParser 解析COFF .Obj文件的小型python脚本
  • 2、进行格式转换,生成obj文件 这个只是示意程序,只生成模型的轮廓,不包含材质 #! /usr/bin/env python # coding: utf-8 import dxfgrabber def convertPolylines(polylines, vertexes, faces, index): ...
  • obj格式解析

    千次阅读 2020-10-15 19:36:24
    最近处理一些网格渲染的时候,需要解析Obj文件,从Free3D上随便找了个免费的人体obj模型解析测试一波 国际惯例,参考博客: 本文所使用的从Free3D下载的模型 .obj文件格式与.mtl文件格式 详解3D中的obj文件格式 3D中...
  • .OBJ-解析器用 Python 编写的 OBJ 文件解析器。 将 OBJ 文件转换为矢量和纹理坐标,以使用 glDrawArrays 在 OpenGL 中绘制。示例 .obj 文件格式解析器当前仅检查以“v”(顶点)、“vt”(顶点纹理)和“f”(面)...
  • OpenGL ES 3. OBJ文件渲染

    2021-01-03 18:02:42
    由于OBJ文本文件是按照一定的规则储存的(详见上一节内容介绍),所以,我们首先介绍OBJ文件的加载解析,加载后用于渲染物体的 LoadedObjectVertexNormalTexture 类。介绍加载顶点坐标、三角形面、纹理坐标等信息。 ...
  • obj文件解析器开源,可重用的Wavefront 3D对象文件(.OBJ)解析器,以javascript编写,并作为NPM软件包自由分发。特征仅支持(.OBJ)文件,请参阅mtl-file-parser以获取mtl文件不区分大小写,灵活的解析器简单的JS...
  • OBJ文件解析代码

    2012-03-25 13:52:31
    OBJ文件解析代码 使用opengl c++
  • obj文件内容详解

    2013-01-08 16:14:49
    帮助你了解流行3D文件格式obj的内部结构和内容
  • Unity功能实现——解析OBJ模型文件

    千次阅读 2018-12-25 11:09:58
    Unity功能实现——解析OBJ模型文件 1、OBJFile类 用于存储读取obj文件的g、v、f g对应gameobject.name v对应mesh.vertices f对应mesh.triangles 注意unity对于mesh点索引的起始点是0不是1 using System.Collections....
  • 三维模型格式obj文件解析源码,vs2013编译环境下编写的
  • cocos3d下实现obj文件解析,并使用解析出来的数据调用opengl画出实际模型。其中还涉及到图片uv卷动,多重贴图。
  • OBJ文件解析之OBJ的整体结构

    万次阅读 2016-09-09 23:13:37
    在windows下c语言编程的过程中,你有没有好奇过obj文件的格式呢?以及后续的 静态库,动态链接库.lib文件的格式,在这里我将带领大家 做一下 力所能及的简单分析, 在此过程中做好以下准备工作: 1.用VS创建一个...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 131,404
精华内容 52,561
关键字:

解析obj文件