Skip to main content
ARShow
ARShow
 首页 » 资源教程 » Unity3D教程

UnityC#教程:立方球体圆润度优化

2016年09月26日 19:07:3210130蛮牛网

把立方体变成一个球

以球的形式显示在unity中

严格检查转换

用数学提出一个最好的解决办法

在本教程中,我们将基于立方体创建一个球体网格,然后用数学推理去改善它。

本教程是圆形的立方体,它可以在unity5.0.1及其以上版本运行

UnityC#教程:立方球体圆润度优化 Unity3D教程 第1张

从一个形状延伸为另一个形状

1.采用圆形的立方体

你可以已经注意到,你可以创建以一个完美的球形立方体,确保它早三个维度中有相同的大小,并将大小设置为圆形大小的一半。

如果你仅仅想要一个球体,那么提供的具有灵活性的立方体只会起到阻碍作用。所以让我们给它创建一个单独的组建,复制RoundedCube 脚本并将它重命名为CubeSphere。然后用单个的girdSize取代三种尺寸并移除roundness的字段。

[C#] 纯文本查看 复制代码

?

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
public class CubeSphere : MonoBehaviour {
        public int gridSize;
        // No more roundness.
        private Mesh mesh;
        private Vector3[] vertices;
        private Vector3[] normals;
        private Color32[] cubeUV;
        
        private void Generate () {
                GetComponent<MeshFilter>().mesh = mesh = new Mesh();
                mesh.name = "Procedural Sphere";
                CreateVertices();
                CreateTriangles();
                CreateColliders();
        }
        
}

你如将三个大小字段替换为一个?

最方便的方法是使用编辑器去重构变量名。这确保了所有的引用都是重命名的,第一个重命名是将 xSize 变成gridSize,接下来是将ySize 和 zSize都进行重命名,与此同时,执行多个索搜和替换操作。

你会从gridSize + gridSize得到一些代码的片段,你可以通过重写去表述它,如果不是真的有必要我不喜欢麻烦去写。

删除roundness 字段将会导致错误,因为它仍然在一些地方使用,首先让我们看看碰撞器的创建。

我们需要多个盒子和胶囊碰撞器去代表立方球体,但是球一般只需要单个的球形碰撞器。这意味着我们可以删除AddBoxCollider 和AddCapsuleCollider方法,然后将CreateColliders 方法减少到只需要一行代码。

[C#] 纯文本查看 复制代码

?

1
2
private void CreateColliders () {
[/size][/font][font=微软雅黑][size=3]                gameObject.AddComponent<SphereCollider>();
[/size][size=3]        }

接下来是定点的位置,同样依赖于roundness

我们通过一个从角落内部指向原始的立方体的某些地方的普通向量,从而形成立方体圆度的角落。当我们转换成一个球体的时候,我们然需要这样做,其他情况下则会退化。

如果我们把球集中在它的本地原点的时候,也是很方便的。我们做网格和立方体的圆度的时候总是懒得去这样。所以我们直接让它们在中心。

创建一个单位球——球体的半径为一个单位——我们需要规范一点去做,让立方体的顶点集中在原点,为了立方体能完全包含球体,因此需要使立方体的边缘长度为2.

UnityC#教程:立方球体圆润度优化 Unity3D教程 第2张

符合边缘长度为2的单位圆。

我们可以通过除以原始坐标网格大小的方式,达到在立方体的SetVertex上创建顶点的目的,翻一倍,然后再减掉一个。

[C#] 纯文本查看 复制代码

?

1
2
3
4
5
6
7
public float radius = 1;
        private void SetVertex (int i, int x, int y, int z) {
                Vector3 v = new Vector3(x, y, z) * 2f / gridSize - Vector3.one;
                normals = v.normalized;
                vertices = normals;
                cubeUV = new Color32((byte)x, (byte)y, (byte)z, 0);
        }

这样产生一个单位球,如果你想要不同的半径的话,让我们按下述方法去做

[C#] 纯文本查看 复制代码

?

1
2
3
4
5
6
public float radius = 1f;
private void SetVertex (int i, int x, int y, int z) {
                Vector3 v = new Vector3(x, y, z) * 2f / gridSize - Vector3.one;
                normals[/font][i][font=微软雅黑] = v.normalized;
                vertices[/font][i][font=微软雅黑] = normals[/font][i][font=微软雅黑] * radius;
                cubeUV[/font][i][font=微软雅黑] = new Color32((byte)x, (byte)y, (byte)z, 0);
        }

现在所有的错误都应该清理了,让我们在场景中放入一个立方球体。我们不需要弄它或者替换一个立方体对象的圆度组件。

UnityC#教程:立方球体圆润度优化 Unity3D教程 第3张

形成一个立方球体

2.调查映射的结果

我们有一个创建球体网格的方法,但是他有多么好呢?让我们从多观点去观察球体,网格是如何统一的?

UnityC#教程:立方球体圆润度优化 Unity3D教程 第4张

不同的观点,视角和 拼写

网格单元格的大小各不相同 。最大的单元格 来自于中间的立方体表面。它们大约是立方体角落的最小的单元格的4倍。

明白为什么是这样了吧,它让我们更形想地看到如何从球映射到圆。我们将会使用一个圆,因为这比使用一个球体更容易,它只会是一个单片的问题。

我们可以在unity中创建一个可视化的小玩意。我们将在OnDrawGizmosSelected方法中使用一个自定义组件。它除了gizmos当对象被选中的时候会被画,其它的时候工作原理就和OnDrawGizmos一样。用这个方法我们可以添加一个有可视化对象的场景,除了我们选择它的情况其余都不可见。

[C#] 纯文本查看 复制代码

?

1
2
3
4
5
6
using UnityEngine;
public class CircleGizmo : MonoBehaviour {
        private void OnDrawGizmosSelected () {
        }
}

我们通过展示4边形顶点边缘的黑色球体来开始。无论怎么样我都喜欢我的解决方案,让我们从顶部到底部边缘开始吧。

[C#] 纯文本查看 复制代码

?

01
02
03
04
05
06
07
08
09
10
11
12
13
14
public int resolution = 10;
        private void OnDrawGizmosSelected () {
                float step = 2f / resolution;
                for (int i = 0; i <= resolution; i++) {
                        ShowPoint(i * step - 1f, -1f);
                        ShowPoint(i * step - 1f, 1f);
                }
        }
        private void ShowPoint (float x, float y) {
                Vector2 square = new Vector2(x, y);
                Gizmos.color = Color.black;
                Gizmos.DrawSphere(square, 0.025f);
        }

UnityC#教程:立方球体圆润度优化 Unity3D教程 第5张

完整的填写左边和右边的四方块,随着角落的显现,现在我们可以跳过它们了。

[C#] 纯文本查看 复制代码

?

01
02
03
04
05
06
07
08
09
10
private void OnDrawGizmosSelected () {
                float step = 2f / resolution;
                for (int i = 0; i <= resolution; i++) {
                        ShowPoint(i * step - 1f, -1f);
                        ShowPoint(i * step - 1f, 1f);
                }
                for (int i = 1; i < resolution; i++) {
                        ShowPoint(-1f, i * step - 1f);
                        ShowPoint(1f, i * step - 1f);
                }
        }

UnityC#教程:立方球体圆润度优化 Unity3D教程 第6张

                       完整的方格

接下来我们将显示每个点所相对应的顶点,使用白色球体,然后我们显示了俩个顶点和黄线之间的关系,最后一个灰色线是从圆的顶点到中心的。

[C#] 纯文本查看 复制代码

?

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
private void ShowPoint (float x, float y) {
                Vector2 square = new Vector2(x, y);
                Vector2 circle = square.normalized;
                Gizmos.color = Color.black;
                Gizmos.DrawSphere(square, 0.025f);
                Gizmos.color = Color.white;
                Gizmos.DrawSphere(circle, 0.025f);
                Gizmos.color = Color.yellow;
                Gizmos.DrawLine(square, circle);
                Gizmos.color = Color.gray;
                Gizmos.DrawLine(circle, Vector2.zero);
        }

UnityC#教程:立方球体圆润度优化 Unity3D教程 第7张

                   从方块到圆形

现在我们看看映射如何工作。正常的话是把顶点直线拉伸链接到中心,直到他们到达单位圆,接近方块的角落处比在主轴的地方放更多的顶点,顶点在主轴上尽量不移动,当顶点在对角线的单位时被移动。

随着所有的点被指向同一个点。使他们最终都会很接近。这就是为什么靠近对角线的单元格特别小。

3.用数学方法去做

现在我们终于知道为什么单元格最后呈现出不同的大小。我们根据这做一些什么呢?或许会有不同的映射产生更加均匀的单元格,因此,我们该如何找到这样的一个映射呢?

这真的是一个数学问题,让我们更加正式的去描述我们当前的映射吧。在这一次开始时我们只关心我们的圈,假设我们以后可以推广到别的领域。

所以我们的映射点是从方块指向圆,用向量去描述点,我们真的可以把一个向量映射到另一个上。

vs→vc

具体的说我们的圈是一个单位圆,被放在边长为2 的正方形的边缘,因此我们的映射是标准且简单的向量VS。

vc=ˆvs

通过除以自己的长度去的到一个正常的向量

ˆvs=vs||vs||

你如何得到一个二维向量的长度?它有两个坐标。

vs=[xy]

这些坐标定义一个直角三角形,你也可以用勾股定理。

UnityC#教程:立方球体圆润度优化 Unity3D教程 第8张

现在我们可以用最明确的形式去编写映射。

UnityC#教程:立方球体圆润度优化 Unity3D教程 第9张

这非常好,但实际上我们可以证明Vc在单位圆上定义了一个点吗?像这样做的话,它的长度必须是1,所以我们必须证明下列的等式成立。

UnityC#教程:立方球体圆润度优化 Unity3D教程 第10张

这操作很难,所以让它们先达到一致。

UnityC#教程:立方球体圆润度优化 Unity3D教程 第11张

把它简化成微不足道的是很容易的。

UnityC#教程:立方球体圆润度优化 Unity3D教程 第12张

最后表达式的分子和分母必须是相等的,结果必须是1。也就是说,只有在X2+Y2结果是0的情况下,结果才能没有意义。但是我们只是用我们在方块边缘我们输入的点,并且还要保证一个或者两个坐标是1或者-1,才能保证

UnityC#教程:立方球体圆润度优化 Unity3D教程 第13张

所以我们无法得到一个有意义的结果。

这个证明有用吗?他告诉我们,再我们方块的每一个点有一个收益率为1 的公式。这个公式符合从方块映射到单位圆。

我们可以找到不同的公式去做同样的事情吗?如果这样,必须有一个不同的映射。让我们逆转这个过程,在我们的方块上每一个点提出一个公式收益率为一。这个函数会是什么样子呢,如果我们尽可能使它简单吗?我们至少知道有一个坐标总是为-1或者1,我们的方块有两个坐标,我们可以得到至少有一个为1

,至少其中一个总是0,如果他们相乘的话那么肯定可以保证为0,这是我们给出的公式。

UnityC#教程:立方球体圆润度优化 Unity3D教程 第14张

为什么要减去一而不是加上呢?我们也可以使用

UnityC#教程:立方球体圆润度优化 Unity3D教程 第15张

两者都有效。减少的话在这种情况下更直观一些,因为这个公式起初收益率为0和收益率为更大的结果时都会远离原点,当我们从写的时候还可以让我们消除1,这真的很方便。   

这个公式可以用简单的形式重写。

UnityC#教程:立方球体圆润度优化 Unity3D教程 第16张

你该如何重写呢?

这有两个中间步骤

UnityC#教程:立方球体圆润度优化 Unity3D教程 第17张

在最终结果是-1.

现在我们有一个新的定义平方Vc长度的方法。

让我们重新整理方程右边的a + b。x2,y2部分是显而易见的,但如何处理 

UnityC#教程:立方球体圆润度优化 Unity3D教程 第18张

   部分?让事情尽可能对称是有意义的,所以把它相等对待。

UnityC#教程:立方球体圆润度优化 Unity3D教程 第19张

我们现在可以把这分为两个坐标

UnityC#教程:立方球体圆润度优化 Unity3D教程 第20张

我们可以把不同的进行分离,但这种方式最好用于的分布在圆上的点。现在我们只是远离最后一个向量的一个平方根。

UnityC#教程:立方球体圆润度优化 Unity3D教程 第21张

如何进行重写?

我们刚发现它为我们提供了一个新的映射,从方块到圆。让我们试一下ShowPoint。

[C#] 纯文本查看 复制代码

?

1
2
Vector2 circle;
                circle.x = square.x * Mathf.Sqrt(1f - square.y * square.y * 0.5f);
                circle.y = square.y * Mathf.Sqrt(1f - square.x * square.x * 0.5f);

UnityC#教程:立方球体圆润度优化 Unity3D教程 第22张

事实证明,这种映射将使带你远离对角线并且转向轴,他们最近的就是在轴附近。幸运的是,这样得到的点比第一这种方法得到的点更加统一,这是一个进步的方法。

4.调整映射

我们有从方块到圆的新映射,但是从立方体到圆的话呢?我们可以用同样的方法吗?是的,但是我们需要第三个坐标,就像我们已有的两个一样。这种方法我们可以得到单位圆由圆指向点的向量的长度。

UnityC#教程:立方球体圆润度优化 Unity3D教程 第23张

的确,这试用于任何数量的坐标,我们可以在任何超立方体映射到任何超球面的维度,公式扩产变得更加复杂。

UnityC#教程:立方球体圆润度优化 Unity3D教程 第24张

让我们用这个SetVertex看看它是什么样子!

[C#] 纯文本查看 复制代码

?

01
02
03
04
05
06
07
08
09
10
11
12
private void SetVertex (int i, int x, int y, int z) {
                Vector3 v = new Vector3(x, y, z) * 2f / gridSize - Vector3.one;
                float x2 = v.x * v.x;
                float y2 = v.y * v.y;
                float z2 = v.z * v.z;
                Vector3 s;
                s.x = v.x * Mathf.Sqrt(1f - y2 / 2f - z2 / 2f + y2 * z2 / 3f);
                s.y = v.y * Mathf.Sqrt(1f - x2 / 2f - z2 / 2f + x2 * z2 / 3f);
                s.z = v.z * Mathf.Sqrt(1f - x2 / 2f - y2 / 2f + x2 * y2 / 3f);
                normals = s;
                vertices = normals * radius;
                cubeUV = new Color32((byte)x, (byte)y, (byte)z, 0);
        }

UnityC#教程:立方球体圆润度优化 Unity3D教程 第25张

一个更统一的映射

网格细胞会变得更加扭曲从而使他们接近对角线,没有任何别的办法。但这个新的映射产生细胞比归一化方法有更统一的大小。细胞在轴和立方体角落似乎现在大致有相同的大小。这比我们开始的时候更加好的!最大的细胞正在沿着立方体的边缘排列。那些习惯于被压扁,但现在他们被拉伸。

你是如何想出这样的数学的?

在这里提出一个逻辑顺序,指出了用自己的方法去做总是有一个不稳定的路径。当好的机会出现时跟随一个清晰的逻辑路径是对的,但直觉通常只有在试验之后

。在这之前,研究别人已经找到的东西。

互联网拥有很多知识,利用它!在这种情况下,立方体球体映射被菲利普·阿普描述在他的博客上。在直觉背后,映射的答案来自数学堆栈交换问题。

如果你现在觉得给的这个漂亮的球是一个利用挤压好的教程的话,那么你可以继续网格变形教程。

评论列表暂无评论
发表评论