介绍
本文接上一遍Unity网格篇Mesh(二)
4.生成额外的顶点数据
- 我们的网格目前处于一种特殊的情况下。因为我们到目前为止还没有给他们法线向量,默认的法线向量是(0,0,1)(垂直于屏幕向里),而我们需要的正好相反。
- 法线工作原理是什么呢?
- 法线是垂直于面的向量。我们通常使用单位长度的法向量,并向量指向面的外部,而不是内部。
- 法线可以用于确定光线与顶点的夹角。这个细节的使用取决于Shader。
- 作为三角面它永远是平的,因此它不应该需要被提供一个单独的法线信息。然而,我们需要造假。在现实中,顶点是不存在法线的,三角面才有。通过附加自定义顶点法线和三角面插值,我们可以假装有一个平滑的曲面代替一堆平的三角面。这个错觉是令人信服的,只要你不去注意网格锋利的轮廓(锯齿)。
- 法线用于规定每个顶点,所以我们必须填充另一个向量数组。另一种选择,我们可以依据网格的三家面来计算出法线。我们可以偷懒,向下面这样做。
private void Generate ()
{
…
mesh.triangles = triangles;
mesh.RecalculateNormals();
}
- 法线是如何重新计算的?
- Mesh.RecalculateNormals方法利用与顶点相连的三角面计算出每一个顶点的法线。计算平面三角形法线平均值,然后使用normalize方法单位化。
未计算法线
计算法线
没有法线vs有法线
- 接下来是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;
错误的UV坐标Clamping vs warpping
纹理现在显示了,但是他并没有覆盖整个网格。它的真实外观取决于纹理模式是Clamp模式或者是Repeat模式。产生这种现象是因为当前我们是通过整数划分的,UV坐标的计算结果是整数。为了得到正确的在0到1之间的坐标,我们必须要使用浮点数。
uv[i] = new Vector2((float)x / xSize, (float)y / ySize);
- 纹理现在被投影到了整个纹理上。当我设置网格尺寸为(5,10),这个网格纹理将会出现横向拉伸,可以自动反向适应材质的纹理的平铺设置。设置X的坐标为(2,1)将产生双倍。如果把纹理模式设置为Repeat模式,我们可以看到四个方瓷砖网格。
正确的UV纹理,平铺(1,1) vs 平铺(2,1)
- 另外一个表面细节的方法是使用法线贴图。这个贴图使用颜色值记录了法线向量。应用这个纹理到网格上将产生更多的灯光细节效果,它是单独使用顶点法线产生的。
凹凸不平的表面,产生了金属材质的效果
- 目前应用这个材质球到我们的网格将不会产生任何的凹凸效果。我们需要给我们的网格添加切线向量。
- 切线的工作原理是什么?
- 法线贴图在切线空间中定义。这是一个流动在物理表面的3D空间。这个方法使我们能够使用相同的法线贴图在不同的空间和方向。
- 表面法相用于描述空间中的向上方向。但是那个方向是正确的?它是由切线决定的。理论上,法线和切线的夹角是成90度的。两者的叉积可求出三维空间的第三个方向。但是现实中这个结果并不是90度,但是效果依然是比较满意的。
- 所以切线是一个三维向量,但是在Unity中它是使用思维向量定义的。第四个值通常是1或者-1,用于控制第三切线空间维度方向朝前或朝后,这有助于展示法线贴图,通常用于左右对称的3D模型,像人一样。Unity的shader执行此计算要求我们使用-1。
- 当我们有一个平面,所有的切线仅仅指向相同的方向,是正确的的。
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;
一个平面被伪装成凹凸不平的面
- 现在你知道如何创建一个简单的网格并使得它在使用材质球的情况下看起来更加复杂。网格需要顶点,三角面,通常还需要UV坐标,经常也需要法线和切线。你也可以添加顶点颜色,尽管Unity标准着色器不使用这个属性。但是你可以自己创建Shader去使用这个颜色属性。