【问题标题】:How can I Instantiate a model and dissociate the material/texture from another instance of the model?如何实例化模型并将材质/纹理与模型的另一个实例分离?
【发布时间】:2018-02-07 05:51:47
【问题描述】:

所以标题可能并不完美,但它本质上是我想做的。

我的问题

我正在使用 Unity 2017.3.0f3 和 Google 的 ARCore 创建一个应用程序,您可以在其中选择要放置在世界中的模型并根据需要更改材质上的纹理。我遇到的问题是我可以在上面放置一个带有 1 个纹理的模型,但是当我更改 Ghost 模型上的纹理时(在这种情况下,Ghost 模型在 ARCore @ 上漂浮在世界各地987654330@,其中来自屏幕中心的RaycastPlane 碰撞)它也会改变已放置模型上的纹理。请参阅下面的屏幕截图,与我手机上的屏幕截图相差几秒钟:

  1. 我已经放置了一个带有“点燃”火坑纹理的模型(左/后),而Ghost 模型仍然是“点燃”(右/前):

  1. 我点击了列表中的“Unlit”火坑纹理,现在两个模型都改变了纹理:

这是我编写的将Ghost更改为所选模型的代码(它有效,我已经用多个模型对其进行了测试,但演示目前只有1个),在单击模型缩略图时调用在底部窗格中:

public void SetPlaceableModel(GameObject newModel)
{
    PlaceableModel = newModel;
    var pos = (Ghost != null) ? Ghost.transform.position : FirstPersonCamera.ScreenToWorldPoint(new Vector3(Screen.width / 2f, Screen.height / 2f, -100f));
    var rot = (Ghost != null) ? Ghost.transform.rotation : Quaternion.identity;
    Destroy(Ghost);
    Ghost = Instantiate(PlaceableModel, pos, rot);
}

这是我为更改 Ghost 模型上的纹理而编写的代码,在单击顶部窗格中的纹理缩略图时会调用该代码:

public void ChangeGhostTextures(TextureSet textureSet)
{
    SelectedTextureSet = textureSet;
    var materials = Ghost.GetComponentsInChildren<Renderer>().SelectMany(x => x.materials);
    foreach (var texture in textureSet.Textures.Where(x => !x.name.ToLower().Contains("normal")))
    {
        var normalTexture = textureSet.Textures.FirstOrDefault(x => x.name == string.Format("{0}_Normal", texture.name));
        foreach (var material in materials.Where(x => x.name.Replace(" (Instance)", string.Empty) == texture.name))
        {
            material.SetTexture("_MainTex", texture);
            if (normalTexture != null)
            {
                material.SetTexture("_BumpMap", normalTexture);
            }
        }
    }
}

这是TextureSet的定义:

public class TextureSet : MonoBehaviour {
    public string SetName;
    public Texture Thumbnail;
    public List<Texture> Textures = new List<Texture>();
}

这是我用来将 InstantiateGhost 模型放入新对象并将其放置在 Update() 方法中的 ARCore Plane 上的代码:

var productModel = Instantiate(Ghost, Ghost.transform.position, Ghost.transform.rotation);
productModel.GetComponent<PlaneAttachment>().Attach(hit.Plane);

据我所知,我不知道为什么会这样,当我将Instantiate Ghost 转到Plane 时,这些材料似乎保持对Ghost 材料的引用。我尝试更改 ChangeGhsotTexture 来做到这一点:

var _tex = Instantiate(texture) as Texture; //also tried 'as Texture2D'
material.SetTexture("_MainTex", _tex);

但是当我更改纹理时会发生这种情况:

我也尝试过 Instantiate(material) as Material 然后设置它,但发生了同样的事情(作为原始问题,而不是黑色纹理):

material.SetTexture("_MainTex", texture);
if (normalTexture != null)
{
    material.SetTexture("_BumpMap", normalTexture);
}
var _mat = Instantiate(material) as Material;
//can't assign directly to `material` because it's the enumeration result
var assignableMaterial = materials.First(x => x.Equals(material));
assignableMaterial = _mat;

现在是踢球者

放下模型后,如果我单击模型缩略图(底部窗格,调用SetPlaceableModel),即使它与已选择的模型相同,因为它仍然调用SetPlaceableModel,它也可以工作**!在将模型放入Update() 方法后,我尝试调用SetPlaceableModel,但它不能解决问题(添加信息时,单击模型缩略图时,它会在Update() 中调用SetPlaceableModel不同的脚本)。 另外,它现在似乎工作正常仅适用于放置的第一个模型,然后它的行为与我遇到的问题有关(但我不知道为什么......我没有改变任何东西)。

