夜風のMixedReality

xRと出会って変わった人生と出会った技術を書き残すためのGeekなHoloRangerの居場所

UnityでCharactorCreator製のキャラクターの瞳孔をコントロールする

本日はReallusion&Unity枠です。

〇環境

・Windows11PC

・Unity2022.3.26f1

〇瞳孔

人間は様々な条件で瞳孔の大きさが変わります。

瞳孔は交感神経、副交感神経や環境の明るさ、見ているものの距離によって変わります。これは生理的な反応であり、意識して瞳孔の大きさを変えるものではありません。

また、近くのものを見るときと遠くのものを見る時では水晶体の厚みが変わり、屈折力が変わります。

これらの変化をCharactorCreator製のキャラクターで実装します。

前提としてCCToolsを用いてUnityにインポートしてMaterialの設定を行っていることから始めます。

redhologerbera.hatenablog.com

〇瞳孔の変化

Reallusionの提供するシェーダーでは瞳用のシェーダーが用意されています。URPの場合はRL_Amplify_CorneaShaderParallax_URPシェーダーです。

このシェーダーでは、瞳の大きさや色の他にIOR(屈折率)やPupilScale(瞳孔)の設定ができます。

IrisSizeMin

Pupil Scale

この値をC#側で調整可能にしたものが以下になります。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace SOADA3DART.Pharmacetical
{
    public class EnvManager : MonoBehaviour
    {
        public bool useDefaultSettings = true; // デフォルト設定を使用するかどうか
        [Header("Environment Settings")] [SerializeField]
        private int DayTime = 12; // 環境が昼か夜か
        public DayTimeS DayTimeSetting = DayTimeS.noon; // 環境の昼夜設定
        public enum DayTimeS
        {
            earlyMorning, // 早朝
            morning, // 朝
            noon, // 昼
            evening, // 夕方
            night, // 夜
            middnight // 真夜中
        }
        // Start is called before the first frame update
        void Start()
        {
            if (!useDefaultSettings)
            {
                //現在の時刻を取得  
                System.DateTime currentTime = System.DateTime.Now;
                DayTime = currentTime.Hour; // 現在の時間を取得
                // 環境の昼夜を設定
                switch (DayTime)
                {
                  case <4 :
                      DayTimeSetting = DayTimeS.middnight; // 0時から3時は真夜中
                      break;
                  case <7:
                      DayTimeSetting = DayTimeS.earlyMorning; 
                      break;
                  case <10:
                      DayTimeSetting = DayTimeS.morning; // 4時から6時は早朝
                      break;
                  case <15:
                      DayTimeSetting = DayTimeS.noon; // 7時から14時は昼
                      break;
                  case <18:
                      DayTimeSetting = DayTimeS.evening; // 15時から17時は夕方
                      break;
                  case <21:
                      DayTimeSetting = DayTimeS.night; // 18時から20時は夜
                      break;
                  case <24:
                      DayTimeSetting = DayTimeS.middnight; // 21時から23時は真夜中
                      break;
                }
            }
            
        }

    
    }

}
        GameObject _lookAtTarget; // 目の注視ターゲット
        [Header("IrisSettings"),SerializeField]
        Material[] _irisMaterials; // アイリスのマテリアル
        [SerializeField] float _pupilScale = 0.5f;
       public enum SleepState
        {
            less,
            Sleepy,
            normal
        }
      public void SetIrisSettingsWithEnv()
        {
            switch (_envManager.DayTimeSetting)
            {
                case EnvManager.DayTimeS.earlyMorning:
                    StartCoroutine(changeIrisSize(0.7f, 1f));
                    break;
                case EnvManager.DayTimeS.morning:
                    StartCoroutine(changeIrisSize(0.65f, 1f));
                    break;
                case EnvManager.DayTimeS.noon:
                    StartCoroutine(changeIrisSize(0.55f, 1f));
                    break;
                case EnvManager.DayTimeS.evening:
                    StartCoroutine(changeIrisSize(0.65f, 1f));
                    break;
                case EnvManager.DayTimeS.night:
                    StartCoroutine(changeIrisSize(0.8f, 1f));
                    break;
                case EnvManager.DayTimeS.middnight:
                    StartCoroutine(changeIrisSize(0.9f,1f));
                    break;
            }
        }
        //なだらかに瞳孔の大きさを変更する
        IEnumerator changeIrisSize(float targetSize, float duration)
        {
            _pupilScale = targetSize;
            float startSizeR = _irisMaterials[0].GetFloat("_PupilScale");
            float startSizeL = _irisMaterials[1].GetFloat("_PupilScale");
            float elapsedTime = 0f;

            while (elapsedTime < duration)
            {
                elapsedTime += Time.deltaTime;
                float t = Mathf.Clamp01(elapsedTime / duration);
                float newSizeR = Mathf.Lerp(startSizeR, targetSize, t);
                float newSizeL = Mathf.Lerp(startSizeL, targetSize, t);
                _irisMaterials[0].SetFloat("_PupilScale", newSizeR);
                _irisMaterials[1].SetFloat("_PupilScale", newSizeL);
                yield return null;
            }
        }
  public void SetIrisSizewithCharactorState()
        {
            SetIrisSettingsWithEnv();
            switch (_sleepState)
            {
                case SleepState.less:
                    _pupilScale *= 0.8f; 
                    break;
                case SleepState.Sleepy:
                    _pupilScale *= 0.9f; // 眠い
                    break;
                case SleepState.normal:
                    break;
            }

            switch (_spO2)
            {
                case SpO2.Normal:
                    _pupilScale *= 1f; // 正常
                    break;
                case SpO2.Hypoxemia:
                    _pupilScale *= 0.8f; // 低酸素血症
                    break;
            }
            _irisMaterials[0].SetFloat("_PupilScale", _pupilScale);
            _irisMaterials[1].SetFloat("_PupilScale", _pupilScale);
        }
       public void SetIrisWithDistance()
        {
            //_lookAtTargetとの値に応じて瞳孔の大きさとIOCを変える。
            float distance = Vector3.Distance(_lookAtTarget.transform.position, _EyeR.position)/5f;
            distance = 1 + (0.5f - Mathf.Clamp01(distance));
            _pupilScale *= 1 +( 0.5f - Mathf.Clamp01(distance)); 
            _irisMaterials[0].SetFloat("_PupilScale" ,_pupilScale);
            _irisMaterials[0].SetFloat("_IOR" ,1.4f - distance*3);
            _irisMaterials[1].SetFloat("_PupilScale" ,_pupilScale);
            _irisMaterials[1].SetFloat("_IOR" , 1.4f - distance*3);
        }

コア部分はMaterial.SetFloat("_PupilScale" ,_pupilScale)Material.SetFloat("_IOR" , 1.4f - distance*3)です。

これは瞳のシェーダーのパラメータに値を渡しています。

どのような値を与えるかという点で、ここでは環境や体調によって細かく基本的なパラメータを与えて、最終的にはGameObject _lookAtTargetの値で注視しているオブジェクトとの距離で変化させています。

これ次実際に動かしたものが以下になります。

以上で瞳孔のコントロールができるようになりました。本日は以上です。