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. Atitit nodejs5 nodejs6  nodejs 7.2.1  新特性attialx总结

    Atitit nodejs5 nodejs6  nodejs 7.2.1  新特性attialx总结 1.1. Node.js 4.0.0 已经发布了 .这是和 io.js 合并之后的首个稳定版本,它 ...

  2. Atitit 图像处理类库 halcon11  安装与环境搭建attilax总结

    Atitit 图像处理类库 halcon11  安装与环境搭建attilax总结 正常安装软件,安装前请先退出其它一切正在运行的程序. 先安装halcon-10.0-windows.exe.安装完成后 ...

  3. [转载]关于generate用法的总结【Verilog】

    转载自http://www.cnblogs.com/nanoty/archive/2012/11/13/2768933.html Abtract generate语句允许细化时间(Elaboratio ...

  4. nginx配置ssl双向证书

    CA根证书制作 # 创建CA私钥 openssl genrsa -out ca.key 2048 #制作CA根证书(公钥) openssl req -new -x509 -days 3650 -key ...

  5. Hibernate 建立一对多双向关联关系

    下面内容整理自<精通Hibernate>第二版 注:既然是双向关联."一对多双向关联"和"多对一双向关联"是同一回事. 对象位于内存中,在内存中从一 ...

  6. 转:显示技术中的帧、帧数、帧率、 FPS

    在视频领域,电影.电视.数字视频等可视为随时间连续变换的许多张画面,而“帧( Frame)”是指每一张画面.而我们日常口语习惯或者说不严谨的交流中,通常对于帧数( Frames)与帧率( Frame ...

  7. 未能为数据库 '*'中得对象'*'分配空间,因文件组'PRIMARY'已满

    服务器使用mssqlserver2005,最近经常出现无法新增信息错误,查看日志,发现严重错误提示,内容大致为: 无法为数据库 'weixin_main' 中的对象 'dbo.wx_logs'.'PK ...

  8. 【ARM】ARM程序规范

    1.函数名单词之间用_隔开,每一个字母大写      Uart_Printf()    //这个由三星的TEST风格延续下来,因此没有参数时,必须加void,否则ADS会编译报警    void Te ...

  9. vm12序列号

    VMware tools怎么删除rpm -e open-vm-tools-desktop vm12序列号5A02H-AU243-TZJ49-GTC7K-3C61NVF5XA-FNDDJ-085GZ-4 ...

  10. Leetcode:Edit Distance 解题报告

    Edit Distance Given two words word1 and word2, find the minimum number of steps required to convert  ...