Создать сетку из одноцветной текстуры - PullRequest
0 голосов
/ 26 декабря 2018

Я делаю код, чтобы можно было рисовать и генерировать спрайт этого рисунка.Таким образом, я получаю спрайт с белым фоном и мой рисунок (который другого цвета).

Мой вопрос : Как я могу удалить белый фон во время выполнения? (С кодом C #)

Моя проблема : я хочу сгенерированную сетку с использованием чертежа, но на белом фоне у меня есть 4 вершины (четвертые углыспрайта) и я хочу получить все вершины из реальной фигуры, которую я рисую на своем спрайте (намного больше, чем 4 вершины)

Моя текущая идея - преобразовать рисунок в прозрачный фон и затем использоватьУпаковщик спрайтов в Unity для создания меша из этого.

Мой проект : это игра, в которой мы можем создать свою собственную игровую схему: пользователь рисует черно-белый спрайт -> я его конвертируюна сетку с коллайдером и сгенерировал новую игровую схему.

Я уже разбавил, чтобы очистить все белые пиксели, но не думаю, что получу много вершин с этой техникой.

Спасибо запомощь, аксель the real shape

Ответы [ 4 ]

0 голосов
/ 08 января 2019
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

0 голосов
/ 26 декабря 2018

Другой подход состоит в том, чтобы установить альфа любого не красного пикселя равным нулю, и позволить упаковщику спрайтов 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 для упаковки текстур, созданных во время выполнения, в атлас спрайтов.Если это невозможно, то для этого подхода потребуется другой инструмент для создания сеток из спрайтов во время выполнения.

0 голосов
/ 04 января 2019

Хорошо, я сделал что-то:

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);
    // }
}

Полезная ссылка: 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

Но в редакторе спрайтов я мог сгенерировать физическую форму: enter image description here

0 голосов
/ 26 декабря 2018

Одним из подходов является создание сетки непосредственно на ваших собственных условиях.Преимущество этого заключается в том, что вы можете очень точно контролировать то, как вы хотите, чтобы границы пикселей выглядели, и у вас есть более точная информация, чтобы выполнить собственную триангуляцию сетки.Недостатком является то, что вы должны делать все это самостоятельно.

Один из способов реализовать это - использовать алгоритм Marching Squares для генерации изобанд из данных пикселей (вы можете использовать синий/ зеленый / альфа-канал, чтобы получить значение isovalue в зависимости от того, является ли фон белым или прозрачным), а затем сгенерировать фрагмент сетки из каждого из 2x2 пикселей основания, которые имеют часть изобанды.

Toполучить данные пикселей из изображения, которое вы можете использовать Texture2D.GetPixels.Затем вы можете использовать алгоритм квадратов на этой информации, чтобы определить, как представлять каждый кластер пикселей 2x2 в сетке.Затем вы будете использовать эту информацию, чтобы найти вершины каждого треугольника, который представляет этот квадрат пикселей.

После преобразования каждого квадрата пикселей в треугольники расположите вершины этих треугольников в массив (убедитесь, что вы упорядочили вершины каждого треугольника по часовой стрелке от видимой стороны) и используйте Mesh.SetVertices, чтобысоздать меш с этими вершинами.

...