网格切割(MeshCut)
Unity学习一篇
<details>
<summary>通用快捷引导</summary>
目录
Mesh 学习
入门级代码
相关代码已经同步至Git。现在粘贴有意的代码块:
public class MeshExample : MonoBehaviour
{
// 必要的 属性:顶点 、三角形索引
public Vector3[] newVertices;
public Vector2[] newUV;
/// <summary>
/// 表示要 存在的三角面。其值为 顶点的索引。 从0 开始 每三个索引 表示一个三角面。 三角面的法线
/// 是 三个点 以 左手手坐标系,叉乘结果方向为准。以第一个点为两个向量的起点
/// </summary>
public int[] newTriangles;
public Color[] colors;
void Start() {
Mesh mesh = new Mesh();
GetComponent<MeshFilter>().mesh = mesh;
mesh.vertices = newVertices;
mesh.uv = newUV;
mesh.triangles = newTriangles;
mesh.colors = colors;
}
}
/// <summary>
/// 可以通过脚本修改法线反向。
/// </summary>
public class MeshExample3 : MonoBehaviour
{
// Start is called before the first frame update
private Mesh meshV;
void Start()
{
meshV = GetComponent<MeshFilter>().mesh;
NormalBack(meshV);
}
private void NormalBack(Mesh IN){
Vector3[] vertices = IN.vertices;
Vector3[] normals = IN.normals;
int[] Triangles = IN.triangles;
int buffer;
for(int i=0;i<Triangles.Length;i+=3){
buffer = Triangles[i+1];
Triangles[i+1]=Triangles[i+2];
Triangles[i+2]=buffer;
}
// 这里数组肯定是引用类型,但是其中通过 某种手段,看起来像值类型。因此 存在一个Clear().
for(int i=0;i<vertices.Length;i++){
vertices[i] *=1.2f;
}
IN.triangles = Triangles;
IN.vertices = vertices;
}
}
概念
BlendShape 属性: 混合形状。用来做面部表情用。
切割案例分析(核心)
-
两向量,计算垂直向量。
image -
计算旋转
如图所示: 好久没动手,差点忘了,好险!
image
- 切割面思路。(案例中)
入射线碰撞,+ 世界 Up 向量 确定一个平面。在该平面内 ,求得 入射线 得垂向量。 —— 利用 上面得公式即可。
再点乘,求得切割法向量。
- 判断 三角面 点 是否被切割 图解:
求得黑色 “差向量”和“法向量” 点乘,通过 点乘值“0” 判断左右。
换种理解方式:以入射线、切割法线、垂线,建立一个坐标系。求得 顶点 在该坐标系的相对位置。…… 没有进行 线性变换 的坐标系转换,这样说也不妥。 反正,要心中右这个坐标轴。不然 千辛万苦求出来干嘛。
- 拷贝顶点。
只要切割 必定舍弃原来得 mesh。存在两个临时 mesh。
因此情况一:三角面 完全再边。该三角面不需要被切割。 需要将三角面拷贝到 对应得临时 mesh 属性中。
注意:(三角面不需要切割:) 需要拷贝得 包括:顶点、三角索引、法线。 正常情况,只要==按顺序==将 旧三角顶点索引,添加到临时 三角索引中即可。 顶点、法线也是如此。但是 顶点、法线需要判断是否已经存在。————错误!
零时顶点集合、零时三角面集合。 临时三角面集合需要 的是 临时顶点 集合 的索引。因此,中间需要一个过度。
代码: 关闭列表—— 字典是 关键
//拷贝顶点
/// <summary>
/// 三角面、顶点、法线拷贝
/// </summary>
/// <param name="index1">旧顶点列表索引1</param>
/// <param name="index2">旧顶点列表索引2</param>
/// <param name="index3">旧顶点列表索引3</param>
/// <param name="tempVert">临时(新)顶点列表</param>
/// <param name="tempNormal">新法线列表</param>
/// <param name="triangles">新三角列表</param>
/// <param name="pointIndex">关闭列表</param>
static void CopyVert(int index1,int index2,int index3,ref List<Vector3> tempVert,ref List<Vector3> tempNormal,ref List<int> triangles,ref Dictionary<int,int> pointIndex){
//是顶点索引否存在 : 字典作为 一个 旧顶点 索引 的关闭列表。(键值对:旧顶点列表索引 —— 新顶点列表索引)
//用于判断点是否拷贝过
if (!pointIndex.ContainsKey (index1)) {
tempVert.Add (targetMesh.vertices [index1]); //不存在索引时,插入对应索引得值。——1
tempNormal.Add (targetMesh.normals [index1]);
pointIndex.Add (index1,tempVert.Count-1); //这里是关键
}
if (!pointIndex.ContainsKey (index2)) {
tempVert.Add (targetMesh.vertices [index2]); //不存在索引时,插入对应索引得值。——2
tempNormal.Add (targetMesh.normals [index2]);
pointIndex.Add (index2,tempVert.Count-1);
}
if (!pointIndex.ContainsKey (index3)) {
tempVert.Add (targetMesh.vertices [index3]); //不存在索引时,插入对应索引得值。——3
tempNormal.Add (targetMesh.normals [index3]);
pointIndex.Add (index3,tempVert.Count-1);
}
triangles.Add (pointIndex[index1]); //通过 关闭列表,找到新顶点列表索引。
triangles.Add (pointIndex[index2]);
triangles.Add (pointIndex[index3]);
}
- 切割算法核心——求切割点
如图
image
==关键点:没有,求不出来==。
-
用于切割的 坐标轴(两两垂直)—— 这是基础条件。以后 本能这样做吧,虽然发明不了算法,但看得懂,也是能耐了。
-
像似三角形。
-
其他情况:我能力有限,推不出来。不过实验是成功的,所以,公式 适用于 三维空间下的 其他情况 三角面的 运算。
-
注意求倍数时,除数为零!
- 平面几何和空间几何。
-
我平面几何还行,空间几何问题有点大。
-
假设:在平面几何中 推导的公式(完全由向量构成),那么它可以应用到空间几何。—— 祈祷这个是真理吧。
-
两条 垂直异面直线 在同一平面内的投影 可能不垂直。 两条垂直共面直线在同一平面内的投影,垂直。—— 不知道如何证明,也不知对错。
image
-
补全切面
image
当我们算出所有切面上的切面点时。可以求出一个中心点,然后每两个切面点和 中心点构成一个三角面。——这里 保证 (0、1)(2、3)…… 这样的顺序,才不会出现交叉面
还需要求中心点的法线:直接就是 切面 切割法线的方向转化坐标系即可。
- 关于法线
image这里区分一下:三角面法线、顶点法线。 三角面法线(自己命名)
三角面的法线:决定三角面是哪一个面被渲染可见。 只通过三角面的顶点顺序+叉乘+左手坐标系决定。
顶点法线:决定光照渲染的值,不决定是否被渲染可见。通过直接设置获得。
如图,三角面法线向上,所以三角面 俯视可见。 其中一个顶点法线向下,因此光照计算结果为黑。
- 计算切面三角顺序——三角法线
关键数值: 切面切割法线、切面三角法线(随意)、切割法线和 三角法线的点积值。
随便两个向量,先叉乘 ,再和一个从物体中心发设的向量计算。 (不论是案例,还是其他算法,切割模型的 算法都有局限性,都不能随意切割 任何模型。)
- 切割三角面确定三角的组合
案例是通过 非常多的判断来确定 哪种切割点组合成为新的三角面。
GL 使用
GL 类是图形API类。 Gizmo是在 开发阶段使用辅助图形,那么GL 就是 游戏实际运行时绘制 使用。
入门代码
public class GLTest : MonoBehaviour
{
// Start is called before the first frame update
public Material mat;
public Vector3[] vectList;
// // 相机的图片效果渲染 函数。用于后处理—— 需要挂在相机上才能调用
// private void OnRenderImage(RenderTexture src, RenderTexture dest) {
// }
//相机的 渲染函数,绘制一些图形渲染(有深度测试)—— 需要挂在相机上才能调用
private void OnPostRender() {
if (!mat) {
Debug.LogError("Please Assign a material on the inspector");
return;
}
GL.PushMatrix(); //将 相机使用的 矩阵保存下来
//GL.LoadOrtho(); //加载其他种类相机矩阵:正交投影矩阵、透视投影矩阵(需要自己构建矩阵值)……其他种类
// Ortho 矩阵,是归一化矩阵正交矩阵。右上角:(1,1).
//不写矩阵加载,使用 和相机当前设置的 “正交、透视” 有关。
mat.SetPass(0); // 设置本次GL 绘制的passs 通道。由于不能设置法线,因此注意使用 非光照 Shader 材质。
//GL.Color(Color.yellow); // 这里设置GL 颜色,但是没看到作用。
GL.Begin(GL.TRIANGLES); //GL 绘制的图形:三角、四边、线段……
// 设置顶点。 注意:三角面 的三角法线 遵循左手坐标系。
foreach (var item in vectList)
{
GL.Vertex(item);
}
GL.End(); //GL 图形绘制 结束,可以进行下个GL 图形绘制。
GL.PopMatrix();//将 相机使用的 矩阵 从保存中恢复
}
}