SakeTami
AYA-cis
AYA-cis

fanbox


UnityでLive2Dの物理演算が反映されない?

Vを作ったら99割の人がまず胸を揺らしたいと思うんですけど(要出典)。 Unityでそこら辺のコードを拾って作るとなぜかLive2Dの物理演算が反映されないんですよね。いや厳密に言うと初期値を弄っておいてシーンを再生するとそれに対しては物理演算は反映されるのです。つまり書いたコードに物理演算が反映されてないんですわ。 この問題の答えは公式チュートリアルで触れている実行順制御です。 https://docs.live2d.com/cubism-sdk-tutorials/using-update-controller/ つまり、物理演算の後で、野良コードを実行してるわけです!そら動くわけ無いわ。 public static readonly int CubismPhysicsController = 800; と書いてあるとおり800番指定で物理演算を実行してるので public int ExecutionOrder { get { return 701; } } のように800番より前で実行してる必要があるわけだ。 というわけでFrontalFaceParam.csは以下のように書き直しです。 ------コードここから(FrontalFaceParam.cs) using UnityEngine; using System; using System.Collections.Generic; using Live2D.Cubism.Core; using Live2D.Cubism.Rendering; using Live2D.Cubism.Rendering.Masking; using OpenCVForUnity.CoreModule; using OpenCVForUnity.Calib3dModule; using OpenCVForUnity.UnityUtils; namespace Live2D.Cubism.Framework.Physics { public class FrontalFaceParam : MonoBehaviour, ICubismUpdatable { [SerializeField] DlibWebCamFaceDetector faceDetector; [SerializeField] CubismParameter headAngleParameterX; [SerializeField] CubismParameter headAngleParameterY; [SerializeField] CubismParameter headAngleParameterZ; [SerializeField] float lerpT = 0.2f; private Vector3 _headRotation; // 座標変換関連 private MatOfPoint3f _objectPoints; private MatOfPoint2f _imagePoints; private Mat _rotM; private Mat _camMatrix; private MatOfDouble _distCoeffs; private Matrix4x4 _invertYM; private Matrix4x4 _invertZM; private Matrix4x4 _transformationM; private Mat _rVec; private Mat _tVec; private Matrix4x4 _ARM; private PoseData _oldPoseData; private Matrix4x4 _VP; // 正規化関連 [SerializeField] float normWidth = 200; [SerializeField] float normHeight = 200; List _normPoints; // ローパスフィルタ関連 [SerializeField] float positionLowPass = 4f; [SerializeField] float rotationLowPass = 2f; // Sceneの非実行中に実行順の制御を行うか public bool NeedsUpdateOnEditing { get { return false; } } // このコンポーネントの実行順 public int ExecutionOrder { get { return 701; } } void Awake() { _invertYM = Matrix4x4.TRS(Vector3.zero, Quaternion.identity, new Vector3(1, -1, 1)); _invertZM = Matrix4x4.TRS(Vector3.zero, Quaternion.identity, new Vector3(1, 1, -1)); // 顔の初期位置を設定 _objectPoints = new MatOfPoint3f( new Point3(-31, 72, 86), // 左目 new Point3(31, 72, 86), // 右目 new Point3(0, 40, 114), // 鼻 new Point3(-20, 15, 90), // 左口角 new Point3(20, 15, 90), // 右口角 new Point3(-69, 76, -2), // 左耳 new Point3(69, 76, -2) // 右耳 ); _imagePoints = new MatOfPoint2f(); _rotM = new Mat(3, 3, CvType.CV_64FC1); // カメラの内部パラメータ float maxD = Mathf.Max(normHeight, normWidth); float fx = maxD; float fy = maxD; float cx = normWidth / 2.0f; float cy = normHeight / 2.0f; _camMatrix = new Mat(3, 3, CvType.CV_64FC1); _camMatrix.put(0, 0, fx); _camMatrix.put(0, 1, 0); _camMatrix.put(0, 2, cx); _camMatrix.put(1, 0, 0); _camMatrix.put(1, 1, fy); _camMatrix.put(1, 2, cy); _camMatrix.put(2, 0, 0); _camMatrix.put(2, 1, 0); _camMatrix.put(2, 2, 1.0f); _distCoeffs = new MatOfDouble(0, 0, 0, 0); // カメラキャリブレーション Matrix4x4 P = ARUtils.CalculateProjectionMatrixFromCameraMatrixValues((float)fx, (float)fy, (float)cx, (float)cy, normWidth, normHeight, 0.3f, 2000f); Matrix4x4 V = Matrix4x4.TRS(Vector3.zero, Quaternion.identity, new Vector3(1, 1, -1)); _VP = P * V; _normPoints = new List(68); for (int i = 0; i < 68; i++) _normPoints.Add(new Vector2(0, 0)); } public void OnLateUpdate() { var landmarks = faceDetector.Landmarks; Vector3 angles = GetFrontalFaceAngle(landmarks); // 検出ミスしたら補正をかける float angleX = (angles.x > 180) ? angles.x - 360 : angles.x; float angleY = (angles.y > 180) ? angles.y - 360 : angles.y; float angleZ = (angles.z > 180) ? angles.z - 360 : angles.z; angleX = (angles.x < -180) ? angles.x + 360 : angles.x; angleY = (angles.y < -180) ? angles.y + 360 : angles.y; angleZ = (angles.z < -180) ? angles.z + 360 : angles.z; // 座標系を変換 float temp = angleX; angleX = -angleY; angleY = temp; angleZ = -angleZ; // 線形補間によるスムージング _headRotation = new Vector3( Mathf.LerpAngle(_headRotation.x, angleX, lerpT), Mathf.LerpAngle(_headRotation.y, angleY, lerpT), Mathf.LerpAngle(_headRotation.z, angleZ, lerpT)); SetParameter(headAngleParameterX, _headRotation.x); SetParameter(headAngleParameterY, _headRotation.y); SetParameter(headAngleParameterZ, _headRotation.z); } public bool HasUpdateController { get; set; } private void LateUpdate() { // CubismUpdateControllerがアタッチされていない場合、CubismExampleController自身のイベント関数から更新処理を行う if (!HasUpdateController) { OnLateUpdate(); } } /// /// 顔の向きを取得 /// /// private Vector3 GetFrontalFaceAngle(List points) { int position_flag = 0; if (points.Count != 68) throw new ArgumentNullException("ランドマークが正しくありません。"); if (_camMatrix == null) throw new ArgumentNullException("カメラの内部パラメータが正しくありません。"); if (position_flag == 1) { ///顔の位置をVの座標に強制的にするコード Vector3 position = transform.position; position.x = (points[0].x + points[1].x + points[2].x) / 1500; position.y = -1 * (points[0].y + points[1].y + points[2].y) / 1500 + 1; transform.position = position; Vector3 scale = transform.localScale; scale.x = (points[0].x - points[28].x) / -100; scale.y = (points[0].x - points[28].x) / -100; scale.z = (points[0].x - points[28].x) / -100; //transform.localScale = scale; } // スケールの正規化 float normScale = Math.Abs(points[30].y - points[8].y) / (normHeight / 2); Vector2 normDiff = points[30] * normScale - new Vector2(normWidth / 2, normHeight / 2); for (int i = 0; i < points.Count; i++) _normPoints[i] = points[i] * normScale - normDiff; _imagePoints.fromArray( new Point((_normPoints[38].x + _normPoints[41].x) / 2, (_normPoints[38].y + _normPoints[41].y) / 2), // 左目 new Point((_normPoints[43].x + _normPoints[46].x) / 2, (_normPoints[43].y + _normPoints[46].y) / 2), // 右目 new Point(_normPoints[33].x, _normPoints[33].y), // 鼻 new Point(_normPoints[48].x, _normPoints[48].y), // 左口角 new Point(_normPoints[54].x, _normPoints[54].y), // 右口角 new Point(_normPoints[0].x, _normPoints[0].y), // 左耳 new Point(_normPoints[16].x, _normPoints[16].y) // 右耳 ); // 2-3次元対応点から頭部を姿勢推定する if (_rVec == null || _tVec == null) { _rVec = new Mat(3, 1, CvType.CV_64FC1); _tVec = new Mat(3, 1, CvType.CV_64FC1); Calib3d.solvePnP(_objectPoints, _imagePoints, _camMatrix, _distCoeffs, _rVec, _tVec); } double tVecX = _tVec.get(0, 0)[0]; double tVecY = _tVec.get(1, 0)[0]; double tVecZ = _tVec.get(2, 0)[0]; bool isNotInViewport = false; Vector4 pos = _VP * new Vector4((float)tVecX, (float)tVecY, (float)tVecZ, 1.0f); if (pos.w != 0) { float x = pos.x / pos.w, y = pos.y / pos.w, z = pos.z / pos.w; if (x < -1.0f || x > 1.0f || y < -1.0f || y > 1.0f || z < -1.0f || z > 1.0f) isNotInViewport = true; } // オブジェクトがカメラ視野に存在しない場合、外部パラメータを使用しない if (double.IsNaN(tVecZ) || isNotInViewport) { Calib3d.solvePnP(_objectPoints, _imagePoints, _camMatrix, _distCoeffs, _rVec, _tVec); } else { Calib3d.solvePnP(_objectPoints, _imagePoints, _camMatrix, _distCoeffs, _rVec, _tVec, true, Calib3d.SOLVEPNP_ITERATIVE); } if (!isNotInViewport) { // Unityのポーズデータに変換 double[] rVecArr = new double[3]; _rVec.get(0, 0, rVecArr); double[] tVecArr = new double[3]; _tVec.get(0, 0, tVecArr); PoseData poseData = ARUtils.ConvertRvecTvecToPoseData(rVecArr, tVecArr); // 閾値以下ならば更新を無視 ARUtils.LowpassPoseData(ref _oldPoseData, ref poseData, positionLowPass, rotationLowPass); _oldPoseData = poseData; // 変換行列を作成 _transformationM = Matrix4x4.TRS(poseData.pos, poseData.rot, Vector3.one); } Calib3d.Rodrigues(_rVec, _rotM); _transformationM.SetRow(0, new Vector4( (float)_rotM.get(0, 0)[0], (float)_rotM.get(0, 1)[0], (float)_rotM.get(0, 2)[0], (float)_tVec.get(0, 0)[0])); _transformationM.SetRow(1, new Vector4( (float)_rotM.get(1, 0)[0], (float)_rotM.get(1, 1)[0], (float)_rotM.get(1, 2)[0], (float)_tVec.get(1, 0)[0])); _transformationM.SetRow(2, new Vector4( (float)_rotM.get(2, 0)[0], (float)_rotM.get(2, 1)[0], (float)_rotM.get(2, 2)[0], (float)_tVec.get(2, 0)[0])); _transformationM.SetRow(3, new Vector4( 0, 0, 0, 1)); _ARM = _invertYM * _transformationM * _invertYM; _ARM = _ARM * _invertYM * _invertZM; return ExtractRotationFromMatrix(ref _ARM).eulerAngles; } /// /// 変換行列からクォータニオンを導出 /// private static Quaternion ExtractRotationFromMatrix(ref Matrix4x4 matrix) { Vector3 forward; forward.x = matrix.m02; forward.y = matrix.m12; forward.z = matrix.m22; Vector3 upwards; upwards.x = matrix.m01; upwards.y = matrix.m11; upwards.z = matrix.m21; return Quaternion.LookRotation(forward, upwards); } void SetParameter(CubismParameter parameter, float value) { if (parameter != null) { parameter.Value = Mathf.Clamp(value, parameter.MinimumValue, parameter.MaximumValue); } } } } ------コードここまで これで顔と連動した髪や胸の揺れの物理演算が反映されました。偉い。 次に前回抜けていたmocopiを使った動きのコードについて記載。 非アクティブでもキー入力を拾うためにunity-raw-inputを以下のページよりインポートします。 https://github.com/elringus/unity-raw-input 分かりやすい所で以下の「AvatarToLive2D.cs」を作ってVtuberのモデルへアタッチ。 ------コードここから(AvatarToLive2D.cs) using UnityEngine; using System; using System.Collections.Generic; using Live2D.Cubism.Core; using OpenCVForUnity.CoreModule; using OpenCVForUnity.Calib3dModule; using OpenCVForUnity.UnityUtils; using UnityRawInput; namespace Live2D.Cubism.Framework.Physics { public class AvatarToLive2D : MonoBehaviour, ICubismUpdatable { // Sceneの非実行中に実行順の制御を行うか public bool NeedsUpdateOnEditing { get { return false; } } // このコンポーネントの実行順 public int ExecutionOrder { get { return 702; } } // 実行順序が制御されるアップデート関数 // 実行順が制御されているか public bool HasUpdateController { get; set; } //腕とかの [SerializeField] CubismParameter AngleParameterX; [SerializeField] CubismParameter AngleParameterZ; [SerializeField] CubismParameter AngleParameterRarm; [SerializeField] CubismParameter AngleParameterLarm; [SerializeField] CubismParameter AngleParameterRarm2; [SerializeField] CubismParameter AngleParameterLarm2; [SerializeField] CubismParameter AngleParameterRarm3; [SerializeField] CubismParameter AngleParameterLarm3; [SerializeField] CubismParameter Eye2; [SerializeField] CubismParameter Panty1; [SerializeField] CubismParameter Panty2; static int hight = 100; int panty = 0; public void OnLateUpdate() { AngleParameterX.Value = GameObject.Find("human_low:_torso_1").transform.rotation.y * 30; AngleParameterZ.Value = GameObject.Find("human_low:_torso_1").transform.rotation.z * -60; AngleParameterRarm.Value = GameObject.Find("human_low:_r_shoulder").transform.rotation.z * 300-5; AngleParameterLarm.Value = GameObject.Find("human_low:_l_shoulder").transform.rotation.z * 300 - 5; AngleParameterRarm2.Value = GameObject.Find("human_low:_r_low_arm").transform.rotation.z * 300 + hight; AngleParameterLarm2.Value = GameObject.Find("human_low:_l_low_arm").transform.rotation.z * 300 + hight; RawInput.Start(); RawInput.WorkInBackground = true; if (RawInput.IsKeyDown(RawKey.F1)) { Eye2.Value = 1; panty += 2; Panty1.Value = panty; Panty2.Value = 0; } else { Eye2.Value = 0; panty = 0; Panty1.Value = panty; Panty2.Value = 1; } } private void LateUpdate() { // CubismUpdateControllerがアタッチされていない場合、CubismExampleController自身のイベント関数から更新処理を行う if (!HasUpdateController) { OnLateUpdate(); } } } } -----コードここまで アタッチした後、パラメータでAngleParameterXにVの横回転を設定という具合。

UnityでLive2Dの物理演算が反映されない? UnityでLive2Dの物理演算が反映されない?

More Creators