UNITY-Изменение ТОЛЬКО определенной части цвета 3D-модели

Я действительно новичок в unity3D и хотел бы задать вопрос. У меня есть трехмерная модель человека (модель Unity по умолчанию), которая имеет иерархическую структуру костей.

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

введите здесь описание изображения

Я действительно ничего не знаю об этом, потому что я только начал изучать Unity около 3 месяцев назад, поэтому мне очень нужна ваша помощь, это свойство моего рендерера, если он помогает

введите здесь описание изображения


person Richard    schedule 25.12.2015    source источник
comment
Вы можете получить дополнительные ответы на сайте gamedev.stackexchange.com.   -  person Sami Kuhmonen    schedule 25.12.2015
comment
Для этого вы должны иметь другую текстурную карту тех частей, для которых вы хотите изменить цвет, что сравнительно дорого.   -  person Hamza Hasan    schedule 25.12.2015


Ответы (2)


Я просто хотел добавить, что может быть более простой способ добиться того, чего вы хотите, используя проекторы< /а>. Это общий инструмент в Unity, используемый для рисования различных эффектов на поверхности сетки в режиме реального времени, таких как пулевые отверстия. Используя тот же принцип, вы можете выделить некоторые области сетки. Вы можете думать о проекторе как о фонарике: все, на что падает его свет, меняет свою текстуру. В разделе Standard Assets/Effects есть несколько примеров проекторов. Возможно, вы захотите начать с этого.

Чтобы создать проектор, создайте пустой игровой объект => Добавить компонент => Проектор.

ИЗМЕНИТЬ

Еще одна идея, которую вы можете попробовать, — это использовать цвета вершин. Каждая вершина меша содержит помимо координат параметр цвета, доступный через шейдер. Таким образом, вы можете изменить цвет определенного набора вершин, чтобы выделить их. Здесь есть 2 момента, на которые следует обратить внимание:

1) Большинство шейдеров предпочитают игнорировать цвет вершин, за исключением шейдера спрайтов. Вам понадобится собственный шейдер, например этот.

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 — щелкните по частям тела, чтобы выделить их.

