首页 > 解决方案 > 如何从精灵创建四边形区域(texture2d)

问题描述

我有一个图像并将其作为可读/可写精灵添加到统一中。它是 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(), new Vector3() };
                indeces = new int[]{ 0, 1, 2, 2, 1, 3 };
                mesh.vertices = vertices;
                mesh.triangles = indeces;

            }
        }

        mf.sharedMesh = mesh;
    }

标签: unity3d2dtexture2d

解决方案


其实这有点棘手。

我会做什么(因为它更容易实现 - 不是因为它更有效!)基本上是为每个像素创建一个具有相应颜色的小四边形(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, j - 0.5f, 0);
            var vertex2 = pivotOffset + new Vector3(i - 0.5f, j + 0.5f, 0);
            var vertex3 = pivotOffset + new Vector3(i + 0.5f, j + 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;
}

好像

在此处输入图像描述

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


进一步说明:

  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开始)

    因此,为了访问某个条目6,例如位于像素坐标的条目,x=2, y=1您必须去

     1, 2, 3, 4, 5, 6, 7, 8, 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. 最后triangles,Unity 中的网格是简单的int[],其中每个元素都引用vertices数组中的一个顶点索引,并且总是3连续的索引形成一个三角形。

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

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

    请参阅Unity 手册 - 创建四边形,我希望这些图形能解释其余部分

    在此处输入图像描述 在此处输入图像描述

    在我的每个像素的示例中,我按顺序添加了四个顶点的四边形

     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

推荐阅读