精华内容
下载资源
问答
  • obj文件格式解读

    2019-12-25 21:43:58
    学习了很长一段时间的建模,obj文件一直都在使用,...maya导出的obj格式主有4种数据, v顶点——代表该点的xyz坐标 vt纹理坐标——对应贴图上的坐标位置 vn顶点法向量——向量xyz方向 f 面——格式为 :f v/vt...

    学习了很长一段时间的建模,obj文件一直都在使用,但是却很少去研究过,只是知道这是软件之间的通用格式,直到最近因为刚好要在python中加载obj文件,才发现原来obj文件是如此的有规律

    随便用记事本打开一个obj文件:
    在这里插入图片描述
    maya导出的obj格式主有4种数据,
    v顶点——代表该点的xyz坐标
    vt纹理坐标——对应贴图上的坐标位置
    vn顶点法向量——向量xyz方向
    f 面——格式为 :f v/vt/vn v/vt/vn v/vt/vn 代表3个顶点组成一个三角面

    展开全文
  • .obj 和 .mtl文件格式

    千次阅读 2017-08-08 12:12:47
    .obj 和 .mtl文件格式obj文件是3D模型文件格式。由Alias|Wavefront公司为3D建模和动画软件”Advanced Visualizer”开发的一种标准,适合用于3D软件模型之间的互导,也可以通过Maya读写。 + 只支持模型三角面数据和...

    .obj 和 .mtl文件格式

    obj文件是3D模型文件格式。由Alias|Wavefront公司为3D建模和动画软件"Advanced Visualizer"开发的一种标准,适合用于3D软件模型之间的互导,也可以通过Maya读写。

    • 只支持模型三角面数据和材质信息,无动画功能支持;
    • 其中几何信息由.obj文件提供,材质信息由.mtl文件定义;
    • 文件以行为单位表示一条数据,可以根据行开头的字符判断后续的内容;
    • 其中 # 字符表示注释行

    注:
    .obj 与.mtl解析与加载可参考:opengl es 2.0中加载.obj 与 .mtl

    OBJ格式

    
    # obj对应的材质文件
    # mtllib testvt.mtl
    # 组名称
    g default
    # o 对象名称(Object name)
    o testvt.obj
    # 顶点
    v -0.5 -0.5 0.1
    v -0.5 -0.5 -0.1
    v 0 0.5 0.1
    v 0 0.5 -0.1
    v 0.5 -0.5 0.1
    v 0.5 -0.5 -0.1
    # 纹理坐标
    vt 0 1
    vt 1 1
    vt 0.5 0
    # 顶点法线
    vn 0 0 1
    vn 0 0 -1
    # 当前图元所用材质
    usemtl Default
    # s Smooth shading across polygons is enabled by smoothing groups.
    # Smooth shading can be disabled as well.
    s off
    # v1/vt1/vn1 v2/vt2/vn2 v3/vt3/vn3(索引起始于1)    
    f 1/1/1 5/2/1 3/3/1
    f 6/2/2 2/1/2 4/3/2
    
    

    MTL格式

    # 定义一个名为 'xxx'的材质
    newmtl xxx
    # 材质的环境光(ambient color)
    Ka 0 0 0
    # 散射光(diffuse color)用Kd
    Kd 0.784314 0.784314 0.784314
    # 镜面光(specular color)用Ks
    Ks 0 0 0
    # 折射值 可在0.001到10之间进行取值。若取值为1.0,光在通过物体的时候不发生弯曲。玻璃的折射率为1.5。
    Ni 1
    # 反射指数 定义了反射高光度。该值越高则高光越密集,一般取值范围在0~1000。
    Ns 400
    # 滤光透射率
    Tf 1 1 1
    # 渐隐指数描述 参数factor表示物体融入背景的数量,取值范围为0.0~1.0,取值为1.0表示完全不透明,取值为0.0时表示完全透明。
    d 1
    # 为漫反射指定颜色纹理文件
    map_Kd test_vt.bmp
    

    参考:
    https://baike.baidu.com/item/OBJ文件/3574019
    https://en.wikipedia.org/wiki/Wavefront_.obj_file

    ========== THE END ==========

    您对“我的文章”有任何疑问,可用微信扫描以下“二维码”向我提问!

    在这里插入图片描述

    如果文章对您有帮助,请扫描以下二维码支持我!

    在这里插入图片描述

    展开全文
  • 一篇详解OBJ格式以及实现读入的文章,可以参考参考(介绍了当今流行的三维作图工具Maya,阐述了Maya 生成的OBJ 三维格式,如何将OBJ 格式文件在OpenGL(Open Graphic Library)中进行输入和处理。重点介绍了OBJ 文件的...
  • .obj 和 .mtl格式详解

    2019-11-28 10:35:05
    obj文件是3D模型文件格式。由Alias|Wavefront公司为3D建模和动画软件"Advanced Visualizer"开发的一种标准,适合用于3D软件模型之间的互导,也可以通过Maya读写。 只支持模型三角面数据和材质信息,无动画功能支持...

    obj文件是3D模型文件格式。由Alias|Wavefront公司为3D建模和动画软件"Advanced Visualizer"开发的一种标准,适合用于3D软件模型之间的互导,也可以通过Maya读写。

    • 只支持模型三角面数据和材质信息,无动画功能支持;
    • 其中几何信息由.obj文件提供,材质信息由.mtl文件定义;
    • 文件以行为单位表示一条数据,可以根据行开头的字符判断后续的内容;
    • 其中 # 字符表示注释行



    OBJ格式

    
    # obj对应的材质文件
    # mtllib testvt.mtl
    # 组名称
    g default
    # o 对象名称(Object name)
    o testvt.obj
    # 顶点
    v -0.5 -0.5 0.1
    v -0.5 -0.5 -0.1
    v 0 0.5 0.1
    v 0 0.5 -0.1
    v 0.5 -0.5 0.1
    v 0.5 -0.5 -0.1
    # 纹理坐标
    vt 0 1
    vt 1 1
    vt 0.5 0
    # 顶点法线
    vn 0 0 1
    vn 0 0 -1
    # 当前图元所用材质
    usemtl Default
    # s Smooth shading across polygons is enabled by smoothing groups.
    # Smooth shading can be disabled as well.
    s off
    # v1/vt1/vn1 v2/vt2/vn2 v3/vt3/vn3(索引起始于1)    
    f 1/1/1 5/2/1 3/3/1
    f 6/2/2 2/1/2 4/3/2
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    

    MTL格式

    # 定义一个名为 'xxx'的材质
    newmtl xxx
    # 材质的环境光(ambient color)
    Ka 0 0 0
    # 散射光(diffuse color)用Kd
    Kd 0.784314 0.784314 0.784314
    # 镜面光(specular color)用Ks
    Ks 0 0 0
    # 折射值 可在0.001到10之间进行取值。若取值为1.0,光在通过物体的时候不发生弯曲。玻璃的折射率为1.5。
    Ni 1
    # 反射指数 定义了反射高光度。该值越高则高光越密集,一般取值范围在0~1000。
    Ns 400
    # 滤光透射率
    Tf 1 1 1
    # 渐隐指数描述 参数factor表示物体融入背景的数量,取值范围为0.0~1.0,取值为1.0表示完全不透明,取值为0.0时表示完全透明。
    d 1
    # 为漫反射指定颜色纹理文件
    map_Kd test_vt.bmp
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    

    参考:
    https://baike.baidu.com/item/OBJ%E6%96%87%E4%BB%B6/3574019
    https://en.wikipedia.org/wiki/Wavefront_.obj_file


    作者:xiaxveliang
    链接:https://www.jianshu.com/p/b52e152d44a9
    来源:简书
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

    展开全文
  • Android端使用OpenGL ES加载OBJ文件数据

    千次阅读 热门讨论 2018-05-09 20:08:32
    obj文件是最简单的一种3D模型文件,可由3dx MAX或Maya等建模软件导出,广泛应用于3D图形应用(如游戏)程序和3D打印等等,其本质上就是文本文件,里面存储的是模型的顶点坐标,顶点法向量和纹理坐标信息。...

    一、obj模型文件概览

    在介绍如何用程序加载obj模型文件之前,首先需要了解一下它的格式。
    obj文件是最简单的一种3D模型文件,可由3dx MAX或Maya等建模软件导出,广泛应用于3D图形应用(如游戏)程序和3D打印等等,其本质上就是文本文件,里面存储的是模型的顶点坐标,顶点法向量和纹理坐标信息。

    下面看一个典型的obj文件

    # 3ds Max Wavefront OBJ Exporter v0.97b - (c)2007 guruware
    # 创建的文件:01.05.2018 15:16:27
    
    #
    # object 棒球帽
    #
    
    v  22.7219 49.3250 -5.6920
    v  22.7255 49.3244 -5.6873
    v  24.6979 49.3887 -10.4577
    v  24.4561 49.4295 -10.6732
    v  22.7288 49.3238 -5.6824
    v  24.9195 49.3465 -10.2052
    v  26.1314 49.3264 -14.0106
    v  25.7093 49.3979 -14.3621
    v  26.5186 49.2526 -13.5884
    v  22.7318 49.3231 -5.6773
    ......
    vn 0.5713 0.0798 -0.8169
    vn 0.5751 0.0783 -0.8144
    vn 0.1130 0.2192 -0.9691
    vn 0.1180 0.2182 -0.9687
    vn 0.5800 0.0763 -0.8110
    vn 0.5844 0.0746 -0.8080
    vn 0.1342 0.2105 -0.9683
    ......
    vt -0.1830 0.1242 0.0073
    vt -0.1789 0.1238 0.0074
    vt -0.1828 0.1220 0.0073
    vt -0.1779 0.1204 0.0075
    vt -0.1863 0.1216 0.0072
    vt -0.1873 0.1176 0.0072
    vt -0.1843 0.0892 0.0072
    vt -0.1872 0.0892 0.0071
    vt -0.1875 0.0867 0.0071
    ......
    f 238/240/239 243/245/244 244/247/245 
    f 244/247/245 239/241/240 238/240/239 
    f 239/241/240 244/247/245 245/248/246 
    f 245/248/246 242/244/243 239/241/240 
    f 246/249/247 247/250/248 248/251/249 
    f 248/251/249 249/252/250 246/249/247 
    f 248/251/249 250/253/251 251/254/252 
    f 251/254/252 249/252/250 248/251/249 

    这里只截取了一部分,从上述obj文件片段中可以看出,其内容是以行为基本单位进行组织的,每种不同前缀开头的行有不同含义:

    • “#”号开头的行为注释,描述模型的一些基本信息,在程序加载的过程中可以忽略。
    • “v”开头的行用于存放顶点坐标,其后面的3个数值分别表示一个顶点的X、Y、Z坐标。
    • “vn”开头的行用于存放顶点法向量,其后面的3个数值分别表示一个顶点的法向量在X轴、Y轴、Z轴上的分量。
    • “vt”开头的行用于存放顶点纹理坐标,其后面的3个数值分别表示纹理坐标的S、T、W分量(S、T为纹理采样坐标,W指的是深度纹理坐标,主要用于3D纹理的采样,OpenGL ES 2.0中对3D纹理还没有普遍支持,故这里不使用)
    • “f”开头的行表示一个面,如果是三角形,(由于OpenGL ES仅支持三角形,故我选择的obj模型都是基于三角形面的)则后面有3组用空格分隔的数据,代表三角形的3个顶点,每组数据包含3个数值,用“/”分隔,依次表示顶点坐标数据索引,顶点纹理坐标数据索引,顶点法向量数据索引。例如有这样一行“ f 238/240/239 243/245/244 244/247/245 ”,则表示这个三角形面中3个顶点的坐标分别来自第238、243、244号“v”开头的行,3个顶点的纹理坐标分别了来自第240、245、247号“vt”开头的行,3个顶点的法向量分别来自第239、244、245号“vn”开头的行。

      有一点需要注意,就是就是我们加载显示obj文件时,顶点和面的数据是必需的,而法向量和纹理数据是可选的。

    二、加载并显示

    根据计算机图形学中的知识,我们加载并显示模型的步骤可分为以下几步:

    1. 将obj文件中的文本信息通过文件IO流读进内存。
    2. 一行行读取,分别用3个数组保存顶点,纹理和法向量数据。
    3. 创建OpenGL场景(这一点Android的GLSurfaceView已经帮我们做好了)
    4. 创建着色器程序,将顶点、纹理等数据传进渲染管线
    5. 启用着色器程序,并设置摄像头位置,启用纹理,添加光照。

    步骤一、创建物体类
    LoadedObjectVertexNormalTexture.java

    public LoadedObjectVertexNormalTexture(MySurfaceView mv,float[] vertices,float[] normals,float texCoors[])
        {
            //初始化顶点坐标与着色数据
            initVertexData(vertices,normals,texCoors);
            //初始化shader
            initShader(mv);
        }

    在构造函数中,根据传入的GLSurfaceView以及顶点,纹理,法向量数组,初始化着色器数据和shader。

    //初始化顶点坐标与着色数据的方法
        public void initVertexData(float[] vertices,float[] normals,float texCoors[])
        {
            //顶点坐标数据的初始化================begin============================
            vCount=vertices.length/3;
    
            //创建顶点坐标数据缓冲
            //vertices.length*4是因为一个整数四个字节
            ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length*4);
            vbb.order(ByteOrder.nativeOrder());//设置字节顺序
            mVertexBuffer = vbb.asFloatBuffer();//转换为Float型缓冲
            mVertexBuffer.put(vertices);//向缓冲区中放入顶点坐标数据
            mVertexBuffer.position(0);//设置缓冲区起始位置
            //特别提示:由于不同平台字节顺序不同数据单元不是字节的一定要经过ByteBuffer
            //转换,关键是要通过ByteOrder设置nativeOrder(),否则有可能会出问题
            //顶点坐标数据的初始化================end============================
    
            //顶点法向量数据的初始化================begin============================
            ByteBuffer cbb = ByteBuffer.allocateDirect(normals.length*4);
            cbb.order(ByteOrder.nativeOrder());//设置字节顺序
            mNormalBuffer = cbb.asFloatBuffer();//转换为Float型缓冲
            mNormalBuffer.put(normals);//向缓冲区中放入顶点法向量数据
            mNormalBuffer.position(0);//设置缓冲区起始位置
            //特别提示:由于不同平台字节顺序不同数据单元不是字节的一定要经过ByteBuffer
            //转换,关键是要通过ByteOrder设置nativeOrder(),否则有可能会出问题
            //顶点着色数据的初始化================end============================
    
            //顶点纹理坐标数据的初始化================begin============================
            ByteBuffer tbb = ByteBuffer.allocateDirect(texCoors.length*4);
            tbb.order(ByteOrder.nativeOrder());//设置字节顺序
            mTexCoorBuffer = tbb.asFloatBuffer();//转换为Float型缓冲
            mTexCoorBuffer.put(texCoors);//向缓冲区中放入顶点纹理坐标数据
            mTexCoorBuffer.position(0);//设置缓冲区起始位置
            //特别提示:由于不同平台字节顺序不同数据单元不是字节的一定要经过ByteBuffer
            //转换,关键是要通过ByteOrder设置nativeOrder(),否则有可能会出问题
            //顶点纹理坐标数据的初始化================end============================
        }
    
        //初始化shader
        public void initShader(MySurfaceView mv)
        {
            //加载顶点着色器的脚本内容
            mVertexShader=ShaderUtil.loadFromAssetsFile("vertex.sh", mv.getResources());
            //加载片元着色器的脚本内容
            mFragmentShader=ShaderUtil.loadFromAssetsFile("frag.sh", mv.getResources());
            //基于顶点着色器与片元着色器创建程序
            mProgram = ShaderUtil.createProgram(mVertexShader, mFragmentShader);
            //获取程序中顶点位置属性引用
            maPositionHandle = GLES20.glGetAttribLocation(mProgram, "aPosition");
            //获取程序中顶点颜色属性引用
            maNormalHandle= GLES20.glGetAttribLocation(mProgram, "aNormal");
            //获取程序中总变换矩阵引用
            muMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
            //获取位置、旋转变换矩阵引用
            muMMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMMatrix");
            //获取程序中光源位置引用
            maLightLocationHandle=GLES20.glGetUniformLocation(mProgram, "uLightLocation");
            //获取程序中顶点纹理坐标属性引用
            maTexCoorHandle= GLES20.glGetAttribLocation(mProgram, "aTexCoor");
            //获取程序中摄像机位置引用
            maCameraHandle=GLES20.glGetUniformLocation(mProgram, "uCamera");
        }

    步骤二、编写从obj文件读取信息的工具类
    LoadUtil.java
    其中的LoadedObjectVertexNormalTexture方法从Resource资源文件中加载obj格式的数据,存入数组,并生成LoadedObjectVertexNormalTexture对象

    //从obj文件中加载携带顶点信息的物体,并自动计算每个顶点的平均法向量
        public static LoadedObjectVertexNormalTexture loadFromFile
        (String fname, Resources r,MySurfaceView mv)
        {
            //加载后物体的引用
            LoadedObjectVertexNormalTexture lo=null;
            //原始顶点坐标列表--直接从obj文件中加载
            ArrayList<Float> alv=new ArrayList<Float>();
            //顶点组装面索引列表--根据面的信息从文件中加载
            ArrayList<Integer> alFaceIndex=new ArrayList<Integer>();
            //结果顶点坐标列表--按面组织好
            ArrayList<Float> alvResult=new ArrayList<Float>();
            //平均前各个索引对应的点的法向量集合Map
            //此HashMap的key为点的索引, value为点所在的各个面的法向量的集合
            HashMap<Integer,HashSet<Normal>> hmn=new HashMap<Integer,HashSet<Normal>>();
            //原始纹理坐标列表
            ArrayList<Float> alt=new ArrayList<Float>();
            //纹理坐标结果列表
            ArrayList<Float> altResult=new ArrayList<Float>();
    
            try
            {
                InputStream in=r.getAssets().open(fname);
                InputStreamReader isr=new InputStreamReader(in);
                BufferedReader br=new BufferedReader(isr);
                String temps=null;
    
                //扫面文件,根据行类型的不同执行不同的处理逻辑
                while((temps=br.readLine())!=null)
                {
                    //用空格分割行中的各个组成部分
                    String[] tempsa=temps.split("[ ]+");
                    if(tempsa[0].trim().equals("v"))
                    {//此行为顶点坐标
                        //若为顶点坐标行则提取出此顶点的XYZ坐标添加到原始顶点坐标列表中
                        alv.add(Float.parseFloat(tempsa[1]));
                        alv.add(Float.parseFloat(tempsa[2]));
                        alv.add(Float.parseFloat(tempsa[3]));
                    }
                    else if(tempsa[0].trim().equals("vt"))
                    {//此行为纹理坐标行
                        //若为纹理坐标行则提取ST坐标并添加进原始纹理坐标列表中
                        alt.add(Float.parseFloat(tempsa[1])/2.0f);
                        alt.add(Float.parseFloat(tempsa[2])/2.0f);
                    }
                    else if(tempsa[0].trim().equals("f"))
                    {//此行为三角形面
                        /*
                         *若为三角形面行则根据 组成面的顶点的索引从原始顶点坐标列表中
                         *提取相应的顶点坐标值添加到结果顶点坐标列表中,同时根据三个
                         *顶点的坐标计算出此面的法向量并添加到平均前各个索引对应的点
                         *的法向量集合组成的Map中
                        */
    
                        int[] index=new int[3];//三个顶点索引值的数组
    
                        //计算第0个顶点的索引,并获取此顶点的XYZ三个坐标
                        index[0]=Integer.parseInt(tempsa[1].split("/")[0])-1;
                        float x0=alv.get(3*index[0]);
                        float y0=alv.get(3*index[0]+1);
                        float z0=alv.get(3*index[0]+2);
                        alvResult.add(x0);
                        alvResult.add(y0);
                        alvResult.add(z0);
    
                        //计算第1个顶点的索引,并获取此顶点的XYZ三个坐标
                        index[1]=Integer.parseInt(tempsa[2].split("/")[0])-1;
                        float x1=alv.get(3*index[1]);
                        float y1=alv.get(3*index[1]+1);
                        float z1=alv.get(3*index[1]+2);
                        alvResult.add(x1);
                        alvResult.add(y1);
                        alvResult.add(z1);
    
                        //计算第2个顶点的索引,并获取此顶点的XYZ三个坐标
                        index[2]=Integer.parseInt(tempsa[3].split("/")[0])-1;
                        float x2=alv.get(3*index[2]);
                        float y2=alv.get(3*index[2]+1);
                        float z2=alv.get(3*index[2]+2);
                        alvResult.add(x2);
                        alvResult.add(y2);
                        alvResult.add(z2);
    
                        //记录此面的顶点索引
                        alFaceIndex.add(index[0]);
                        alFaceIndex.add(index[1]);
                        alFaceIndex.add(index[2]);
    
                        //通过三角形面两个边向量0-1,0-2求叉积得到此面的法向量
                        //求0号点到1号点的向量
                        float vxa=x1-x0;
                        float vya=y1-y0;
                        float vza=z1-z0;
                        //求0号点到2号点的向量
                        float vxb=x2-x0;
                        float vyb=y2-y0;
                        float vzb=z2-z0;
                        //通过求两个向量的叉积计算法向量
                        float[] vNormal=vectorNormal(getCrossProduct
                                (
                                        vxa,vya,vza,vxb,vyb,vzb
                                ));
                        for(int tempInxex:index)
                        {//记录每个索引点的法向量到平均前各个索引对应的点的法向量集合组成的Map中
                            //获取当前索引对应点的法向量集合
                            HashSet<Normal> hsn=hmn.get(tempInxex);
                            if(hsn==null)
                            {//若集合不存在则创建
                                hsn=new HashSet<Normal>();
                            }
                            //将此点的法向量添加到集合中
                            //由于Normal类重写了equals方法,因此同样的法向量不会重复出现在此点
                            //对应的法向量集合中
                            hsn.add(new Normal(vNormal[0],vNormal[1],vNormal[2]));
                            //将集合放进HsahMap中
                            hmn.put(tempInxex, hsn);
                        }
    
                        //将纹理坐标组织到结果纹理坐标列表中
                        //第0个顶点的纹理坐标
                        int indexTex=Integer.parseInt(tempsa[1].split("/")[1])-1;
                        altResult.add(alt.get(indexTex*2));
                        altResult.add(alt.get(indexTex*2+1));
                        //第1个顶点的纹理坐标
                        indexTex=Integer.parseInt(tempsa[2].split("/")[1])-1;
                        altResult.add(alt.get(indexTex*2));
                        altResult.add(alt.get(indexTex*2+1));
                        //第2个顶点的纹理坐标
                        indexTex=Integer.parseInt(tempsa[3].split("/")[1])-1;
                        altResult.add(alt.get(indexTex*2));
                        altResult.add(alt.get(indexTex*2+1));
                    }
                }
    
                //生成顶点数组
                int size=alvResult.size();
                float[] vXYZ=new float[size];
                for(int i=0;i<size;i++)
                {
                    vXYZ[i]=alvResult.get(i);
                }
    
                //生成法向量数组
                float[] nXYZ=new float[alFaceIndex.size()*3];
                int c=0;
                for(Integer i:alFaceIndex)
                {
                    //根据当前点的索引从Map中取出一个法向量的集合
                    HashSet<Normal> hsn=hmn.get(i);
                    //求出平均法向量
                    float[] tn=Normal.getAverage(hsn);
                    //将计算出的平均法向量存放到法向量数组中
                    nXYZ[c++]=tn[0];
                    nXYZ[c++]=tn[1];
                    nXYZ[c++]=tn[2];
                }
    
                //生成纹理数组
                size=altResult.size();
                float[] tST=new float[size];
                for(int i=0;i<size;i++)
                {
                    tST[i]=altResult.get(i);
                }
    
                //创建3D物体对象
                lo=new LoadedObjectVertexNormalTexture(mv,vXYZ,nXYZ,tST);
            }
            catch(Exception e)
            {
                Log.d("load error", "load error");
                e.printStackTrace();
            }
            return lo;
        }

    方法比较长,不过总的思路也很清晰,就先打开文件输入流,循环不断从文件中读取行,根据行的类型不同执行不同的处理逻辑,比如“v”开头的行代表顶点坐标数据,直接用一个数组存储,根据“f”开头的面数据行查找其3个顶点的坐标,纹理索引,最终创建加载的物体对象。这里为了求面的平均法向量,还用到了向量叉乘和求平均的方法。

    //求两个向量的叉乘
        public static float[] getCrossProduct(float x1,float y1,float z1,float x2,float y2,float z2)
        {
            //求出两个矢量叉积矢量在XYZ轴的分量ABC
            float A=y1*z2-y2*z1;
            float B=z1*x2-z2*x1;
            float C=x1*y2-x2*y1;
    
            return new float[]{A,B,C};
        }

    表示法向量的类
    Normal.java

    /**
     * 表示法向量的类
     */
    public class Normal {
        //判断两个法向量是否相同的阈值
        public static final float DIFF = 0.0000001f;
        //法向量在X、Y、Z轴的分量
        float nx;
        float ny;
        float nz;
        public Normal(float nx , float ny , float nz ){
            this.nx = nx;
            this.ny = ny;
            this.nz = nz;
        }
    
        @Override
        public boolean equals(Object obj) {
            //若两个法向量X、Y、Z 3个分量的差都小于指定的阈值则认为这两个法向量相等
            if( obj instanceof Normal ){
                Normal tn = (Normal) obj;
                if( Math.abs(nx - tn.nx) < DIFF && Math.abs(ny - tn.ny) < DIFF && Math.abs(nz - tn.nz) < DIFF ){
                    return true;
                }else {
                    return false;
                }
            }else {
                return false;
            }
        }
    
        @Override
        public int hashCode() {
            return 1;
        }
    
        //求法向量平均值的工具方法
        public static float[] getAverage(Set<Normal> sn){
            //存放法向量X、Y、Z分量和的数组
            float[] result = new float[3];
            for( Normal n : sn ){
                result[0] += n.nx;
                result[1] += n.ny;
                result[2] += n.nz;
            }
            return LoadUtil.vectorNormal(result);
    
        }
    }

    步骤三、接收纹理数据并启用光源和纹理
    LoadedObjectVertexNormalTexture.java

     //绘制物体的方法
        public void drawSelf(int texId)
        {
            //制定使用某套着色器程序
            GLES20.glUseProgram(mProgram);
            //将最终变换矩阵传入着色器程序
            GLES20.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, MatrixState.getFinalMatrix(), 0);
            //将位置、旋转变换矩阵传入着色器程序
            GLES20.glUniformMatrix4fv(muMMatrixHandle, 1, false, MatrixState.getMMatrix(), 0);
            //将光源位置传入着色器程序
            GLES20.glUniform3fv(maLightLocationHandle, 1, MatrixState.lightPositionFB);
            //将摄像机位置传入着色器程序
            GLES20.glUniform3fv(maCameraHandle, 1, MatrixState.cameraFB);
            // 将顶点位置数据传入渲染管线
            GLES20.glVertexAttribPointer
                    (
                            maPositionHandle,
                            3,
                            GLES20.GL_FLOAT,
                            false,
                            3*4,
                            mVertexBuffer
                    );
            //将顶点法向量数据传入渲染管线
            GLES20.glVertexAttribPointer
                    (
                            maNormalHandle,
                            3,
                            GLES20.GL_FLOAT,
                            false,
                            3*4,
                            mNormalBuffer
                    );
            //为画笔指定顶点纹理坐标数据
            GLES20.glVertexAttribPointer
                    (
                            maTexCoorHandle,
                            2,
                            GLES20.GL_FLOAT,
                            false,
                            2*4,
                            mTexCoorBuffer
                    );
            //启用顶点位置、法向量、纹理坐标数据
            GLES20.glEnableVertexAttribArray(maPositionHandle);
            GLES20.glEnableVertexAttribArray(maNormalHandle);
            GLES20.glEnableVertexAttribArray(maTexCoorHandle);
            //绑定纹理
            GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
            GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texId);
            //绘制加载的物体
            GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vCount);
        }

    下面是我们用来显示3D内容的GLSurfaceView

    class MySurfaceView extends GLSurfaceView
    {
        private final float TOUCH_SCALE_FACTOR = 180.0f/320;//角度缩放比例
        private SceneRenderer mRenderer;//场景渲染器
    
        private float mPreviousY;//上次的触控位置Y坐标
        private float mPreviousX;//上次的触控位置X坐标
    
        int textureId;//系统分配的纹理id
    
        public MySurfaceView(Context context) {
            super(context);
            this.setEGLContextClientVersion(2); //设置使用OPENGL ES2.0
            mRenderer = new SceneRenderer();    //创建场景渲染器
            setRenderer(mRenderer);             //设置渲染器
            setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);//设置渲染模式为主动渲染
        }
    
        //触摸事件回调方法
        @Override
        public boolean onTouchEvent(MotionEvent e)
        {
            float y = e.getY();
            float x = e.getX();
            switch (e.getAction()) {
                case MotionEvent.ACTION_MOVE:
                    float dy = y - mPreviousY;//计算触控笔Y位移
                    float dx = x - mPreviousX;//计算触控笔X位移
                    mRenderer.yAngle += dx * TOUCH_SCALE_FACTOR;//设置沿x轴旋转角度
                    mRenderer.xAngle+= dy * TOUCH_SCALE_FACTOR;//设置沿z轴旋转角度
                    requestRender();//重绘画面
            }
            mPreviousY = y;//记录触控笔位置
            mPreviousX = x;//记录触控笔位置
            return true;
        }
    
        private class SceneRenderer implements GLSurfaceView.Renderer
        {
            float yAngle;//绕Y轴旋转的角度
            float xAngle; //绕Z轴旋转的角度
            //从指定的obj文件中加载对象
            LoadedObjectVertexNormalTexture lovo;
    
            public void onDrawFrame(GL10 gl)
            {
                //清除深度缓冲与颜色缓冲
                GLES20.glClear( GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);
    
                //坐标系推远
                MatrixState.pushMatrix();
                MatrixState.translate(0, -2f, -25f);   //ch.obj
                //绕Y轴、Z轴旋转
                MatrixState.rotate(yAngle, 0, 1, 0);
                MatrixState.rotate(xAngle, 1, 0, 0);
    
                //若加载的物体部位空则绘制物体
                if(lovo!=null)
                {
                    lovo.drawSelf(textureId);
                }
                MatrixState.popMatrix();
            }
    
            public void onSurfaceChanged(GL10 gl, int width, int height) {
                //设置视窗大小及位置
                GLES20.glViewport(0, 0, width, height);
                //计算GLSurfaceView的宽高比
                float ratio = (float) width / height;
                //调用此方法计算产生透视投影矩阵
                MatrixState.setProjectFrustum(-ratio, ratio, -1, 1, 2, 500);
                //调用此方法产生摄像机9参数位置矩阵
                MatrixState.setCamera(0,0,50,0f,0f,-20f,0f,1.0f,0.0f);
            }
    
            public void onSurfaceCreated(GL10 gl, EGLConfig config)
            {
                //设置屏幕背景色RGBA
                GLES20.glClearColor(0.0f,0.0f,0.0f,1.0f);
                //打开深度检测
                GLES20.glEnable(GLES20.GL_DEPTH_TEST);
                //打开背面剪裁
                //GLES20.glEnable(GLES20.GL_CULL_FACE);
                //初始化变换矩阵
                MatrixState.setInitStack();
                //初始化光源位置
                MatrixState.setLightLocation(40, 40, 40);
                //加载要绘制的物体
                lovo=LoadUtil.loadFromFile("hat.obj", MySurfaceView.this.getResources(),MySurfaceView.this);
                //加载纹理
                textureId=initTexture(R.drawable.hat_t);
            }
        }
        public int initTexture(int drawableId)//textureId
        {
            //生成纹理ID
            int[] textures = new int[1];
            GLES20.glGenTextures
                    (
                            1,          //产生的纹理id的数量
                            textures,   //纹理id的数组
                            0           //偏移量
                    );
            int textureId=textures[0];
            GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId);
            GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER,GLES20.GL_NEAREST);
            GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,GLES20.GL_TEXTURE_MAG_FILTER,GLES20.GL_LINEAR);
            GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S,GLES20.GL_REPEAT);
            GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T,GLES20.GL_REPEAT);
    
            //通过输入流加载图片===============begin===================
            InputStream is = this.getResources().openRawResource(drawableId);
            Bitmap bitmapTmp;
            try
            {
                bitmapTmp = BitmapFactory.decodeStream(is);
            }
            finally
            {
                try
                {
                    is.close();
                }
                catch(IOException e)
                {
                    e.printStackTrace();
                }
            }
            //通过输入流加载图片===============end=====================
            GLUtils.texImage2D
                    (
                            GLES20.GL_TEXTURE_2D, //纹理类型
                            0,
                            GLUtils.getInternalFormat(bitmapTmp),
                            bitmapTmp, //纹理图像
                            GLUtils.getType(bitmapTmp),
                            0 //纹理边框尺寸
                    );
            bitmapTmp.recycle();          //纹理加载成功后释放图片
            return textureId;
        }

    这里面用到了一个矩阵变换和保存矩阵状态的类MatrixState
    MatrixState.java

    //存储系统矩阵状态的类
    public class MatrixState
    {
        private static float[] mProjMatrix = new float[16];//4x4矩阵 投影用
        private static float[] mVMatrix = new float[16];//摄像机位置朝向9参数矩阵
        private static float[] currMatrix;//当前变换矩阵
        public static float[] lightLocation=new float[]{0,0,0};//定位光光源位置
        public static FloatBuffer cameraFB;
        public static FloatBuffer lightPositionFB;
    
        public static Stack<float[]> mStack=new Stack<float[]>();//保护变换矩阵的栈
    
        public static void setInitStack()//获取不变换初始矩阵
        {
            currMatrix=new float[16];
            Matrix.setRotateM(currMatrix, 0, 0, 1, 0, 0);
        }
    
        public static void pushMatrix()//保护变换矩阵
        {
            mStack.push(currMatrix.clone());
        }
    
        public static void popMatrix()//恢复变换矩阵
        {
            currMatrix=mStack.pop();
        }
    
        public static void translate(float x,float y,float z)//设置沿xyz轴移动
        {
            Matrix.translateM(currMatrix, 0, x, y, z);
        }
    
        public static void rotate(float angle,float x,float y,float z)//设置绕xyz轴移动
        {
            Matrix.rotateM(currMatrix,0,angle,x,y,z);
        }
    
    
        //设置摄像机
        public static void setCamera
        (
                float cx,   //摄像机位置x
                float cy,   //摄像机位置y
                float cz,   //摄像机位置z
                float tx,   //摄像机目标点x
                float ty,   //摄像机目标点y
                float tz,   //摄像机目标点z
                float upx,  //摄像机UP向量X分量
                float upy,  //摄像机UP向量Y分量
                float upz   //摄像机UP向量Z分量
        )
        {
            Matrix.setLookAtM
                    (
                            mVMatrix,
                            0,
                            cx,
                            cy,
                            cz,
                            tx,
                            ty,
                            tz,
                            upx,
                            upy,
                            upz
                    );
    
            float[] cameraLocation=new float[3];//摄像机位置
            cameraLocation[0]=cx;
            cameraLocation[1]=cy;
            cameraLocation[2]=cz;
    
            ByteBuffer llbb = ByteBuffer.allocateDirect(3*4);
            llbb.order(ByteOrder.nativeOrder());//设置字节顺序
            cameraFB=llbb.asFloatBuffer();
            cameraFB.put(cameraLocation);
            cameraFB.position(0);
        }
    
        //设置透视投影参数
        public static void setProjectFrustum
        (
                float left,     //near面的left
                float right,    //near面的right
                float bottom,   //near面的bottom
                float top,      //near面的top
                float near,     //near面距离
                float far       //far面距离
        )
        {
            Matrix.frustumM(mProjMatrix, 0, left, right, bottom, top, near, far);
        }
    
        //设置正交投影参数
        public static void setProjectOrtho
        (
                float left,     //near面的left
                float right,    //near面的right
                float bottom,   //near面的bottom
                float top,      //near面的top
                float near,     //near面距离
                float far       //far面距离
        )
        {
            Matrix.orthoM(mProjMatrix, 0, left, right, bottom, top, near, far);
        }
    
        //获取具体物体的总变换矩阵
        public static float[] getFinalMatrix()
        {
            float[] mMVPMatrix=new float[16];
            Matrix.multiplyMM(mMVPMatrix, 0, mVMatrix, 0, currMatrix, 0);
            Matrix.multiplyMM(mMVPMatrix, 0, mProjMatrix, 0, mMVPMatrix, 0);
            return mMVPMatrix;
        }
    
        //获取具体物体的变换矩阵
        public static float[] getMMatrix()
        {
            return currMatrix;
        }
    
        //设置灯光位置的方法
        public static void setLightLocation(float x,float y,float z)
        {
            lightLocation[0]=x;
            lightLocation[1]=y;
            lightLocation[2]=z;
            ByteBuffer llbb = ByteBuffer.allocateDirect(3*4);
            llbb.order(ByteOrder.nativeOrder());//设置字节顺序
            lightPositionFB=llbb.asFloatBuffer();
            lightPositionFB.put(lightLocation);
            lightPositionFB.position(0);
        }
    }

    最后不要忘了编写我们的顶点着色器和片元着色器,和我文章里之前写过的那些着色器程序基本类似,都是正常的格式,主要注意添加光源和光照模型,比较简单,这里就不再赘述了。

    最终的效果如下所示:
    这里写图片描述
    是不是挺酷炫的?

    展开全文
  • osgb是二进制文件,可以包括贴图数据。 fbx文件 FBX 是 FilmBoX 这套软件所使用的格式,后改称 Motionbuilder。因为Motionbuilder扮演的是动作制作的平台,所以在前端的modeling和后端的rendering也都有赖于其它软件...
  • 前面都是在代码中手写 ...模型有专门负责模型制作的人,她们使用3dmax 或者 maya 或者Blende来制作各种精美的模型,然后用QQ发给程序猿。 程序猿就负责把这些模型文件解析出来,从里面读取顶点信息、纹理信息、摄像
  • .obj和.mtl格式详解 obj文件是3D模型文件格式。由Alias|Wavefront公司为3D建模和动画软件"Advanced Visualizer"开发的一种标准,适合用于3D软件模型之间的互导,也可以通过Maya读写。 只支持模型三角面数据和材质...
  • obj文件是3D模型文件格式。由Alias|Wavefront公司为3D建模和动画软件”Advanced Visualizer”开发的一种标准,适合用于3D软件模型之间的互导,也可以通过Maya读写。 只支持模型三角面数据和材质信息,无动画功能...
  • glm库 使用说明

    千次阅读 2017-11-10 11:01:58
    3DS MAX,MAYA都可以把模型以OBJ文件格式导出。OBJ文件中包含模型的顶点,面,三角形,法向,纹理坐标等数据,但是其中不包含纹理和材质。材质可以放在OBJ文件中指定一个材质库文件中,使用时材质库文
  • glm使用说明

    千次阅读 2015-03-27 10:06:47
    3DS MAX,MAYA都可以把模型以OBJ文件格式导出。OBJ文件中包含模型的顶点,面,三角形,法向,纹理坐标等数据,但是其中不包含纹理和材质。材质可以放在OBJ文件中指定一个材质库文件中,使用时材质库文
  • GLM中文使用说明

    2012-05-23 09:24:19
    OBJ文件: 这里的OBJ文件是...3DS MAX,MAYA都可以把模型以OBJ文件格式导出。OBJ文件中包含模型的顶点,面,三角形,法向,纹理坐标等数据,但是其中不包含纹理和材质。材质可以放在OBJ文件中指定一个材质库...
  • 在加载模型的过程中,最通用的一种模型格式obj,不管是maya,3dsmax,还是blender,c4d,solidworks,都可以导出obj这种格式的模型,obj模型是一种文本格式,即意味着可以用记事本或者notepad++打开并编辑它,下图...
  • cocos2d-x 学习笔记1 Sprite3D

    千次阅读 2014-12-06 01:50:15
    1) objobj是max或maya默认可以导出的格式,不需要任何转换,但是它有一个缺点就是不支持动画的导出。 2) c3t文件是通过FBX模型文件转换后生成的Json格式的文件,使用c3t格式的目的是方便用户进行模型数据的查看...
  • 如果maya或者3dmax导出obj然后导进blender,再导出gltf格式,就容易再格式转换的过程中出现数据丢失。(这时候你会发现你展示出来的模型为黑色,在你代码没有问题的情况下,你会发现后台也不报错,别的模型也可以...
  • 最近研究3d引擎在html5中的效果实现,需求当然是将美工做好的3D模型导入到程序中,并且实现...并且通过COLLDAD把obj文件转换成类似json数据格式的参数,导入网页中才可以导出模型,经过一番研究终于可以将模型导入到...
  • 角色有蒙皮,你可以根据自己需要选择自己的蒙皮,但需要自己提供Obj格式的三维模型,为了使骨骼驱动皮肤,你还需要提供皮肤与骨骼绑定的权重数据,我提供了maya插件可以将你的模型和骨骼的权重数据maya中导出,...
  • Opengl学习之模型加载——Assimp

    万次阅读 2017-08-13 14:27:24
    它支持多种格式的模型文件,如obj、3ds、c4e等。模型一般通过Blender、3DS Max 或者Maya这样的工具软件制作,然后可以导出模型文件。我们在使用Opengl时,就需要将这些文件中的数据内容解析出来,内容主要有顶点数据...
  • 我们也可以使用3d max,maya,blender这类建模工具制作一个球体,输出一个数据文件,导入程序。常用格式obj,ply。一般而言,3d max 类似的工具导出的物体都是三角形或者四边形网格。当然,还有其他的表现方式,....

空空如也

空空如也

1 2
收藏数 24
精华内容 9
关键字:

mayaobj数据格式