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 = ; i < cube.triangles.Length / ; i++) {//一个模型包含非常多的三角形,每个三角形都要执行我们定义的复杂操作
Vector3 t0 = cube.vertices[cube.triangles[i * + ]];//得到第一个顶点的坐标
Vector3 t1 = cube.vertices[cube.triangles[i * + ]];//得到第二个顶点的坐标
Vector3 t2 = cube.vertices[cube.triangles[i * + ]];//得到第三个顶点的坐标 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 + ); triangles.Add(count + ); triangles.Add(count + );
triangles.Add(count + ); triangles.Add(count + ); triangles.Add(count + );
triangles.Add(count + ); triangles.Add(count + ); triangles.Add(count + );
triangles.Add(count + ); triangles.Add(count + ); triangles.Add(count + ); //-------------------------------------------------------------
//和上面获得顶点坐标的做法一样,获得各个normals法线坐标
Vector3 n0 = cube.normals[cube.triangles[i * + ]];
Vector3 n1 = cube.normals[cube.triangles[i * + ]];
Vector3 n2 = cube.normals[cube.triangles[i * + ]]; 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 * + ]];
Vector2 uv1 = cube.uv[cube.triangles[i * + ]];
Vector2 uv2 = cube.uv[cube.triangles[i * + ]]; 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 * + ]];
Vector4 tan1 = cube.tangents[cube.triangles[i * + ]];
Vector4 tan2 = cube.tangents[cube.triangles[i * + ]]; 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.效果

      

关于Unity中Mesh网格的详解的更多相关文章

  1. unity中camera摄像头控制详解

    目录 1. 缘起 2. 开发 2.1. 建立项目 2.2. 旋转 2.2.1. 四元数 2.3. 移动 2.3.1. 向量操作 2.4. 镜头拉伸 2.5. 复位 2.6. 优化 1 缘起 我们的产品 ...

  2. Grid 网格布局详解

    Grid网格布局详解: Grid布局与Flex布局有着一定的相似性,Grid布局是将容器划分成行和列,产生单元格,可以看做是二维布局. 基本概念: 采用网格布局的区域,称为"容器" ...

  3. C#中string.format用法详解

    C#中string.format用法详解 本文实例总结了C#中string.format用法.分享给大家供大家参考.具体分析如下: String.Format 方法的几种定义: String.Form ...

  4. c++中vector的用法详解

    c++中vector的用法详解 vector(向量): C++中的一种数据结构,确切的说是一个类.它相当于一个动态的数组,当程序员无法知道自己需要的数组的规模多大时,用其来解决问题可以达到最大节约空间 ...

  5. 011-Scala中的apply实战详解

    011-Scala中的apply实战详解 object中的apply方法 class中的apply方法 使用方法 apply方法可以应用在类或者Object对象中 class类 必须要创建实例化的类对 ...

  6. C# WinForm 中 MessageBox的使用详解

    1.C# WinForm 中 MessageBox的使用详解:http://www.cnblogs.com/bq-blog/archive/2012/07/27/2611810.html

  7. JScript中的条件注释详解(转载自网络)

    JScript中的条件注释详解-转载 这篇文章主要介绍了JScript中的条件注释详解,本文讲解了@cc_on.@if.@set.@_win32.@_win16.@_mac等条件注释语句及可用于条件编 ...

  8. java中的io系统详解 - ilibaba的专栏 - 博客频道 - CSDN.NET

    java中的io系统详解 - ilibaba的专栏 - 博客频道 - CSDN.NET 亲,“社区之星”已经一周岁了!      社区福利快来领取免费参加MDCC大会机会哦    Tag功能介绍—我们 ...

  9. Scala 深入浅出实战经典 第57讲:Scala中Dependency Injection实战详解

    王家林亲授<DT大数据梦工厂>大数据实战视频 Scala 深入浅出实战经典(1-87讲)完整视频.PPT.代码下载:百度云盘:http://pan.baidu.com/s/1c0noOt6 ...

随机推荐

  1. [svc]高并发场景 LVS DR +KeepAlive高可用实现及ka的persistence_timeout参数

    LVS-DR+keepalived模式是一种非常经典的常用生产组合 高可用场景及LVS架构 一般都用一(负载)拖多(Server Array)方式 使用LVS架设的服务器集群系统有三个部分组成: (1 ...

  2. lua -- 商店的数据管理类

    module (..., package.seeall) local ShopM = {} local SystemPrompt = require(__APP_PACKAGE_NAME__ .. & ...

  3. Java 虚拟机类加载器

    虚拟机设计团队把类加载阶段张的”通过一个类的全限定名来获取此类的二进制字节流”这个动作放到Java虚拟机外部去实现,以便让应用程序自己决定如何去获取所需要的类.实现这个动作的代码模块称为”类加载器”. ...

  4. 不忘初心,回归本质 .net core

    static void Main(string[] args) { ; object j = i;//装箱 就是把值类型转换成引用类型 int k = (int)j;//拆箱 就是把引用类型转换成值类 ...

  5. NET Core 1.1 版本项目和2.0环境下的项目开发注意事项

    在NET Core 1.1开发下的项目最好不要随便把工具更新升级到2.0,这样最容易导致之前的.NETCore直接被升级不兼容早前版本 会引起项目无法启动在运行调试IIS express 时候直接一闪 ...

  6. 在 Yosemite 装 Jave 的方法. ( 适用于 OS X 10.10 )

    因为并非所有用户都用得着 Java ,所以在默认状态下 OS X 不预装 Java , 如果你需要的话可以手动安装. 1. http://support.apple.com/kb/DL1572到苹果官 ...

  7. 【delphi】Byte数组与String类型的转换

    string string = AnsiString = 长字符串,理论上长度不受限制,但其实受限于最大寻址范围2的32次方=4G字节: 变量Str名字是一个指针,指向位于堆内存的字符序列,字符序列起 ...

  8. sbt编译spark程序提示value toDF is not a member of Seq()

    sbt编译spark程序提示value toDF is not a member of Seq() 前提 使用Scala编写的Spark程序,在sbt编译打包的时候提示value toDF is no ...

  9. RedHat下安装Python开发环境

    Linux RedHat下安装Python2.7.pip.ipython环境.eclipse和PyDev环境 准备工作,源Python2.6备份: 根据which python具体目录而定,进行备份 ...

  10. 通过Fiddler肆意修改接口返回数据进行测试

    [本文出自天外归云的博客园] 方法介绍与比对 在测试的过程中,有的需求是这样的,它需要你修改接口返回的数据,从而检查在客户端手机app内是否显示正确,这也算是一种接口容错测试,接口容错测试属于app性 ...