UV坐標

UV 是一種紋理坐標系統,與物體的空間坐標 (x, y, z) 屬於不同的坐標系。 UV 座標是用來映射紋理到物體表面,它與物體坐標之間存在映射關係,但這種關係不一定是線性或唯一的。

  • (x, y, z) 是世界或局部空間座標,表示物體在 3D 空間中的位置(Transform 座標),使用笛卡兒坐標系
  • (U, V) 是 2D 紋理座標,通常在 0~1 範圍內,用來對應貼圖的像素

假設紋理大小是 512x512 但我們不會用 512x512 去做映射,因為 UV 座標的範圍是 0 到 1 之間,所以 512x512 大小的圖片就會被轉換為 uv(0/512,0/512) 至 (512/512,512/512)。這麼做的話即使你的紋理不是 512x512,而是 1024x1024 或其他大小,UV 仍然適用,因為它們代表的是 相對位置,而不是像素座標。

當我們想要檢查 Mesh 的頂點索引、 UV 座標,或三角形組成方式,我們可以使用 MeshFilter 來取得 Mesh

  • 使用 mesh.triangles 可以查看三角形索引
    • mesh.triangles 是一個 整數陣列,每 3 個數字代表一個三角形的 3 個頂點索引。
    • 這些索引對應到 mesh.vertices 陣列中的頂點。
  • 使用 mesh.vertices 找到 頂點的 3D 位置
    • mesh.vertices 是一個 Vector3[] 陣列,存放 Mesh 的每個頂點在 世界空間中的 3D 座標。
    • 這些頂點的 順序對應 mesh.triangles 的索引
  • 使用 mesh.uv 來查看 UV 座標
    • mesh.uv 是一個 Vector2[] 陣列,存放 Mesh 每個頂點對應的 UV 座標。
    • UV 是 貼圖座標,範圍通常是 (0,0) 到 (1,1),但如果是 貼圖 Atlas 或特殊映射方式,可能會超出這個範圍。

接下來我們建立一個 Quad 並設定他的 Materail 試試看。

  1. 建立一個 512x512 的圖片
  2. 建立一個 Material , 將其設為 Unlit/Texture ,在把 512x512 的圖片放到這個 Material 中。
  3. 建立一個 Quad , 並將剛剛建立的 Material 賦給這個 Quad
    • Unity 中 Quad(矩形)通常有 4 個頂點,在 Unity 內部,它其實是由兩個三角形組成的。
  4. 在 Quad GameObject 上建立一個 Script
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    public class Test : MonoBehaviour
    {

    Mesh mesh;
    public Vector2[] uvs;

    private void Awake()
    {
    uvs = new Vector2[4];

    uvs[0] = new Vector2(0f, 0.75f); // 左下
    uvs[1] = new Vector2(0.25f, 0.75f); // 右下
    uvs[2] = new Vector2(0f, 1); // 左上
    uvs[3] = new Vector2(0.25f, 1); // 右上

    mesh = GetComponent<MeshFilter>().mesh;

    for (int i = 0; i < mesh.vertexCount; i++)
    {
    Debug.Log($"頂點索引 {i} 的 3D 位置: {mesh.vertices[i]}");
    }

    for (int i = 0; i < mesh.triangles.Length; i += 3)
    {
    Debug.Log($"三角形 {i / 3}: {mesh.triangles[i]}, {mesh.triangles[i + 1]}, {mesh.triangles[i + 2]}");
    }
    }


    private void LateUpdate()
    {

    mesh.SetUVs(0, uvs);
    }

    }
  5. 執行,會發現顯示的是紅色1的區塊,這是因為我們設定了以下 UV 座標,對照下圖可以清楚看到 (0,0.75) , (0.25,0.75) , (0,1) , (0.25,1) 就是 紅色1 的區塊。
    • uvs[0] = new Vector2(0f, 0.75f); // 左下
    • uvs[1] = new Vector2(0.25f, 0.75f); // 右下
    • uvs[2] = new Vector2(0f, 1); // 左上
    • uvs[3] = new Vector2(0.25f, 1); // 右上
  6. 使用 mesh.triangles 可以查看三角形索引
    1
    2
    3
    4
    for (int i = 0; i < mesh.triangles.Length; i += 3)
    {
    Debug.Log($"三角形 {i / 3}: {mesh.triangles[i]}, {mesh.triangles[i + 1]}, {mesh.triangles[i + 2]}");
    }
    會印出
  • 三角形 0: 0, 3, 1
  • 三角形 1: 3, 0, 2
    這表示有兩個三角形,
  • 第一個三角形 0 的頂點索引是 0, 3, 1
  • 第二個三角形 0 的頂點索引是 3, 0, 2
  1. 之後我們可以使用 mesh.vertices 找到 頂點的 3D 位置
    1
    2
    3
    4
    for (int i = 0; i < mesh.vertexCount; i++)
    {
    Debug.Log($"頂點索引 {i} 的 3D 位置: {mesh.vertices[i]}");
    }
    會印出
  • 頂點索引 0 的 3D 位置: (-0.50, -0.50, 0.00)
  • 頂點索引 1 的 3D 位置: (0.50, -0.50, 0.00)
  • 頂點索引 2 的 3D 位置: (-0.50, 0.50, 0.00)
  • 頂點索引 3 的 3D 位置: (0.50, 0.50, 0.00)
  1. 將 Quad 放到坐標系上看就很清楚了

參考


Power 對 UV 的影響
當你對 UV 使用 Power,會導致貼圖的變形,因為 UV 不是線性的了。
舉例:UV 使用 Power

UV.xy → Power(UV.xy, 2)
Power(UV, 1) 👉 不變
Power(UV, 2) 👉 使貼圖集中在 (0,0),朝右上角壓縮

  • (0.5,0.2) 經過 Power(UV, 2) 之後,變為 (0.025,0.04)
    Power(UV, 0.5) 👉 擴展貼圖,使細節集中在 (1,1)

評論