网格切割(MeshCut)

2020-07-20  本文已影响0人  Qkuang

Unity学习一篇

<details>
<summary>通用快捷引导</summary>

主页目录
文集目录
</details>

目录

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 属性: 混合形状。用来做面部表情用。

切割案例分析(核心)

  1. 两向量,计算垂直向量。


    image
  2. 计算旋转

如图所示: 好久没动手,差点忘了,好险!


image
  1. 切割面思路。(案例中)
image

入射线碰撞,+ 世界 Up 向量 确定一个平面。在该平面内 ,求得 入射线 得垂向量。 —— 利用 上面得公式即可。
再点乘,求得切割法向量。

  1. 判断 三角面 点 是否被切割 图解:
image

求得黑色 “差向量”和“法向量” 点乘,通过 点乘值“0” 判断左右。

换种理解方式:以入射线、切割法线、垂线,建立一个坐标系。求得 顶点 在该坐标系的相对位置。…… 没有进行 线性变换 的坐标系转换,这样说也不妥。 反正,要心中右这个坐标轴。不然 千辛万苦求出来干嘛。

  1. 拷贝顶点。

只要切割 必定舍弃原来得 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]);

    }
  1. 切割算法核心——求切割点

如图


image

==关键点:没有,求不出来==。

  1. 平面几何和空间几何。
  1. 补全切面
    image

当我们算出所有切面上的切面点时。可以求出一个中心点,然后每两个切面点和 中心点构成一个三角面。——这里 保证 (0、1)(2、3)…… 这样的顺序,才不会出现交叉面

还需要求中心点的法线:直接就是 切面 切割法线的方向转化坐标系即可。

  1. 关于法线

这里区分一下:三角面法线、顶点法线。 三角面法线(自己命名)
三角面的法线:决定三角面是哪一个面被渲染可见。 只通过三角面的顶点顺序+叉乘+左手坐标系决定。
顶点法线:决定光照渲染的值,不决定是否被渲染可见。通过直接设置获得。

image

如图,三角面法线向上,所以三角面 俯视可见。 其中一个顶点法线向下,因此光照计算结果为黑。

  1. 计算切面三角顺序——三角法线
image

关键数值: 切面切割法线、切面三角法线(随意)、切割法线和 三角法线的点积值。

随便两个向量,先叉乘 ,再和一个从物体中心发设的向量计算。 (不论是案例,还是其他算法,切割模型的 算法都有局限性,都不能随意切割 任何模型。)

  1. 切割三角面确定三角的组合

案例是通过 非常多的判断来确定 哪种切割点组合成为新的三角面。

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();//将 相机使用的 矩阵 从保存中恢复

    }
    
}

上一篇下一篇

猜你喜欢

热点阅读