我试过了……

正如我所提到的,我尝试在放置模型后再次调用SetPlaceableModel,我尝试在TextureMaterial 上调用Instantiate,就像suggested here 一样(这似乎是我的确切问题......所以我必须遗漏一些东西)。 **

我很困惑......请帮忙。如果您需要更多信息,请告诉我。


**

编辑:在进一步测试中(我添加了一个新模型和 2 个 TextureSet 对象,所以我有 2 个模型,每个模型都有 2 个不同的纹理集),这个“有效”是因为该问题仅出现在我第一次更改纹理选择模型之后。当我更改模型并使用模型上默认的材质/纹理时,问题就消失了(每个模型上都有材质/纹理,在编辑器中设置,然后再运行项目)。

编辑 2:正如@Programmer 建议的那样,我将Instantiate(material) as Material 更改为他建议的new Material(material),但它不起作用。这就是我实现它的方式:

public void ChangeGhostTextures(TextureSet textureSet)
{
    var materials = Ghost.GetComponentsInChildren<Renderer>().SelectMany(x => x.materials);
    foreach (var texture in textureSet.Textures.Where(x => !x.name.ToLower().Contains("normal")))
    {
        var normalTexture = textureSet.Textures.FirstOrDefault(x => x.name == string.Format("{0}_Normal", texture.name));
        foreach (var material in materials.Where(x => x.name.Replace(" (Instance)", string.Empty) == texture.name))
        {
            material.SetTexture("_MainTex", texture);
            if (normalTexture != null)
            {
                material.SetTexture("_BumpMap", normalTexture);
            }
            var _material = new Material(material);
            var assignableMaterial = materials.First(x => x.Equals(material));
            assignableMaterial = _material;
        }
    }
}

我做错了吗?我也尝试了这个实现,但它现在根本不改变纹理(而其他实现确实改变了纹理,但仍然存在问题):

var _material = new Material(material);
_material.SetTexture("_MainTex", texture);
if (normalTexture != null)
{
    _material.SetTexture("_BumpMap", normalTexture);
}
var assignableMaterial = materials.First(x => x.Equals(material));
assignableMaterial = _material;

【问题讨论】:

    标签: c# unity3d


    【解决方案1】:

    当我将幽灵实例化到飞机上时,材料似乎是 保持对 Ghost 材料的引用。

    Material 类有三个构造函数重载:

    public Material(string contents);
    public Material(Shader shader);
    public Material(Material source);
    

    最后一个是你需要的。使用它来制作原始材料的副本,然后将该副本分配给您的对象。这应该可以解决材料参考问题。

    public Material objMat;
    
    void Start()
    {
        Material newMat = new Material(objMat);
        //Use the newMat on your object
    }
    

    【讨论】:

    • 那么,与其采用Instantiate(material) as Material 的方式,不如采用构造方法?您能否详细说明为什么Instantiate 不会破坏引用但构造函数会破坏? (我只是在一个基本概念上愚蠢和空白吗?)谢谢,将测试!
    • @Daevin 或者,查看MaterialPropertyBlocks
    • @Programmer 是的,但MaterialPropertyBlock 有可能在没有额外内存开销的情况下实现所需的视觉效果
    • @Daevin MaterialPropertyBlocks 允许您重用相同的 Material 实例,但参数(纹理、颜色等)已更改,您只需应用它们一次 (iirc)。使用更少的内存,不会中断批处理,并且不会因多次更改同一材质而导致过多的 GC。
    • @Daevin 啊,我没有意识到同一个渲染器上有多个单独的材质是的,这会让事情变得棘手。不,我没有。 :\
    【解决方案2】:

    由于我无法使其与@Programmer 的方法一起使用(尽管它可能对其他人有用,但我仍在更改材质,无论我在何处/如何实现var _mat = new Material(mat); 或分配纹理,我选择只定义多种材质作为 Assets 并根据需要在它们之间切换:

    public void ChangeGhostMaterials(MaterialSet materialSet)
    {
        foreach (var renderer in Ghost.GetComponentsInChildren<Renderer>())
        {
            var material = materialSet.Materials.FirstOrDefault(x => x.name.Equals(renderer.material.name.Replace(" (Instance)", string.Empty)));
            renderer.material = material ?? renderer.material;
        }
    }
    

    如果其他人出现并提供有效的答案,我很乐意更改所选答案,但就目前而言,这是我对问题的解决方案。

    【讨论】:

      猜你喜欢
      • 2019-08-01
      • 1970-01-01
      • 1970-01-01
      • 2015-01-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-11-14
      • 1970-01-01
      相关资源
      最近更新 更多