在这篇教程中我们将利用顶点和三角面创建一个网格。
这篇博客的原文地址:http://catlikecoding.com/unity/tutorials/procedural-grid/ 博主翻译并非100%一字一句翻译,对内容进行部分的增删改。
So,什么是网格?概念上来说网格由图形硬件(GPU Graphics Processing Unit图形处理单元)构成来绘制复杂的材料/东西。它至少包含一组在3D空间中位置明确的点再加一组三角形
因为三角形是平坦的并且拥有直边,所以他们可以完美地被用来显示平坦的和连续的东西,像一个立方体的面孔。曲面的或者是圆的面只能被大量小的三角形来接近组成。如果三角面显示足够的小(不大于一个像素),那么你就不会感觉曲面或者是圆是由三角面组成的。从实时性能角度来讲通常这种情况是不可能的,所以我们总能够在面的某个程度上发现锯齿。
Unity自带的胶囊体,立方体和球体 着色 vs 线框
如何显示线框
- 再视图左上角选择Display Mode,前三个选项分别是Shaded(着色)、Wrieframe(线框)和Shaded Wrieframe着色并带着线框
为什么是一个材质球数组?
- 一个网格渲染器可以有多个材质球,它通常被用来渲染多组三角面,也成为子网格。它通常在外界导入的模型中使用,本篇文章不使用多个材质球。
using UnityEngine;
using System.Collections;
public class Grid : MonoBehaviour
{
public int xSize, ySize;
}
我们是需要引用System.Collections?
- 我们不需要它去生成我们的网格,但是我引用它因为我稍后将使用协程。
[RequireComponent(typeof(MeshFilter), typeof(MeshRenderer))]
public class Grid : MonoBehaviour
{
public int xSize, ySize;
}
private void Awake ()
{
Generate();
}
private Vector3[] vertices;
private void Generate ()
{
vertices = new Vector3[(xSize + 1) * (ySize + 1)];
}
private void OnDrawGizmos ()
{
Gizmos.color = Color.black;
for (int i = 0; i < vertices.Length; i++)
{
Gizmos.DrawSphere(vertices[i], 0.1f);
}
}
什么是Gizmos?
这时在编辑模式下会有一个Bug,因为OnDrawGizmos方法在编辑模式下被调用了,但此时我们的顶点数组为空。为防止这个错误发生,我们在OnDrawGizmos方法中添加一个顶点数组为空就跳出方法的操作。
private void OnDrawGizmos ()
{
if (vertices == null)
return;
…
}
private void Generate ()
{
vertices = new Vector3[(xSize + 1) * (ySize + 1)];
for (int i = 0, y = 0; y <= ySize; y++)
{
for (int x = 0; x <= xSize; x++, i++)
{
vertices[i] = new Vector3(x, y);
}
}
}
为什么在Gizmos绘制不能被移动?
现在我们可以看见这些顶点,但是他们生成的顺序我们不能明显地看出来。我们可以使用颜色来标识,但是我们也可以使用协程减慢这个过程的速度。这就是为什么我在这个脚本中引用了System.Collections命名空间。
private void Awake ()
{
StartCoroutine(Generate());
}
private IEnumerator Generate ()
{
WaitForSeconds wait = new WaitForSeconds(0.05f);
vertices = new Vector3[(xSize + 1) * (ySize + 1)];
for (int i = 0, y = 0; y <= ySize; y++)
{
for (int x = 0; x <= xSize; x++, i++)
{
vertices[i] = new Vector3(x, y);
yield return wait;
}
}
}
观察顶点逐个生成
private Mesh mesh;
private IEnumerator Generate ()
{
WaitForSeconds wait = new WaitForSeconds(0.05f);
GetComponent().mesh = mesh = new Mesh();
mesh.name = "Procedural Grid";
vertices = new Vector3[(xSize + 1) * (ySize + 1)];
…
mesh.vertices = vertices;
}
private IEnumerator Generate ()
{
…
int[] triangles = new int[3];
triangles[0] = 0;
triangles[1] = 1;
triangles[2] = 2;
mesh.triangles = triangles;
}
triangles[0] = 0;
triangles[1] = 1;
triangles[2] = xSize + 1;
triangles[0] = 0;
triangles[1] = xSize + 1;
triangles[2] = 1;
int[] triangles = new int[6];
triangles[0] = 0;
triangles[1] = xSize + 1;
triangles[2] = 1;
triangles[3] = 1;
triangles[4] = xSize + 1;
triangles[5] = xSize + 2;
triangles[0] = 0;
triangles[3] = triangles[2] = 1;
triangles[4] = triangles[1] = xSize + 1;
triangles[5] = xSize + 2;
int[] triangles = new int[xSize * 6];
for (int ti = 0, vi = 0, x = 0; x < xSize; x++, ti += 6, vi++)
{
triangles[ti] = vi;
triangles[ti + 3] = triangles[ti + 2] = vi + 1;
triangles[ti + 4] = triangles[ti + 1] = vi + xSize + 1;
triangles[ti + 5] = vi + xSize + 2;
yield return wait;
}
mesh.triangles = triangles;
yield return wait;
int[] triangles = new int[xSize * ySize * 6];
for (int ti = 0, vi = 0, y = 0; y < ySize; y++, vi++)
{
for (int x = 0; x < xSize; x++, ti += 6, vi++)
{
…
}
}
private void Awake ()
{
Generate();
}
private void Generate ()
{
GetComponent().mesh = mesh = new Mesh();
mesh.name = "Procedural Grid";
vertices = new Vector3[(xSize + 1) * (ySize + 1)];
for (int i = 0, y = 0; y <= ySize; y++) {
for (int x = 0; x <= xSize; x++, i++) {
vertices[i] = new Vector3(x, y);
}
}
mesh.vertices = vertices;
int[] triangles = new int[xSize * ySize * 6];
for (int ti = 0, vi = 0, y = 0; y < ySize; y++, vi++) {
for (int x = 0; x < xSize; x++, ti += 6, vi++) {
triangles[ti] = vi;
triangles[ti + 3] = triangles[ti + 2] = vi + 1;
triangles[ti + 4] = triangles[ti + 1] = vi + xSize + 1;
triangles[ti + 5] = vi + xSize + 2;
}
}
mesh.triangles = triangles;
}
我们的网格目前处于一种特殊的情况下。因为我们没有直到目前还没有给他们法线向量,默认的发现向量是(0,0,1)(垂直于屏幕向里),而我们需要的正好相反。
法线工作原理是什么呢?
法线用于规定每个顶点,所以我们必须填充另一个向量数组。另一种选择,我们可以依据网格的三角面来计算出法线。我们也可以偷懒,向下面这样做。
private void Generate ()
{
…
mesh.triangles = triangles;
// 网格自动计算法线向量
mesh.RecalculateNormals();
}
接下来是UV坐标。你可能注意到网格目前是颜色统一的,即便是我们给它赋值一个带反射贴图的材质球。这很容易理解,因为我们没有自行给它提供UV坐标,他们默认为(0,0)。
想要纹理适应我们的网格,简单地划分顶点的位置通过网格的大小。
vertices = new Vector3[(xSize + 1) * (ySize + 1)];
Vector2[] uv = new Vector2[vertices.Length];
for (int i = 0, y = 0; y <= ySize; y++)
{
for (int x = 0; x <= xSize; x++, i++)
{
vertices[i] = new Vector3(x, y);
uv[i] = new Vector2(x / xSize, y / ySize);
}
}
mesh.vertices = vertices;
mesh.uv = uv;
纹理现在显示了,但是它并没有覆盖整个网格。它的真实外观取决于纹理模式是Clamp模式或者是Repeat模式。产生这种现象是因为当前我们是通过整数划分的,UV坐标的计算结果是整数。为了得到正确的在0到1之间的坐标,我们必须使用浮点数。
uv[i] = new Vector2((float)x / xSize, (float)y / ySize);
目前应用这个材质球到我们的网格将不会产生任何的凹凸效果。我们需要给我们的网格添加切线向量。
切线的工作原理是什么?
当我们有一个平面,所有的切线仅仅指向相同的方向,是正确的。
vertices = new Vector3[(xSize + 1) * (ySize + 1)];
Vector2[] uv = new Vector2[vertices.Length];
Vector4[] tangents = new Vector4[vertices.Length];
Vector4 tangent = new Vector4(1f, 0f, 0f, -1f);
for (int i = 0, y = 0; y <= ySize; y++)
{
for (int x = 0; x <= xSize; x++, i++)
{
vertices[i] = new Vector3(x, y);
uv[i] = new Vector2((float)x / xSize, (float)y / ySize);
tangents[i] = tangent;
}
}
mesh.vertices = vertices;
mesh.uv = uv;
mesh.tangents = tangents;
这篇博客的原文地址:http://catlikecoding.com/unity/tutorials/procedural-grid/ 博主翻译并非100%一字一句翻译,对内容进行部分的增删改。
- Unity自定义UI组件(十二) 条形图篇
- Unity自定义UI组件(十一) 雷达图、属性图
- Unity自定义UI组件(十) 折线图
- Unity自定义UI组件(九) 颜色拾取器(下)
- Unity自定义UI组件(八) 颜色拾取器(上)
- Unity自定义UI组件(七)渐变工具、渐变色图片、渐变遮罩
- Unity自定义UI组件(六)日历、日期拾取器
- Unity自定义组件之(五) 目录树 UITree
- Unity自定义UI组件(四)双击按钮、长按按钮
- Unity自定义UI组件(三)饼图篇
- Unity自定义UI组件(二)函数图篇(下)
- Unity自定义UI组件(一)函数图篇(上)
- [Unity]PureMVC框架解读(下)
- [Unity]PureMVC框架解读(上)
- Github :https://github.com/ll4080333/UnityCodes
- CSDN : http://blog.csdn.net/qq_29579137
- 博客专栏 : http://blog.csdn.net/column/details/16329.html
- QQ群 : 593906968 有什么不懂的可以加群咨询互相学习
如果你想了解UGUI的更多拓展组件,欢迎关注我的博客,我会持续更新,支持一下我这个博客新手,你的关注也会给予我更多的动力。如果以上文章对你有帮助,点个赞,让更多的人看到这篇文章,我们一起学习。如果有什么指点的地方欢迎在评论区留言,秒回复。