入门图形学之辅助工具是我个人对一些学习主线程之外的相关知识的单独分类讲解,因为有些知识在学习图形学过程中属于辅助程序知识,这些知识虽然和数学以及图形学知识并没有串联关系,但是大部分时候我们整个图形学学习中都会偶尔用到,所以我独立开出一个分类也就是“入门图形学之辅助工具”来讲解。
这篇我来讲一下rectangle长方形拓扑网格的创建,大部分时候,3d图形引擎提供的基础立体网格并不能完全达到我们学习的要求,就比如unity,我需要一个拓扑信息完好的rectangle长方形就没法提供了,此时我们只能使用图形api来代码创建一个。
首先我来解释一下mesh所包含的信息,也就是Vertices,Triangles,以及uvs,什么意思呢?看下图:
上图中正方形假设是我们要创建的mesh网格,那么我们创建这个网格,首先要知道四个顶点的坐标,也就是vertices,其次我们还要知道拓扑信息,也就是节点(或者说坐标点)之间的关联信息,比如图形学中网格三角面的三个顶点的逆时针顺时针拓扑信息,这个正方形网格包含两个三角面,那么三角面的拓扑信息就是(012),(023),这就是triangles。最后如果这个网格要承载一张贴图,那么贴图的每个顶点对应的网格的顶点是怎么样的呢?这个也就是uvs所记录的信息。
所以说一个网格需要包含着三种信息才能在图形程序中使用。那么unity要创建一个这种正方形具体怎么办呢?代码如下:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MeshSquare : MonoBehaviour
{
private Mesh mMesh;
void Start()
{
mMesh = new Mesh();
Vector3[] vertices = new Vector3[4] {
new Vector3(0,0,0),
new Vector3(1,0,0),
new Vector3(1,0,1),
new Vector3(0,0,1)
};
int[] triangles = new int[6] {
0,1,2,
0,2,3
};
Vector2[] uvs = new Vector2[4]
{
new Vector3(0, 0),
new Vector3(1, 0),
new Vector3(0, 1),
new Vector3(1, 1),
};
mMesh.vertices = vertices;
mMesh.triangles = triangles;
mMesh.uv = uvs;
GetComponent<MeshFilter>().sharedMesh = mMesh;
}
}
可以看得出代码就是上面讲的正方形创建方法,效果就是这样的:
绑定meshfilter和meshrenderer以及材质球后就能得到一个正方形了。
可惜啊可惜,实际上我们写图形程序中需要的大部分时候都是长方形,而不是这么规范的正方形,比如这个矩阵论书籍的封面就是600*820分辨率的,那么其实我们只需要修改vertices中的网格点坐标就达到目的了,比如这样:
改下顶点坐标就达到目的了。实际上这种方式在我们图形学计算中会导致很多问题,因为这种网格不是单位化的,也就是说网格的长宽比例不同,如果我们要是想用无数个单位正方形组成长方形该怎么办呢?下面我描绘一下长方形的拓扑信息,如下图:
这里我没有标明坐标和uv信息,因为坐标和uv只需要通过坐标系的xy进行for循环计算就得到了,可以根据需要任意轴向的正负计算,所以这里我只标明了长方形网格的拓扑三角信息,比如这时候就是(0,4,5),(0,5,1),(1,5,6),(1,6,2)...,这时候我们就来写程序吧,如下图:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[ExecuteInEditMode]
public class MeshRectangle : MonoBehaviour {
public bool Refresh = false;
[SerializeField]public int xCount = 60; //单位方块x轴数量
[SerializeField]public int yCount = 82; //单位方块y轴数量
private float mCellLen = 0.1f; //单位小方块边长
private Mesh mMesh;
void Start()
{
CreateMesh();
}
void Update()
{
if (Refresh)
{
CreateMesh();
Refresh = false;
}
}
private void CreateMesh()
{
//构建一个任意单位长宽的长方形
mMesh = new Mesh();
int xPointCount = xCount + 1; //x轴网格点数量
int yPointCount = yCount + 1; //y轴网格点数量
int xyMeshPointCount = xPointCount * yPointCount; //网格顶点的数量
int triangleCount = xCount * yCount * 2; //三角面数量(小正方形的两倍)
//构建mesh网格的所有信息数组
Vector3[] vertices = new Vector3[xyMeshPointCount];
int[] triangles = new int[triangleCount * 3];
Vector2[] uvs = new Vector2[xyMeshPointCount];
//记录拓扑信息循环的间隔
int triangleIndex = 0;
for (int x = 0; x < xPointCount; x++)
{
for (int y = 0; y < yPointCount; y++)
{
int index = x + y * xPointCount;
vertices[index] = new Vector3((xCount - x) * mCellLen, 0, (yCount - y) * mCellLen);
if (x < xCount && y < yCount)
{
//这里就是拓扑信息的循环计算,结合绘画的拓扑信息图算一下
triangles[triangleIndex] = x + y * xPointCount;
triangles[triangleIndex + 1] = x + (y + 1) * xPointCount;
triangles[triangleIndex + 2] = x + (y + 1) * xPointCount + 1;
triangles[triangleIndex + 3] = x + y * xPointCount;
triangles[triangleIndex + 4] = x + (y + 1) * xPointCount + 1;
triangles[triangleIndex + 5] = x + y * xPointCount + 1;
triangleIndex += 6;
}
uvs[index] = new Vector2((float)x / (float)xCount, (float)y / (float)yCount);
}
}
mMesh.vertices = vertices;
mMesh.triangles = triangles;
mMesh.uv = uvs;
GetComponent<MeshFilter>().sharedMesh = mMesh;
}
}
看下实际运行效果,如下图:
我们按照60:82的宽高比来绘制网格,这样矩阵论的封面网格就匹配了,顺便看下我们生成的网格,如下图:
生成的网格数量和三角面以及uv都是符合我们需求的。这种网格生成方式我们以后会用来制作计算机图形程序,如果没有这种需求,那么就按照我最开始生成四个顶点的网格方式生成节省资源会更好。