How to change the color of only one part of a 3D model in Unity

I want to achieve the following effect in a Unity game: I have a 3D model and, under certain conditions, I want to change the color of one of its limbs only, let’s say right leg (see screenshot below, with an example of how I want it to look). This is intended to highlight the limb in question.

I’ve been doing a lot of research and using custom shaders comes up often, but in all of the examples I’ve seen they only show how to draw an outline around the 3D model, which is not what I want. The only other suggestions I’ve found are to either use projectors (for drawing effects on mesh surfaces in real time) or to change the vertex colors programatically during run-time. Both of those suggestions are from the following question: c# - UNITY-Changing ONLY certain part of 3D model's color - Stack Overflow. The latter suggestion is the closest to what I want to achieve, but it doesn’t seem to be working for me. Here is the C# code for accomplishing the change in vertex colors:

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

What is the best way to achieve the effect I want in Unity?

4699511--443498--Change_color_question.png

Vertex coloring is a valid option, but it won’t have any effect if your character’s material is using a shader that doesn’t make use vertex coloring, which most of the default Unity shaders do not.

You can either write a custom shader that does something with those vertex colors, or maybe draw the character a second time with a material using a particle shader, which does use vertex colors.

What do you mean by “…draw the character a second time…”? Could you expand on that?

Thx.

Either have a duplicate skinned mesh renderer with a vertex colored material on it, or render it via script using a command buffer and DrawRenderer() and that same material.

1 Like

Hi, we just publish a little tool to do that: