从单色纹理生成网格

时间:2018-12-26 18:02:39

标签: unity3d runtime png alpha-transparency vertices

我编写了一个代码,能够绘制并生成此绘制的sprite。这样我得到了一个带有白色背景和我的绘图的精灵(它的颜色不同)。

我的问题:如何在运行时删除白色背景?(使用C#代码)

我的问题是:我想使用图形生成网格,但是在白色背景下,我具有4个顶点(精灵),我想从我在精灵上绘制的真实形状中获取所有顶点(远远超过4个顶点)

我当前的想法是将图形转换为具有透明背景,然后使用unity的精灵打包程序从中生成网格。

我的项目:这是一个游戏,我们可以在其中创建自己的游戏电路:用户绘制黑白精灵—>我将其转换为带有对撞机的网格并生成新的游戏电路。

我已经很瘦了,可以清洗所有白色像素,但是我认为使用该技术不会得到很多顶点。

感谢您的帮助, 阿克塞尔 the real shape

4 个答案:

答案 0 :(得分:0)

一种方法是直接根据自己的条件生成网格。这样做的好处是,您可以非常精确地控制所需像素边界的外观,并且可以对网格进行自己的三角剖分,从而获得更好的信息。缺点是您必须自己做所有这一切。

一种实现方法是使用Marching Squares算法从像素数据生成等值带(您可以使用blue / green / alpha通道来获取等值,具体取决于背景是白色还是透明) ,然后从具有等值带一部分的2x2像素地面中的每个地面生成一块网格。

要从图像中获取像素数据,可以使用Texture2D.GetPixels。然后,您可以对该信息使用行进平方算法,以确定如何表示网格中的每个2x2像素簇。然后,您将使用该信息来找到代表该像素四边形的每个三角形的顶点。

将每个四边形像素转换为三角形后,将这些三角形的顶点排列成一个数组(请确保从可见面开始按顺时针方向排列每个三角形的顶点)并使用Mesh.SetVertices创建带有这些顶点的网格。

答案 1 :(得分:0)

另一种方法是将任何非红色像素的alpha设置为零,然后让Unity的精灵打包程序为您生成网格。

这是一种实现方法:

  1. 如果它是资产并且要对其进行修改,请将纹理资产设置为选中Read/Write enabled。如果纹理是在运行时创建的(因此不是资产),则可以跳过此步骤。

  2. 使用Texture2D.GetPixels获取像素数据。这将为您提供Color[] pixels形式的像素阵列:

    public Texture2D tex;  
    
    ...
    
    Color[] pixels = tex.GetPixels();
    
  3. 遍历每个索引,并用透明像素替换任意数量的蓝色像素(例如白色像素):

    for (int i = 0 ; i < pixels.Length ; i++ ) 
    {
        if (
            pixels[i].r != 1f 
            || pixels[i].g != 0f
            || pixels[i].b != 0f) 
            pixels[i] = Color.clear;
    }
    
  4. 使用修改后的像素数组设置纹理像素数据:

    tex.SetPixels(pixels);
    tex.Apply();
    

这种方法的缺点是我不知道是否可以使用Unity spritepacker将运行时创建的纹理打包到sprite地图集上。如果不能,那么此方法将需要使用其他工具在运行时从精灵生成网格。

答案 2 :(得分:0)

好吧,我做了点事情:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class scri : MonoBehaviour
{

    public Texture2D tex;  
    public Texture2D newText;  
    public Sprite sprite;
    public List<Color> colorList;

    private Sprite mySprite;
    private SpriteRenderer sr;

    // Start is called before the first frame update
    void Start()
    {
    sr = gameObject.AddComponent<SpriteRenderer>() as SpriteRenderer;
    newText =new Texture2D(tex.width,tex.height,TextureFormat.ARGB32, false);
    Color[] pixels = sprite.texture.GetPixels();
    for (int i = 0 ; i < pixels.Length ; i++ ) 
    { 
    Debug.Log(pixels[i]);
        if (pixels[i].r==1) {
            pixels[i] = Color.clear;
        }
    }
        newText.SetPixels(pixels);
        newText.Apply();
        mySprite = Sprite.Create(newText, new Rect(0.0f, 0.0f, newText.width, newText.height), new Vector2(0.5f, 0.5f), 100.0f);
        sr.sprite = mySprite;
    }

    // Update is called once per frame
    // void Update()
    // {
    //     Debug.Log(sprite.triangles.Length);
    //     Debug.Log(sprite.vertices.Length);
    // }
}

Usefull链接: https://forum.unity.com/threads/setting-pixel-to-transparent-turns-out-black.172375/ https://docs.unity3d.com/ScriptReference/Sprite.Create.html https://forum.unity.com/threads/is-it-possible-to-convert-a-texture2d-from-one-format-to-another-in-standalone-run-time.327141/ https://forum.unity.com/threads/texture-setpixels.431177/

但是我不知道为什么,如果png开头有白色背景,则效果不佳...: 使用svg可以,不需要我的代码就可以了。 enter image description here

但是在Sprite编辑器中,我可以生成自定义的物理形状:enter image description here

答案 3 :(得分:0)

using System.IO;
using UnityEngine.UI;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using UnityEngine.Networking;
public class scri : MonoBehaviour
{
// For saving the mesh------------------------ 
public KeyCode saveKey = KeyCode.F12;
public string saveName = "SavedMesh";

// Concerning mesher--------------------------
public GameObject mesher; //require

public List<Vector3> vertices;
public  List<int> triangles;

public Vector3 point0;
public Vector3 point1;
public Vector3 point2;
public Vector3 point3;

public int loop;
public float size;

public Mesh meshFilterMesh;
public Mesh meshColliderMesh;

// Sprite work
public Color[] pixels;

public Texture2D newTexture;  
public Texture2D oldTexture;  //require

private Sprite mySprite;
private SpriteRenderer spriteRenderer;

public int pathCount;

public GameObject displayerComponent; //require

public PolygonCollider2D polygonColliderAdded; //require

void Start()
{
    // Mesher
    vertices = new List<Vector3> (); 
    triangles = new List<int> (); 
    meshFilterMesh= mesher.GetComponent<MeshFilter>().mesh;
    meshColliderMesh= mesher.GetComponent<MeshCollider>().sharedMesh;
    size = 10; // lenght of the mesh in Z direction
    loop=0;

    // Sprite
    pixels = oldTexture.GetPixels();
    newTexture =new Texture2D(oldTexture.width,oldTexture.height,TextureFormat.ARGB32, false);
    spriteRenderer = gameObject.AddComponent<SpriteRenderer>();

    ConvertSpriteAndCreateCollider (pixels);
    BrowseColliderToCreateMesh (polygonColliderAdded);

}

void Update()
{
    // Save if F12 press
    if (Input.GetKeyDown(saveKey)){SaveAsset();}
}

public void ConvertSpriteAndCreateCollider (Color[] pixels) {
    for (int i = 0 ; i < pixels.Length ; i++ ) 
    { 
        // delete all black pixel (black is the circuit, white is the walls)
        if ((pixels[i].r==0 && pixels[i].g==0 && pixels[i].b==0 && pixels[i].a==1)) {
            pixels[i] = Color.clear;
        }
    }
    // Set a new texture with this pixel list
    newTexture.SetPixels(pixels);
    newTexture.Apply();

    // Create a sprite from this texture
    mySprite = Sprite.Create(newTexture, new Rect(0, 0, newTexture.width, newTexture.height), new Vector2(10.0f,10.0f), 10.0f, 0, SpriteMeshType.Tight,new Vector4(0,0,0,0),false);

    // Add it to our displayerComponent
    displayerComponent.GetComponent<SpriteRenderer>().sprite=mySprite;

    // Add the polygon collider to our displayer Component and get his path count
    polygonColliderAdded = displayerComponent.AddComponent<PolygonCollider2D>();

}

// Method to browse the collider and launch makemesh
public void BrowseColliderToCreateMesh (PolygonCollider2D polygonColliderAdded){
    //browse all path from collider
    pathCount=polygonColliderAdded.pathCount;
    for (int i = 0; i < pathCount; i++)
    {
        Vector2[] path = polygonColliderAdded.GetPath(i);

        // browse all path point
        for (int j = 1; j < path.Length; j++)
        {
            if (j != (path.Length - 1)) // if we aren't at the last point
            {
            point0 = new Vector3(path[j-1].x ,path[j-1].y ,0);
            point1 = new Vector3(path[j-1].x ,path[j-1].y ,size);
            point2 = new Vector3(path[j].x ,path[j].y ,size);
            point3 = new Vector3(path[j].x ,path[j].y ,0);
                MakeMesh(point0,point1,point2,point3);

            } 
            else if(j == (path.Length - 1))// if we are at the last point, we need to close the loop with the first point
            {
            point0 = new Vector3(path[j-1].x ,path[j-1].y ,0);
            point1 = new Vector3(path[j-1].x ,path[j-1].y ,size);
            point2 = new Vector3(path[j].x ,path[j].y ,size);
            point3 = new Vector3(path[j].x ,path[j].y ,0);
                MakeMesh(point0,point1,point2,point3);
            point0 = new Vector3(path[j].x ,path[j].y ,0);
            point1 = new Vector3(path[j].x ,path[j].y ,size);
            point2 = new Vector3(path[0].x ,path[0].y ,size); // First point
            point3 = new Vector3(path[0].x ,path[0].y ,0); // First point
                MakeMesh(point0,point1,point2,point3);
            }
        }
    }
}


//Method to generate 2 triangles mesh from the 4 points 0 1 2 3 and add it to the collider
public void MakeMesh (Vector3 point0,Vector3 point1,Vector3 point2, Vector3 point3){

    // Vertice add
    vertices.Add(point0);
    vertices.Add(point1);
    vertices.Add(point2);
    vertices.Add(point3);

    //Triangle order
    triangles.Add(0+loop*4);
    triangles.Add(2+loop*4);
    triangles.Add(1+loop*4);
    triangles.Add(0+loop*4);
    triangles.Add(3+loop*4);
    triangles.Add(2+loop*4);
    loop = loop + 1; 

    // create mesh 
    meshFilterMesh.vertices=vertices.ToArray();
    meshFilterMesh.triangles=triangles.ToArray();

    // add this mesh to the MeshCollider
    mesher.GetComponent<MeshCollider>().sharedMesh=meshFilterMesh;
}

// Save if F12 press
public void SaveAsset() 
{
    var mf = mesher.GetComponent<MeshFilter>();
    if (mf)
    {
        var savePath = "Assets/" + saveName + ".asset";
        Debug.Log("Saved Mesh to:" + savePath);
        AssetDatabase.CreateAsset(mf.mesh, savePath);
    }
}

}

enter image description here