这篇教程说的非常清楚.
[RequireComponent(typeof(MeshFilter), typeof(MeshRenderer))] public class CodeMeshTriangle : MonoBehaviour { public Transform p0; public Transform p1; public Transform p2; private void Generate() { GetComponent<MeshFilter>().mesh = mMesh = new Mesh(); var vertices = new[] {p0.localPosition, p1.localPosition, p2.localPosition}; var triangles = new[] {0, 1, 2}; mMesh.vertices = vertices; mMesh.triangles = triangles; } }构建一个Mesh只需要两部分,一个是顶点坐标(vertices),一个是由顶点坐标构成的三角形(triangles)
需要注意就是其正反面,顺时针(clockwise)正面,逆时针(Counter-clockwise)反面
UV映射及Normal映射教程前半部分很好理解,比较让我困惑的是后半部分关于NormalMap中TBN(Tangent,Bitangent,Normal)的阐述.
NormalMap的含义首先要说的是什么是NormalMap,官网解说的非常清楚了.
普通Mesh
加入NormalMap
{% note success %}
each pixel in the texture of the normal map (called a texel) represents a deviation in surface normal direction away from the “true” surface normal of the flat (or smooth interpolated) polygon.
{% endnote %}
最让我不理解的是教程中关于切线(tangents)的解释. 他直接说到
As we have a flat surface, all tangents simply point in the same direction,which is to the right.
然后直接给出代码
Vector4 tangent = new Vector4(1f, 0f, 0f, -1f);让人不明所以,不知道他这个为啥直接就point了right.
不过搞清楚这点要有几个预备知识.
首先是UV映射. 可以看Y2B上的这个视频,
没有涉及到太底层的数学推导,感性的认知UVMaping就是
{% note success %}
给定UV中的一个三角形U(三个点分别为U1,U2,U3) => 通过某种方法映射到 => 模型中的三角形P(P1,P2,P3)
{% endnote %}
这个视频有开了一个头,但是也没具体讲. 不过具体的映射方法,应该是和线性插值有关,没有细研究
可以用线性插值,双线性插值,Trilinear,Bilinear 作为Key搜搜看.
不过重点是下面这张图
个人理解,不一定对. 无论UV映射也好,Normal映射也好. 都需要将贴图首先旋转到与需要映射的三角形相同的一个角度. 也就是对UV图,Normal图的坐标系有旋转
如图,左边的P'三角形 和右边的P三角形 就不在一个平面. 相应的做UV时候,其坐标轴也发生的旋转.
而这个旋转的角度 从某种角度来说就是TBN
原因可以看OpenGL的教程
选定Normal后
垂直与该Normal的向量有很多
为了方便计算,所以选择和和纹理空间对齐方向
结合我自己画的那个图,也就是说 纹理空间的方向就是P'三角形所在的那个坐标系. 这个坐标系也就是UV的坐标系,也是NormalMap的切线(Tangent)和副切线(Bitangent)方向.
至于这个坐标系的求出
给定UV中的一个三角形U(三个点分别为U1,U2,U3) => 通过某种方法映射到 => 模型中的三角形P(P1,P2,P3)
我是理解为,给出了UV上的三个点,然后给出模型上的三个点,Unity已经就帮我们算出来了.(通过数学推导肯定是可以算出来的,我没有细研究).
所以我理解的设置NormalMap流程应该是,设定好UV,然后给定Normal方向,最后通过Mesh.RecalculateTangents重新计算出TB两个方向,而不是反过来直接给定了Vector4 tangent = new Vector4(1f, 0f, 0f, -1f);
不过直接给定Tangent角度现在想想似乎也可以理解. 教程中的Mesh角度和我自己画的图中右侧的正方形一样,此时UV图面没有任何旋转,(1,0,0)也就是其x轴方向.
完整代码:
[RequireComponent(typeof(MeshFilter), typeof(MeshRenderer))] public class CodeMeshTriangle : MonoBehaviour { public Transform p0; public Transform p1; public Transform p2; private Mesh mMesh; private Vector3[] mVertices; private Vector3[] mNormals; private void Update() { Generate(); } private void Generate() { GetComponent<MeshFilter>().mesh = mMesh = new Mesh(); var normal = new Vector3(0, 0, -1); mVertices = new[] {p0.localPosition, p1.localPosition, p2.localPosition}; mNormals = new[] {normal, normal, normal}; var triangles = new[] {0, 1, 2}; Vector2[] uv = new Vector2[3]; uv[0] = new Vector2(0, 0); uv[1] = new Vector2(0, 1); uv[2] = new Vector2(1, 0); mMesh.vertices = mVertices; mMesh.triangles = triangles; mMesh.uv = uv; mMesh.normals = mNormals; mMesh.RecalculateTangents(); } }