【发布时间】:2018-02-07 05:51:47
【问题描述】:
所以标题可能并不完美,但它本质上是我想做的。
我的问题
我正在使用 Unity 2017.3.0f3 和 Google 的 ARCore 创建一个应用程序,您可以在其中选择要放置在世界中的模型并根据需要更改材质上的纹理。我遇到的问题是我可以在上面放置一个带有 1 个纹理的模型,但是当我更改 Ghost 模型上的纹理时(在这种情况下,Ghost 模型在 ARCore @ 上漂浮在世界各地987654330@,其中来自屏幕中心的Raycast 与Plane 碰撞)它也会改变已放置模型上的纹理。请参阅下面的屏幕截图,与我手机上的屏幕截图相差几秒钟:
- 我已经放置了一个带有“点燃”火坑纹理的模型(左/后),而
Ghost模型仍然是“点燃”(右/前):
- 我点击了列表中的“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>();
}
这是我用来将 Instantiate 和 Ghost 模型放入新对象并将其放置在 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,我尝试在Texture 和Material 上调用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;
【问题讨论】: