如何从精灵创建四边形区域texture2d

问题描述

我有一个图像并将其作为可读/可写精灵添加到 unity 中。它是 32x32。但我不知道如何为顶点向量提供坐标,以便它给我一个与我的精灵具有相同形状的四边形区域。例如,如果我有一个 Heart.png 32x32 像素精灵,那么我想要一个来自四边形的心形区域

void Awake()
    {
        texture2D = sprite.texture;
        mesh = new Mesh();
        mf = GetComponent<MeshFilter>();
        

        for(int i=0; i< texture2D.width; i++)
        {
            for(int j=0; j< texture2D.height; j++)
            {

                Color color = texture2D.GetPixel(i,j);
                if(color.a == 0)
                {
                    continue;
                }

                vertices = new Vector3[] { new Vector3(),new Vector3(),new Vector3() };
                indeces = new int[]{ 0,1,2,3 };
                mesh.vertices = vertices;
                mesh.triangles = indeces;

            }
        }

        mf.sharedMesh = mesh;
    }

解决方法

其实这有点棘手。

我会做什么(因为它更容易实现 - 不是因为它更有效!)基本上是为每个像素创建一个具有相应颜色的小四边形(4 个顶点 -> 2 个三角形)。

[SerializeField] private Texture2D texture;
[SerializeField] private Mesh mesh;
[SerializeField] private MeshFilter mf;

[ContextMenu("TEST")]
private void Awake()
{
    // Just in case use the mesh format that allows more vertices
    
    if(!mf) mf = GetComponent<MeshFilter>();
    
    mesh = new Mesh {indexFormat = IndexFormat.UInt32};

    // Create these ONCE and fill them bit by bit
    var verticesList = new List<Vector3>();
    var trianglesList = new List<int>();
    var colors = new List<Color>();

    // Do this in ONE call -> more efficient
    var pixels = texture.GetPixels();

    // Place the pivot in the center of the texture
    var pivotOffset = -new Vector3(texture.width / 2f,texture.height / 2f);

    for (var i = 0; i < texture.width; i++)
    {
        for (var j = 0; j < texture.height; j++)
        {
            var color = pixels[i + j * texture.width];

            if (color.a == 0)
            {
                continue;
            }

            // Add the pixel color for the FOUR vertices we create
            colors.Add(color);
            colors.Add(color);
            colors.Add(color);
            colors.Add(color);

            // Create the four vertices around this pixel position
            var vertex0 = pivotOffset + new Vector3(i - 0.5f,j - 0.5f,0);
            var vertex1 = pivotOffset + new Vector3(i + 0.5f,0);
            var vertex2 = pivotOffset + new Vector3(i - 0.5f,j + 0.5f,0);
            var vertex3 = pivotOffset + new Vector3(i + 0.5f,0);

            // Store the current length of so far vertices => first new vertex index
            var currentIndex = verticesList.Count;

            // Add the vertices to the list
            verticesList.Add(vertex0);
            verticesList.Add(vertex1);
            verticesList.Add(vertex2);
            verticesList.Add(vertex3);

            // Two triangles for front side
            trianglesList.Add(currentIndex);
            trianglesList.Add(currentIndex + 2);
            trianglesList.Add(currentIndex + 3);

            trianglesList.Add(currentIndex);
            trianglesList.Add(currentIndex + 3);
            trianglesList.Add(currentIndex + 1);
            
            // For double sided add the inverted faces for the back as well
            trianglesList.Add(currentIndex);
            trianglesList.Add(currentIndex + 3);
            trianglesList.Add(currentIndex + 2);

            trianglesList.Add(currentIndex);
            trianglesList.Add(currentIndex + 1);
            trianglesList.Add(currentIndex + 3);
        }
    }

    // Finally apply them ONCE to the mesh
    mesh.vertices = verticesList.ToArray();
    mesh.triangles = trianglesList.ToArray();
    mesh.colors = colors.ToArray();

    mf.sharedMesh = mesh;
}

看起来像

enter image description here

如您所见,您可以玩转材料。通过将像素颜色存储到网格颜色中,您可以简单地使用支持顶点颜色的着色器。


进一步说明:

  1. GetPixels 返回一个平面的一维数组。在该阵列中,纹理的像素逐行水平排列。

    这意味着假设你有一个像

    这样的网格
     7 8 9 |
     4 5 6 |-height=3
     1 2 3 |
     _____
       |
     width=3
    

    变成

     1,2,3,4,5,6,7,8,9
    

    (Unity 纹理坐标从左下角从 0 开始)

    所以为了访问某个条目,例如位于像素坐标 6x=2,y=1 你必须去

     1,9
                   |
     x(=2) + y(=1) * width(=3)
    
  2. 如前所述,我的简单解决方案是不要像您尝试的那样为每个像素创建一个顶点,而是创建一个正方形。为此,我们需要像

    这样的顶点
     p-0.5x+0.5y        p+0.5x+0.5y
     V------------------V
     |                  |
     |                  |
     |        p         |
     | (actualPosition) |
     |                  |
     V------------------V
     p-0.5x-0.5y        p+0.5x-0.5y
    

    我希望很棒的 ascii 艺术让它以某种方式清晰:D

  3. 最后,Unity 中网格的 triangles 只是一个 int[],其中每个元素都指向 vertices 数组中的一个顶点索引,并且始终 3连续的索引形成一个三角形。

    在一个三角形内,顶点的缠绕顺序定义了它面向的方向。

    • 如果顶点按顺时针顺序排列,则法线朝向您。
    • 如果顶点按逆时针顺序排列,则法线背对您。

    参见例如Unity Manual - Creating a Quad,我希望这些图表能解释其余的内容

    enter image description here enter image description here

    在我的示例中,对于每个像素,我按顺序添加了一个由四个顶点组成的四边形

     2----3
     |  / |
     | /  |
     0----1
    

    然后我顺时针添加两个三角形

    • 0 2 3
    • 0 3 1

    逆时针(使它们双面)

    • 0 3 2
    • 0 1 3

    最后,因为我们不希望索引从 0 开始,而是希望在 currentIndex 之前的值相同

    • currentIndex+0 currentIndex+2 currentIndex+3
    • currentIndex+0 currentIndex+3 currentIndex+1

    • currentIndex+0 currentIndex+3 currentIndex+2
    • currentIndex+0 currentIndex+1 currentIndex+3