zoukankan      html  css  js  c++  java
  • 关于Unity中Mesh网格的详解

    3D模型

    通过3D建模软件所建出来的点和面,如以三角形为主的点和面,比如人的脑袋一个球,就是由各种各样的三角形组成的点和面。

    点和面以及纹理坐标都是通过3D建模软件建模出来的。

    Unity会帮我们把模型的信息存到Mesh里面来,Mesh翻译成中文是网格。

    顶点,三角形,纹理坐标,法线和切线。

    3D建模软件

    1:Autodesk 3D Studio Max 支持mac os windows;
    2: Autodesk 3D Maya 支持windows
    3: Cinema4D 支持mac os windows
    4: Blender 开源跨平台的全能三维制作软件, 支持mac os windows, linux;
    5: Cheetah3D: 支持mac os
    6: Unity与建模软件的单位比例:
    unity系统单位为m, 建模软件的m的尺寸大小不一样,所以导入的时候有差异:

            内部米      导入unity后的尺寸/m        与Unity单位的比例关系
    3Dmax         1        0.01              100:1
    Maya            1        100               1:100
    Cinema 4D       1        100               1:100
    Light Wave       1        0.01                100:1


    网格Mesh

    1: Unity提供一个Mesh类,允许脚本来创建和修改,通过Mesh类能生成或修改物体的网格,能做出非常酷炫的物体变形特效;
    2: Mesh filter 网格过滤器从资源中拿出网格并将其传递给MeshRender,用于绘制, 导入模型的时候,Unity会自动创建一个这样的组件;
    3: Mesh 是网格过滤器实例化的Mesh, Mesh中存储物体的网格数据的属性和生成或修改物体网格的方法
    4: 点---->顶点数组<Vector3>: 每个顶点的x, y, z坐标。Vector3对象,面与面有共用的顶点,所以为了节约内存,先存顶点,然后再存三角形;


    5: 面---->三角形索引数组<int>: Mesh里面每个三角形为一个面,由于面与面的顶点公用,所以,用索引来表示三角形的一个面,可以节约模型内存空间, 0, 1, 2表示一个面,对应的顶点是在顶点数组中的索引,三角形顶点的顺序为逆时针为正面,顺时针为反面。


    6: 顶点法线: 面的法线是与面垂直的线, 严格意义上讲,点是没有法线的, 在光照计算的时候,使用法线来进行光照计算,

     如果一个面上所有的法线都是一样,那么光着色也一样,看起来会很奇怪,所以通过某种算法,把多个面公用的顶点的法线根据算法综合插值,得到顶点法线;
    7: 顶点纹理坐标<Vector2>: 顶点对应的纹理上的UV坐标;
    6: 顶点切线<Vector4> 顶点切线,知道有这个东西就行了;

    Mesh的重要属性

    (1) vertices 网格顶点数组;
    (2) normals 网格的法线数组;
    (3) tangents 网格的切线数组;
    (4) uv 网格的基础纹理坐标;
    (5) uv2 网格设定的第二个纹理坐标;
    (6) bounds 网格的包围盒;
    (7) Colors 网格的顶点颜色数组;
    (8) triangles 包含所有三角形的顶点索引数组;
    (9) vectexCount 网格中的顶点数量(只读的);
    (10) subMeshCount 子网格的数量,每个材质都有一个独立的网格列表;
    (11) bonesWeights: 每个顶点的骨骼权重;
    (12) bindposes: 绑定姿势,每个索引绑定的姿势使用具有相同的索引骨骼;

    Mesh的重要方法

    (1) Clear 清空所有的顶点数据和所有的三角形索引;
    (2) RecalculateBounds 重新计算网格的包围盒;
    (3) RecalculateNormals 重新计算网格的法线;
    (4) Optimze 显示优化的网格;
    (5) GetTriangles 返回网格的三角形列表;
    (6) SetTriangles 为网格设定三角形列表;
    (7) CominMeshes组合多个网格到同一个网格;

    Mesh修改案例

    1: 将模型的Mesh复制给Mesh filter组件的Mesh数据。
    2: 讲模型的Mesh的模型顶点数和面数增加;
    3: 开发思路:
      (1) 创建项目,配置目录,导入模型,材质;
      (2) 模型拖入场景树,去掉其他的组件,只保留Mesh filter,点击里面的实例查看Mesh;
      (3) 创建一个空的节点,加入Mesh filter组件,加入MeshRender组件,关联好材质;
      (4) 创建脚本,挂载到这个空节点上,脚本上有组件Mesh filter,关联到前面有的Mesh节点;
      (5) 赋值顶点,三角形, 法线,切线,纹理坐标, 运行观察结果;
      (6) 插值顶点,法线,切线, 纹理坐标, 重新设置三角形索引, 运行观察结果;

    Mesh案例详细步骤

    1.创建Unity工程和文件目录

    2.导入模型和材质到res文件夹下zhang.FBX和wenli.tga(第54)

    3.把模型拖入场景中,点击模型的Mesh Filter组件的Mesh属性,发现多一个资源出来,那个就是过滤读取到的网格,可以查看详细的网格属性

    24个顶点,12个三角形

    4.模型的Mesh Renderer组件是用来绘制网格的组件,它的Mesh是Mesh Filter传递过来的,如果隐藏这个组件,场景中就不会显示出模型

    5.创建一个材质,把wenli.tga当做材质的纹理贴图拖进Albedo里面,然后把模型和材质关联。

    6.效果

    代码获得Mesh

    1.创建一个空节点item,添加一个Mesh Filter组件

    2.创建一个脚本mesh_test,挂载在item下面,通过代码来获得其他模型的Mesh

    打开mesh_test

    using UnityEngine;
    using System.Collections;public class mesh_test : MonoBehaviour {
        public MeshFilter cube_mesh;//获得编辑器传递进来的模型的MeshFilter组件,必须是已经有MeshFilter组件和Mesh的节点
        // Use this for initialization
        void Start () {
            Mesh cube = this.cube_mesh.mesh;//传递进来的模型的MeshFilter组件的Mesh赋值给Mesh类型的变量cube
            
            Mesh self_mesh = this.GetComponent<MeshFilter>().mesh;//获得自己节点下的MeshFilter组件过滤得到的Mesh
            self_mesh.Clear();//先把自己的Mesh清零
            self_mesh.vertices = cube.vertices;//把变量cube的顶点数组传递给自己
            self_mesh.triangles = cube.triangles;//把变量cube的三角形数组传递给自己
            self_mesh.normals = cube.normals;//把变量cube的法线数组传递给自己
            self_mesh.uv = cube.uv;//把变量cube的纹理坐标数组传递给自己
            self_mesh.tangents = cube.tangents;//把变量cube的切线数组传递给自己
    
            self_mesh.RecalculateBounds();//重新计算自己的Mesh
           }
    
        // Update is called once per frame
        void Update () {
        
        }
    }

    3.再给item添加Mesh Renderer组件,再关联一个材质,这样,它就可以在场景中绘制出模型了,它的Mesh是别人那里拿的。

    复杂操作Mesh

    1.思路

    把模型中的所有三角形都再增加三个顶点,每个顶点在对应边的中点。

    2.创建一个脚本mesh_test,挂载在item下面,通过代码来增加顶点

    打开脚本mesh_test

    using UnityEngine;
    using System.Collections;
    using System.Collections.Generic;
    
    public class mesh_test : MonoBehaviour {
        public MeshFilter cube_mesh;
        // Use this for initialization
        void Start () {
            Mesh cube = this.cube_mesh.mesh;
    
            //定义需要用到的和Mesh有关的变量
            List<Vector3> vertices = new List<Vector3>();
            List<int> triangles = new List<int>();
            List<Vector3> normals = new List<Vector3>();
            List<Vector2> uv = new List<Vector2>();
            List<Vector4> tangents = new List<Vector4>();
    
    
            //遍历Mesh的三角形数组
            for (int i = 0; i < cube.triangles.Length / 3; i++) {//一个模型包含非常多的三角形,每个三角形都要执行我们定义的复杂操作
                Vector3 t0 = cube.vertices[cube.triangles[i * 3 + 0]];//得到第一个顶点的坐标
                Vector3 t1 = cube.vertices[cube.triangles[i * 3 + 1]];//得到第二个顶点的坐标
                Vector3 t2 = cube.vertices[cube.triangles[i * 3 + 2]];//得到第三个顶点的坐标
    
                Vector3 t3 = Vector3.Lerp(t0, t1, 0.5f);//第三个点的坐标为第一个点和第二个点的中点
                Vector3 t4 = Vector3.Lerp(t1, t2, 0.5f);//第四个点的坐标为第二个点和第三个点的中点
                Vector3 t5 = Vector3.Lerp(t0, t2, 0.5f);//第五个点的坐标为第一个点和第三个点的中点
    
                int count = vertices.Count;//获得初始的大小,等下用这个变量可以表示索引
    
                //插入顶点坐标到顶点数组vertices中,vertices填充完毕
                vertices.Add(t0); // 索引为count + 0
                vertices.Add(t1); // 索引为count + 1
                vertices.Add(t2); // 索引为count + 2
                vertices.Add(t3); // 索引为count + 3
                vertices.Add(t4); // 索引为count + 4
                vertices.Add(t5); // 索引为count + 5
    
    
                //-------------------------------------------------------------
                //插入三角形顶点索引到三角形数组triangles中,triangles填充完毕
                triangles.Add(count + 0); triangles.Add(count + 3); triangles.Add(count + 5);
                triangles.Add(count + 3); triangles.Add(count + 1); triangles.Add(count + 4);
                triangles.Add(count + 4); triangles.Add(count + 2); triangles.Add(count + 5);
                triangles.Add(count + 3); triangles.Add(count + 4); triangles.Add(count + 5);
    
                //-------------------------------------------------------------
                //和上面获得顶点坐标的做法一样,获得各个normals法线坐标
                Vector3 n0 = cube.normals[cube.triangles[i * 3 + 0]];
                Vector3 n1 = cube.normals[cube.triangles[i * 3 + 1]];
                Vector3 n2 = cube.normals[cube.triangles[i * 3 + 2]];
    
                Vector3 n3 = Vector3.Lerp(n0, n1, 0.5f);
                Vector3 n4 = Vector3.Lerp(n1, n2, 0.5f);
                Vector3 n5 = Vector3.Lerp(n0, n2, 0.5f);
    
                //插入法线坐标到法线数组normals中,normals填充完毕
                normals.Add(n0); 
                normals.Add(n1); 
                normals.Add(n2); 
                normals.Add(n3); 
                normals.Add(n4); 
                normals.Add(n5);
    
                //-------------------------------------------------------------
                //和上面获得顶点坐标的做法一样,获得各个uv纹理坐标
                Vector2 uv0 = cube.uv[cube.triangles[i * 3 + 0]];
                Vector2 uv1 = cube.uv[cube.triangles[i * 3 + 1]];
                Vector2 uv2 = cube.uv[cube.triangles[i * 3 + 2]];
    
                Vector2 uv3 = Vector3.Lerp(uv0, uv1, 0.5f);
                Vector2 uv4 = Vector3.Lerp(uv1, uv2, 0.5f);
                Vector2 uv5 = Vector3.Lerp(uv0, uv2, 0.5f);
    
                //插入纹理坐标到纹理数组uv中,uv填充完毕
                uv.Add(uv0);
                uv.Add(uv1);
                uv.Add(uv2);
                uv.Add(uv3);
                uv.Add(uv4);
                uv.Add(uv5);
    
                //-------------------------------------------------------------
                //和上面获得顶点坐标的做法一样,获得各个tangents切线坐标
                Vector4 tan0 = cube.tangents[cube.triangles[i * 3 + 0]];
                Vector4 tan1 = cube.tangents[cube.triangles[i * 3 + 1]];
                Vector4 tan2 = cube.tangents[cube.triangles[i * 3 + 2]];
    
                Vector4 tan3 = Vector3.Lerp(tan0, tan1, 0.5f);
                Vector4 tan4 = Vector3.Lerp(tan1, tan2, 0.5f);
                Vector4 tan5 = Vector3.Lerp(tan0, tan2, 0.5f);
    
                //插入切线坐标到切线数组tangents中,tangents填充完毕
                tangents.Add(tan0);
                tangents.Add(tan1);
                tangents.Add(tan2);
                tangents.Add(tan3);
                tangents.Add(tan4);
                tangents.Add(tan5);
            }
    
            //传递给自己的Mesh并重新绘制网格
            Mesh self_mesh = this.GetComponent<MeshFilter>().mesh;
            self_mesh.Clear();
            self_mesh.vertices = vertices.ToArray();//List转换为Array
            self_mesh.triangles = triangles.ToArray();
            self_mesh.normals = normals.ToArray();
            self_mesh.uv = uv.ToArray();
            self_mesh.tangents = tangents.ToArray();
    
            self_mesh.RecalculateBounds();
    
            //没有删除重复的顶点,有待完善
        }
        
        // Update is called once per frame
        void Update () {
        
        }
    }

    3.效果

          

  • 相关阅读:
    Linux命令应用大词典-第11章 Shell编程
    Kubernetes 学习12 kubernetes 存储卷
    linux dd命令
    Kubernetes 学习11 kubernetes ingress及ingress controller
    Kubernetes 学习10 Service资源
    Kubernetes 学习9 Pod控制器
    Kubernetes 学习8 Pod控制器
    Kubernetes 学习7 Pod控制器应用进阶2
    Kubernetes 学习6 Pod控制器应用进阶
    Kubernetes 学习5 kubernetes资源清单定义入门
  • 原文地址:https://www.cnblogs.com/HangZhe/p/7258404.html
Copyright © 2011-2022 走看看