Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Feature Request Wrinklemaps

Discussion in 'Shader Graph' started by KennethC70, Apr 12, 2019.

  1. KennethC70

    KennethC70

    Joined:
    Apr 6, 2019
    Posts:
    6
    Hello.

    Im trying to put wrinklemaps in my facial animations in unity. I can easily connect several normal maps (to be the wrinklemaps) in unreal easily.

    I am able to use many normal maps in shader graph via normal blend node.. however I dont know what to do to connect them to my facial blendshapes.

    I want my facial blendshapes to drive the corresponding normal maps for the face. Ex: brow up blendshape will drive the browup normal maps. And brow down to the browdown normal map.

    How can I achieve this?.
     
  2. alexandral_unity

    alexandral_unity

    Unity Technologies

    Joined:
    Jun 18, 2018
    Posts:
    163
    Without knowing how you connect the maps in Unreal specifically it's very hard to tell you what the Shader Graph equivalent would be. Could you post or link an example and maybe that would help?
     
  3. KennethC70

    KennethC70

    Joined:
    Apr 6, 2019
    Posts:
    6
    I simply create a new LWRP shader in shader graph then open a few blend normal nodes and connect few blendshapes and use vector 1 to control normal strength then turn them into property so that I can see them in the inspector. The normal map values now are in the inspector window. I just had a friend do some coding to connect the blendshapes to the corresponding normal map values. It plays fine in run time.

    However the normal map and a imated blendshapes doesnt show when I play from time editor.

    Is this enough explanation of where I am now? (There was nothing on anywhere about connecting blendshapesnto normal map values) so had to look for a custom programming.

    However it still doesnt work in time editor playing.
     
  4. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,329
    There's no way to drive wrinkle maps from blend shapes directly. I don't believe there's a way to do this with Unreal either. Most likely there's a blueprint setup that's driving both the blend shapes and the material properties that are controlling the wrinkle maps. You would have to do the same thing in Unity, only using a C# script.
     
  5. id0

    id0

    Joined:
    Nov 23, 2012
    Posts:
    455
    I was made something like this. "_happyStr", "_angryStr" etc... it's shader normal map strenght properties.

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class Wrinkles : MonoBehaviour{
    6.  
    7.     private float angry = 0;
    8.     private float happy = 0;
    9.     private float sad = 0;
    10.     private float surprise = 0;
    11.  
    12.     public string anger = "Angry";
    13.     public string happines = "Smile";
    14.     public string sandness = "Sad";
    15.     public string surpising = "Surprise";
    16.  
    17.     public Material face;
    18.     private SkinnedMeshRenderer head;
    19.     public Mesh m;
    20.  
    21.     private int a;
    22.     private int h;
    23.     private int s;
    24.     private int ss;
    25.  
    26.     void Start(){
    27.  
    28.         Shader _shader = Shader.Find("Shader Graphs/SkinMorph");
    29.  
    30.         foreach(Material mat in GetComponent<Renderer>().materials){
    31.  
    32.             if(mat.shader == _shader)
    33.                 face = mat;
    34.         }
    35.  
    36.         if(face != null){
    37.            
    38.             head = GetComponent<SkinnedMeshRenderer>();
    39.             m = head.sharedMesh;
    40.  
    41.             a = m.GetBlendShapeIndex(anger);
    42.             h = m.GetBlendShapeIndex(happines);
    43.             s = m.GetBlendShapeIndex(sandness);
    44.             ss = m.GetBlendShapeIndex(surpising);
    45.         }
    46.     }
    47.    
    48.     public void StartMorph(string emo){
    49.  
    50.         switch(emo){
    51.  
    52.             case "":
    53.  
    54.                 StartCoroutine(EmoMorph(0, 0, 0, 0, 1));
    55.  
    56.                 break;
    57.  
    58.             case "angry":
    59.                
    60.                 StartCoroutine(EmoMorph(100, 0, 0, 0, 1));
    61.  
    62.                 break;
    63.  
    64.             case "happy":
    65.  
    66.                 StartCoroutine(EmoMorph(0, 100, 0, 0, 1));
    67.  
    68.                 break;
    69.  
    70.             case "sad":
    71.  
    72.                 StartCoroutine(EmoMorph(0, 0, 100, 0, 1));
    73.  
    74.                 break;
    75.  
    76.             case "surprise":
    77.                
    78.                 StartCoroutine(EmoMorph(0, 0, 0, 100, 1));
    79.  
    80.                 break;
    81.  
    82.         }
    83.     }
    84.  
    85.     private IEnumerator EmoMorph(float ang, float hap, float sd, float sur, float time){
    86.  
    87.         float elapsedTime = 0;
    88.  
    89.         float _a = head.GetBlendShapeWeight(a);
    90.         float _h = head.GetBlendShapeWeight(h);
    91.         float _s = head.GetBlendShapeWeight(s);
    92.         float _ss = head.GetBlendShapeWeight(ss);
    93.  
    94.         while (elapsedTime < time){
    95.            
    96.             head.SetBlendShapeWeight(a, Mathf.Lerp(_a, ang, (elapsedTime / time)));
    97.             head.SetBlendShapeWeight(h, Mathf.Lerp(_h, hap, (elapsedTime / time)));
    98.             head.SetBlendShapeWeight(s, Mathf.Lerp(_s, sd, (elapsedTime / time)));
    99.             head.SetBlendShapeWeight(ss, Mathf.Lerp(_ss, sur, (elapsedTime / time)));
    100.  
    101.             elapsedTime += Time.deltaTime;
    102.  
    103.             yield return new WaitForEndOfFrame();
    104.         }
    105.     }
    106.  
    107.     static float t = 0.0f;
    108.  
    109.     public void Update(){
    110.  
    111.  
    112.         if(face != null){
    113.  
    114.             angry = head.GetBlendShapeWeight(a) / 100;
    115.             happy = head.GetBlendShapeWeight(h) / 100;
    116.             sad = head.GetBlendShapeWeight(s) / 100;
    117.             surprise = head.GetBlendShapeWeight(ss) / 100;
    118.  
    119.             face.SetFloat("_angryStr", angry);
    120.             face.SetFloat("_happyStr", happy);
    121.             face.SetFloat("_sadStr", sad);
    122.             face.SetFloat("_surpriseStr", surprise);
    123.         }
    124.     }
    125. }
     
  6. freenomon

    freenomon

    Joined:
    Mar 21, 2019
    Posts:
    35
     
  7. freenomon

    freenomon

    Joined:
    Mar 21, 2019
    Posts:
    35
    Hey thanks for the post. Looks like you're a professional programmer. So will this allow me to connect my facial blendshapes to my normal maps? I'm using Unity 2019 with LWRP or HDRP. do you have an email address that I can contact you? : )
     
  8. id0

    id0

    Joined:
    Nov 23, 2012
    Posts:
    455
    freenomon, just replace shader name and properties on yours, and it will do. It's pretty simple script actually. You just need call StartMorph("angry"); (for example) function from somewhere. Script must be on skinned mesh itself, and I'm, for example, just send message on it:

    Code (CSharp):
    1. MyObject.SendMessage("StartMorph", _curDialog.mimic, SendMessageOptions.DontRequireReceiver);
    It must be not in Update, or it will be mess.
     
    Last edited: Jun 17, 2019