背景
我们通过代码动态创建的网格,因为没有法线,不会接收到光照。
正常情况下调用Mesh.RecalculateNormals方法,重新生成法线即可。
但特定情况下通过此方法计算出的顶点发现都是(0, 0,0),这种情况下只能手动生成法线了
如下图,左边物体有正确的法线,可以接收光照信息,右侧物体无法线,无法接收光照。
RecalculateNormals计算异常的原因
经测试发现导致Mesh.RecalculateNormals计算异常的情况:
如果Mesh中的某个顶点,在三角形标号数组中,即画了正面的网格,又画了反面的网格,则会导致RecalculateNormals计算错误,该点法线计算结果为(0, 0,0)。
解决方法1
对于双面网格,将顶点数组增加一倍,用于绘制反面的网格,分别放入Mesh的三角形标号数组中,以保证每个点法线计算正确。
解决方法2
用下方的生成顶点法线算法,手动计算期望的法线数组。
原理
法线:法线是指始终垂直于某平面的直线。在几何学中,法线指平面上垂直于曲线在某点的切线的一条线。
网格的法线数组:法线数组存放Mesh每个顶点的法线,数组大小与顶点坐标对应,normals[i]对应顶点vertices[i]的法线。
在 Max Wagner 的 《Generating Vertex Normals》文章 中写到——严格意义上讲,点是没有法线的。点的法线是在使用Phone或Gouraud模型时计算光照使用。如果一个面上的所有法线都一样,他们的光照也就一样,就会产生 flatness 效果;而如果把每个顶点的法向设置不同,则更平滑。
我们通过代码创建的网格一般都是多边形,所以给每个顶点赋值他所在的平面的法线即可。
生成顶点法线算法
创建网格,并设定如上图的八个点位,三角形序列,uv
Mesh mesh = new Mesh();
Vector3[] vertices = new Vector3[8];
vertices[0] = new Vector3(0,0,0);
vertices[1] = new Vector3(0,1,0);
vertices[2] = new Vector3(1,0,0);
vertices[3] = new Vector3(1,1,0);
vertices[4] = new Vector3(1,0,0);
vertices[5] = new Vector3(1,1,0);
vertices[6] = new Vector3(2,0,1);
vertices[7] = new Vector3(2,1,1);
mesh.vertices = vertices;
int[] triangles = new int[12];
triangles[0] = 0;
triangles[1] = 1;
triangles[2] = 3;
triangles[3] = 2;
triangles[4] = 0;
triangles[5] = 3;
triangles[6] = 4;
triangles[7] = 5;
triangles[8] = 7;
triangles[9] = 6;
triangles[10] = 4;
triangles[11] = 7;
mesh.triangles = triangles;
Vector2[] uv = new Vector2[8];
uv[0] = new Vector2(0,0);
uv[1] = new Vector2(0,1);
uv[2] = new Vector2(1,0);
uv[3] = new Vector2(1,1);
uv[4] = new Vector2(0,0);
uv[5] = new Vector2(0,1);
uv[6] = new Vector2(1,0);
uv[7] = new Vector2(1,1);
mesh.uv = uv;
生成法线数组
Vector3[] normals = new Vector3[8];
Vector3 v1 = Vector3.Cross(vertices[0] - vertices[1], vertices[0] - vertices[3]);
normals[0] = v1;
normals[1] = v1;
normals[2] = v1;
normals[3] = v1;
Vector3 v2 = Vector3.Cross(vertices[4] - vertices[5], vertices[4] - vertices[7]);
normals[4] = v2;
normals[5] = v2;
normals[6] = v2;
normals[7] = v2;
mesh.normals = normals;
//mesh.RecalculateNormals();
最终效果
文章来源:https://www.toymoban.com/news/detail-639941.html
(此案例中,使用Mesh.RecalculateNormal,也可生成正确法线。)文章来源地址https://www.toymoban.com/news/detail-639941.html
完整代码,增加了点位, 注释部分为增加了背部网格
public Mesh CreatMeshTest()
{
Mesh mesh = new Mesh();
Vector3[] vertices = new Vector3[12];
vertices[0] = new Vector3(0,0,0);
vertices[1] = new Vector3(0,1,0);
vertices[2] = new Vector3(1,0,0);
vertices[3] = new Vector3(1,1,0);
vertices[4] = new Vector3(1,0,0);
vertices[5] = new Vector3(1,1,0);
vertices[6] = new Vector3(0.5f,0,1);
vertices[7] = new Vector3(0.5f,1,1);
vertices[8] = new Vector3(0.5f,0,1);
vertices[9] = new Vector3(0.5f,1,1);
vertices[10] = new Vector3(0,0,0);
vertices[11] = new Vector3(0,1,0);
mesh.vertices = vertices;
int[] triangles = new int[18];
triangles[0] = 0;
triangles[1] = 1;
triangles[2] = 3;
triangles[3] = 2;
triangles[4] = 0;
triangles[5] = 3;
triangles[6] = 4;
triangles[7] = 5;
triangles[8] = 7;
triangles[9] = 6;
triangles[10] = 4;
triangles[11] = 7;
triangles[12] = 8;
triangles[13] = 9;
triangles[14] = 11;
triangles[15] = 10;
triangles[16] = 8;
triangles[17] = 11;
// triangles[18] = 0;
// triangles[19] = 3;
// triangles[20] = 1;
//
// triangles[21] = 2;
// triangles[22] = 3;
// triangles[23] = 0;
//
// triangles[24] = 4;
// triangles[25] = 7;
// triangles[26] = 5;
//
// triangles[27] = 6;
// triangles[28] = 7;
// triangles[29] = 4;
//
// triangles[30] = 8;
// triangles[31] = 11;
// triangles[32] = 9;
//
// triangles[33] = 10;
// triangles[34] = 11;
// triangles[35] = 8;
mesh.triangles = triangles;
Vector2[] uv = new Vector2[12];
uv[0] = new Vector2(0,0);
uv[1] = new Vector2(0,1);
uv[2] = new Vector2(1,0);
uv[3] = new Vector2(1,1);
uv[4] = new Vector2(0,0);
uv[5] = new Vector2(0,1);
uv[6] = new Vector2(1,0);
uv[7] = new Vector2(1,1);
uv[8] = new Vector2(0,0);
uv[9] = new Vector2(0,1);
uv[10] = new Vector2(1,0);
uv[11] = new Vector2(1,1);
mesh.uv = uv;
Vector3[] normals = new Vector3[12];
Vector3 v1 = Vector3.Cross(vertices[0] - vertices[1], vertices[0] - vertices[3]);
normals[0] = v1;
normals[1] = v1;
normals[2] = v1;
normals[3] = v1;
Vector3 v2 = Vector3.Cross(vertices[4] - vertices[5], vertices[4] - vertices[7]);
normals[4] = v2;
normals[5] = v2;
normals[6] = v2;
normals[7] = v2;
Vector3 v3 = Vector3.Cross(vertices[8] - vertices[9], vertices[8] - vertices[11]);
normals[8] = v3;
normals[9] = v3;
normals[10] = v3;
normals[11] = v3;
mesh.normals = normals;
//mesh.RecalculateNormals();
return mesh;
}
到了这里,关于【Unity】为网格生成顶点法线(Mesh.RecalculateNormals计算异常的解决方案)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!