person Yuri Nudelman    schedule 25.12.2015
comment
Привет, спасибо за ваш ответ, это звучит как более простое решение для меня, но, когда я читал руководство, в нем говорилось, что Проектор позволяет вам проецировать Материал на все объекты, которые пересекают его усеченную вершину, я не думайте, что он может точно выделить определенную часть тела. Например, если я хочу выделить плечо, когда модель стоит прямо, я боюсь, что это также выделит часть тела, не так ли? - person Richard; 27.12.2015
comment
Конечно, это потребует тонкой настройки, нужно грамотно разместить проекторы, и нет гарантии, что все будет выглядеть красиво из коробки. Для проекторов доступно множество параметров настройки. Например, у проектора есть аргумент дальней плоскости отсечения, и все, что находится за этой плоскостью, не затрагивается. Итак, как вы говорите, если вы выделите руку и убедитесь, что дальняя плоскость клипа проектора не достигает тела, тело не будет выделено. Сказав это, потому что это не будет на 100% точным, если вы хотите быть на 100% точным, вам придется использовать несколько материалов. - person Yuri Nudelman; 27.12.2015
comment
Спасибо, но я застрял в поиске индекса кости, как мы можем определить, какой индекс кости соответствует какой части тела? - person Richard; 28.12.2015
comment
Вы меня заинтересовали, поэтому я написал небольшой проект, чтобы посмотреть, как это работает. Выглядит нормально, смотрите обновление ответа. - person Yuri Nudelman; 28.12.2015
comment
Привет, извините, что беспокою вас снова, ваш код работает очень хорошо, но когда я пытаюсь вызвать функцию Highlight() из другого класса, она отправляет мне NullReferenceExecption, потому что SkinnedMeshRenderer равно нулю, не могли бы вы помочь мне с этим? благодарю вас - person Richard; 12.01.2016
comment
Во-первых, не извиняйтесь, вся цель этого сайта - получить ответы на ваши вопросы, не так ли? О вашей проблеме - вы должны убедиться, что значение SkinnedMeshRenderer (общедоступная переменная smr в примере) установлено на что-то. В приведенном выше примере он установлен в функции Start, поэтому, возможно, вы вызываете его перед Start? Или, может быть, скрипт привязан к какому-то объекту, который напрямую не содержит SkinnedMeshRenderer, поэтому его нельзя найти с помощью GetComponent. Что вы можете сделать, так это вместо автоматической инициализации вручную перетащить SkinnedMeshRenderer в скрипт. - person Yuri Nudelman; 12.01.2016
comment
Помните, что SkinnedMeshRenderer обычно является объектом иерархии второго уровня в вашей модели. Например, если ваша модель — «SomeModel», то ее корневой объект называется «SomeModel», и у него есть несколько дочерних элементов, таких как «SomeModelMesh» и «SomeModelSkeleton». SkinnedMeshRenderer прикреплен к SomeModelMesh. - person Yuri Nudelman; 12.01.2016
comment
Я попробовал, перетащите SkinnedMeshRenderer в общедоступную переменную smr, это не сработало. Я думаю, это потому, что я вызвал его из другого скрипта из другого игрового объекта? Имя игрового объекта модели — Human, в то время как другой называется Comparator, каким-то образом я заставляю его работать, помещая общедоступную переменную GameObject в скрипт компаратора и перетаскивая в нее Human, затем я делаю GetComponent<BoneHighlighter>().Highlight(), и это работает, но очень медленно и лагги, есть предложения? - person Richard; 14.01.2016
comment
Вы описываете то, что не должно происходить, не важно, из какого игрового объекта вы вызываете код. Проблема скорее всего где-то в другом. Было бы полезно, если бы вы разместили некоторый код (возможно, стоит создать для этого новый вопрос). Я могу только догадываться, что это может быть связано с использованием sharedMesh (см. код в функции Start), но это всего лишь предположение, извините. - person Yuri Nudelman; 14.01.2016
comment
Привет, я пробовал другой подход для этого, но результат все еще отстает и не гладкий, я разместил здесь еще один вопрос ссылка, возможно, вы можете мне помочь, спасибо - person Richard; 21.01.2016

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

  • Этот рабочий имеет 1 одиночный меш (3d модель).
  • Эта модель визуализируется с помощью 1 материала (средство визуализации Skinned mesh имеет только 1 присоединенный материал)
  • Этот материал имеет базовую текстуру RGBA, которая окрашивает вашу модель. Теперь вот загвоздка: чтобы наложить 2D-текстуру на 3D-модель, мы используем UV-наложение. UV-отображение — это своего рода мост, который связывает каждую вершину 3D-модели с 2D-координатой на плоскости. В 3D мы называем их Vertex, в 2D — UV. Таким образом, когда вы окрашиваете область вокруг одного UV на 2D-плоскости (в Photoshop, Gimp и т. д.), соответствующая вершина будет окрашена на сетке. Google UV-карты для получения дополнительной информации. ПРИМЕЧАНИЕ: вы не можете делать UV-карты в Unity (кроме как через скрипт), это обычно делается во внешнем программном обеспечении для 3D-моделирования.
  • Наконец, после того, как вы все погуглили, вы можете переключиться на свою задачу. Вы, вероятно, уже поняли, что вам нужно изменить текстуру вашей модели. Вам нужно будет найти, где сопоставляется рука вашей модели, а затем изменить цвет этой области с помощью скрипта. Вы делаете это с помощью Texture2D.GetPixels() и SetPixels().

Есть и другое решение для этого, менее програмного, более логичного, но грубого способа, требующего оптимизации:

  • Импортируйте эту модель в программное обеспечение для 3D-моделирования.
  • Отсоедините руку, но убедитесь, что она все еще контролируется скелетом.
  • Импортируйте модель обратно в Unity
  • Сделать новый материал для руки, можно продублировать существующий (обычная практика - делать отдельные материалы для объектов, меняющих визуальные свойства)
  • Дублируйте текстуру RGBA и обесцветьте ее в программе для редактирования изображений.
  • Назначьте эту дублирующую текстуру цветовому слоту дублированного материала, а дублированный материал — модели руки.
  • Рука уже должна быть в оттенках серого, поэтому вы можете изменить ее цвет, изменив цвет материала.
person Nika Kasradze    schedule 25.12.2015
comment
Привет, большое спасибо за ваш ответ, да, некоторые люди сказали мне, что мне нужно использовать более одного материала, чтобы сделать это. Но я хочу найти что-то более простое, чтобы сделать это. Сначала я изучу и попробую ваши решения, и я дам вам знать, если они сработают, большое спасибо - person Richard; 27.12.2015