【问题标题】:UNITY-Changing ONLY certain part of 3D model's colorUNITY-仅更改 3D 模型颜色的某些部分
【发布时间】:2016-03-31 08:36:42
【问题描述】:

我是 unity3D 的新手,我想问一个问题 我有一个具有分层骨骼结构的 3D 人体模型(默认统一模型)。

我想在这里实现的是,当我按下某个触发器时,我想用不同的颜色为它的一个肢体着色(只是它的一个肢体)。这就是我想要达到的目标的说明

我真的对此一无所知,因为我刚开始学习 Unity 大约 3 个月前,所以我真的需要你的帮助,如果它有帮助,这是我的渲染器的属性

【问题讨论】:

  • 您可能会从gamedev.stackexchange.com得到更多答案
  • 为此,您必须为要更改颜色的部分保留不同的纹理贴图,相对成本较高

标签: c# unity3d 3d modeling 3d-modelling


【解决方案1】:

如果您是初学者,则必须阅读一些内容。在更改某些内容之前,请确保您完全了解模型设置:

  • 此工人有 1 个单一网格(3d 模型)。
  • 此模型由 1 种材质渲染(蒙皮网格渲染器仅附加了 1 种材质)
  • 此材质具有基础 RGBA 纹理,可为您的模型着色。现在有一个问题:为了在 3D 模型上映射 2D 纹理,我们使用 UV 映射。 UV 映射是一种将 3D 模型上的每个顶点链接到平面上的 2D 坐标的桥梁。在 3D 中,我们称它们为顶点,在 2D 中,我们称它们为 - UV。因此,当您在 2D 平面上(在 Photoshop、gimp 等中)为一个 UV 周围的区域着色时,相应的顶点将在网格上着色。谷歌 UV 映射了解更多信息。注意:您不能在 Unity 中进行 UV 映射(除非通过脚本),通常在外部 3D 建模软件中完成。
  • 最后,在完成所有谷歌搜索后,您可以切换到您的任务。您现在可能已经明白您需要修改模型的纹理。您需要找到模型的手被映射到的位置,然后使用脚本更改该区域的颜色。你可以用Texture2D.GetPixels()SetPixels() 来做。

对此还有另一种解决方案,程序化程度较低,逻辑性更强,但需要优化:

  • 将该模型导入 3D 建模软件
  • 拆下手臂,但确保它仍由骨架控制
  • 将模型重新导入 Unity
  • 为手臂制作新材质,您可以复制现有材质(通常的做法是为改变视觉属性的对象制作单独的材质)
  • 复制您的 RGBA 纹理并在图像编辑软件中对其进行去饱和处理。
  • 将此复制纹理分配给复制材质的颜色槽,并将复制材质分配给手臂模型。
  • 现在手臂应该是灰度的,因此您可以通过更改材质颜色来更改其颜色。

【讨论】:

  • 您好,非常感谢您的回复,是的,有些人告诉我,我需要使用不止一种材料来做到这一点。但我想找到更简单的方法来做到这一点。我会先学习并尝试您的解决方案,如果可行,我会告诉您,非常感谢
【解决方案2】:

我只是想补充一点,使用projectors 可能有一种更简单的方法来实现您想要的。这是统一的常用工具,用于实时在网格表面上绘制各种效果,例如弹孔。使用相同的原理,您可以突出显示网格的某些区域。你可以把投影仪想象成手电筒,它的光照射到的所有东西都会改变它的纹理。标准资产/效果下有一些示例投影仪。您可能想从那里开始。

要创建投影仪,请创建一个空的游戏对象 => 添加组件 => 投影仪。

编辑

您可能想尝试的另一个想法是使用vertex colors。除了坐标之外,网格的每个顶点还包含一个颜色参数,该参数可通过着色器访问。因此,您可以更改特定顶点集的颜色以突出显示它们。这里有2点需要注意:

1) 大多数着色器选择忽略顶点颜色,精灵着色器除外。您将需要一个自定义着色器,例如 this one

2) 您需要以某种方式确切地知道要突出显示哪些顶点。你可以做的是迭代Mesh.boneWeights。例如,如果您想选择一个手臂,您需要的是手臂骨骼索引权重 > 1 的所有顶点。你如何找到手臂骨指数?该索引对应于SkinnedMeshRenderer.bones 中的索引。或者只是选择一些您确定属于手臂的顶点并查看其骨骼权重以找到索引。

这里是一个示例脚本,它根据选定的骨骼索引更改顶点颜色。将它附加到您的 SkinnedMeshRenderer(通常是二级层次结构对象):

using UnityEngine;
using System.Collections;

public class BoneHiglighter : MonoBehaviour {

    public Color32 highlightColor = Color.red;
    public Color32 regularColor = Color.white;

    public SkinnedMeshRenderer smr;

    // Just for sake of demonstration
    public Transform bone;
    private Transform prevBone;


    // Find bone index given bone transform
    int GetBoneIndex(Transform bone) {
        Debug.Assert(smr != null);
        var bones = smr.bones;

        for (int i = 0; i < bones.Length; ++i) {
            if (bones[i] == bone) return i;
        }

        return -1;
    }

    // Change vertex colors highlighting given bone
    void Highlight(Transform bone) {
        Debug.Assert(smr != null);
        var idx = GetBoneIndex(bone);
        var mesh = smr.sharedMesh;
        var weights = mesh.boneWeights;
        var colors = new Color32[weights.Length];

        for (int i = 0; i < colors.Length; ++i) {
            float sum = 0;
            if (weights[i].boneIndex0 == idx && weights[i].weight0 > 0)
                sum += weights[i].weight0;
            if (weights[i].boneIndex1 == idx && weights[i].weight1 > 0)
                sum += weights[i].weight1;
            if (weights[i].boneIndex2 == idx && weights[i].weight2 > 0)
                sum += weights[i].weight2;
            if (weights[i].boneIndex3 == idx && weights[i].weight3 > 0)
                sum += weights[i].weight3;

            colors[i] = Color32.Lerp(regularColor, highlightColor, sum);
        }

        mesh.colors32 = colors;

    }

    void Start() {
        // If not explicitly specified SkinnedMeshRenderer try to find one
        if (smr == null) smr = GetComponent<SkinnedMeshRenderer>();
        // SkinnedMeshRenderer has only shared mesh. We should not modify it.
        // So we make a copy on startup, and work with it.
        smr.sharedMesh = (Mesh)Instantiate(smr.sharedMesh);

        Highlight(bone);
    }

    void Update() {
        if (prevBone != bone) {
            // User selected different bone
            prevBone = bone;
            Highlight(bone);
        }
    }
}

查看示例项目:https://www.dropbox.com/s/yfoqo44bubcr48s/HighlightBone.zip?dl=0

演示:http://jolly-squirrel.droppages.com/bones/index.html - 点击身体部位以查看它们突出显示。

【讨论】:

  • 嗨,谢谢您的回答,这对我来说听起来更简单,但是当我阅读手册时,它指出 “投影仪允许您将材质投影到所有对象上intersect它的平截头体”,我不认为它可以“精确地突出身体的某些部位。例如如果我想突出上臂而模特站直,我怕它也会突出身体的一部分。身体不是吗?
  • 当然,它需要一些微调,您需要以巧妙的方式放置投影仪,并且不能保证开箱即用的一切看起来都很漂亮。有许多可用于投影仪的自定义参数。例如,投影仪具有远剪辑平面参数,并且该平面后面的任何内容都不会受到影响。因此,正如您所说,如果您突出显示手臂,并确保投影仪远剪辑平面没有到达身体,身体将不会被突出显示。话虽如此,它当然不会 100% 准确,如果你想 100% 准确,你将不得不使用多种材料。
  • 谢谢,但是我一直在寻找骨骼索引,我们如何确定哪个骨骼索引对应于身体的哪个部位?
  • 你让我很好奇,所以我写了一个小项目来看看它是如何工作的。看起来工作正常,请参阅答案更新。
  • 嗨,很抱歉再次打扰您,您的编码工作得很好,但是当我尝试从另一个类调用 Highlight() 函数时,它发送给我 NullReferenceExecption 因为 SkinnedMeshRenderer 为空,你能帮我解决这个问题吗?谢谢
最近更新 更多