Search Unity

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

Discussion in 'General Graphics' started by Raidenwins, Jun 30, 2019.

  1. Raidenwins

    Raidenwins

    Joined:
    Dec 18, 2012
    Posts:
    132
    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: https://stackoverflow.com/questions/34460587/unity-changing-only-certain-part-of-3d-models-color. 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:

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class BoneHiglighter : MonoBehaviour {
    5.  
    6.     public Color32 highlightColor = Color.red;
    7.     public Color32 regularColor = Color.white;
    8.  
    9.     public SkinnedMeshRenderer smr;
    10.  
    11.     // Just for sake of demonstration
    12.     public Transform bone;
    13.     private Transform prevBone;
    14.  
    15.  
    16.     // Find bone index given bone transform
    17.     int GetBoneIndex(Transform bone) {
    18.         Debug.Assert(smr != null);
    19.         var bones = smr.bones;
    20.  
    21.         for (int i = 0; i < bones.Length; ++i) {
    22.             if (bones[i] == bone) return i;
    23.         }
    24.  
    25.         return -1;
    26.     }
    27.  
    28.     // Change vertex colors highlighting given bone
    29.     void Highlight(Transform bone) {
    30.         Debug.Assert(smr != null);
    31.         var idx = GetBoneIndex(bone);
    32.         var mesh = smr.sharedMesh;
    33.         var weights = mesh.boneWeights;
    34.         var colors = new Color32[weights.Length];
    35.  
    36.         for (int i = 0; i < colors.Length; ++i) {
    37.             float sum = 0;
    38.             if (weights[i].boneIndex0 == idx && weights[i].weight0 > 0)
    39.                 sum += weights[i].weight0;
    40.             if (weights[i].boneIndex1 == idx && weights[i].weight1 > 0)
    41.                 sum += weights[i].weight1;
    42.             if (weights[i].boneIndex2 == idx && weights[i].weight2 > 0)
    43.                 sum += weights[i].weight2;
    44.             if (weights[i].boneIndex3 == idx && weights[i].weight3 > 0)
    45.                 sum += weights[i].weight3;
    46.  
    47.             colors[i] = Color32.Lerp(regularColor, highlightColor, sum);
    48.         }
    49.  
    50.         mesh.colors32 = colors;
    51.  
    52.     }
    53.  
    54.     void Start() {
    55.         // If not explicitly specified SkinnedMeshRenderer try to find one
    56.         if (smr == null) smr = GetComponent<SkinnedMeshRenderer>();
    57.         // SkinnedMeshRenderer has only shared mesh. We should not modify it.
    58.         // So we make a copy on startup, and work with it.
    59.         smr.sharedMesh = (Mesh)Instantiate(smr.sharedMesh);
    60.  
    61.         Highlight(bone);
    62.     }
    63.  
    64.     void Update() {
    65.         if (prevBone != bone) {
    66.             // User selected different bone
    67.             prevBone = bone;
    68.             Highlight(bone);
    69.         }
    70.     }
    71. }
    What is the best way to achieve the effect I want in Unity?

    Change_color_question.png
     
  2. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,348
    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.
     
  3. Raidenwins

    Raidenwins

    Joined:
    Dec 18, 2012
    Posts:
    132
    What do you mean by "..draw the character a second time..."? Could you expand on that?

    Thx.
     
  4. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,348
    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.
     
    Raidenwins likes this.
  5. Klamore74

    Klamore74

    Joined:
    Jun 17, 2013
    Posts:
    103