Fix : 페이셜 노이즈 제거 기능 추가
This commit is contained in:
parent
1b5530240f
commit
76906a6380
@ -31,6 +31,26 @@ public class UnityRecieve_FACEMOTION3D_and_iFacialMocap : MonoBehaviour
|
|||||||
private string lastProcessedMessage = ""; // 이전 메시지 저장용
|
private string lastProcessedMessage = ""; // 이전 메시지 저장용
|
||||||
public int LOCAL_PORT = 49983;
|
public int LOCAL_PORT = 49983;
|
||||||
|
|
||||||
|
// 데이터 필터링 설정
|
||||||
|
[Header("Data Filtering")]
|
||||||
|
[Tooltip("데이터 스무딩/필터링 활성화")]
|
||||||
|
public bool enableFiltering = true;
|
||||||
|
[Tooltip("스무딩 강도 (0=필터없음, 1=최대 스무딩)")]
|
||||||
|
[Range(0f, 0.95f)]
|
||||||
|
public float smoothingFactor = 0.5f;
|
||||||
|
[Tooltip("프레임 간 최대 허용 변화량 (BlendShape, 0~100 스케일)")]
|
||||||
|
[Range(1f, 100f)]
|
||||||
|
public float maxBlendShapeDelta = 30f;
|
||||||
|
[Tooltip("프레임 간 최대 허용 회전 변화량 (도)")]
|
||||||
|
[Range(1f, 90f)]
|
||||||
|
public float maxRotationDelta = 25f;
|
||||||
|
|
||||||
|
// 필터링용 이전 값 저장
|
||||||
|
private Dictionary<string, float> prevBlendShapeValues = new Dictionary<string, float>();
|
||||||
|
private Dictionary<string, Vector3> prevBoneRotations = new Dictionary<string, Vector3>();
|
||||||
|
private Vector3 prevHeadPosition = Vector3.zero;
|
||||||
|
private bool hasFirstFrame = false;
|
||||||
|
|
||||||
// 성능 최적화를 위한 캐시
|
// 성능 최적화를 위한 캐시
|
||||||
private Dictionary<string, List<BlendShapeMapping>> blendShapeCache;
|
private Dictionary<string, List<BlendShapeMapping>> blendShapeCache;
|
||||||
private readonly char[] splitEquals = new char[] { '=' };
|
private readonly char[] splitEquals = new char[] { '=' };
|
||||||
@ -238,9 +258,14 @@ public class UnityRecieve_FACEMOTION3D_and_iFacialMocap : MonoBehaviour
|
|||||||
// 정규화된 이름으로 캐시 검색
|
// 정규화된 이름으로 캐시 검색
|
||||||
string normalizedName = NormalizeBlendShapeName(shapeName).ToLowerInvariant();
|
string normalizedName = NormalizeBlendShapeName(shapeName).ToLowerInvariant();
|
||||||
|
|
||||||
|
// 필터링 적용
|
||||||
|
if (enableFiltering)
|
||||||
|
{
|
||||||
|
weight = FilterBlendShapeValue(normalizedName, weight);
|
||||||
|
}
|
||||||
|
|
||||||
if (blendShapeCache.TryGetValue(normalizedName, out List<BlendShapeMapping> mappings))
|
if (blendShapeCache.TryGetValue(normalizedName, out List<BlendShapeMapping> mappings))
|
||||||
{
|
{
|
||||||
// 캐시에서 찾은 모든 매핑에 대해 weight 설정
|
|
||||||
foreach (var mapping in mappings)
|
foreach (var mapping in mappings)
|
||||||
{
|
{
|
||||||
if (mapping.renderer != null)
|
if (mapping.renderer != null)
|
||||||
@ -348,10 +373,18 @@ public class UnityRecieve_FACEMOTION3D_and_iFacialMocap : MonoBehaviour
|
|||||||
y = -y; // Y축 회전 반전
|
y = -y; // Y축 회전 반전
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Vector3 rotation = new Vector3(x, y, z);
|
||||||
|
|
||||||
|
// 본 회전 필터링 적용
|
||||||
|
if (enableFiltering)
|
||||||
|
{
|
||||||
|
rotation = FilterBoneRotation(strArray2[0], rotation);
|
||||||
|
}
|
||||||
|
|
||||||
switch (strArray2[0])
|
switch (strArray2[0])
|
||||||
{
|
{
|
||||||
case "head" when headBone != null:
|
case "head" when headBone != null:
|
||||||
headBone.localRotation = Quaternion.Euler(x, y, -z);
|
headBone.localRotation = Quaternion.Euler(rotation.x, rotation.y, -rotation.z);
|
||||||
|
|
||||||
if (headPositionObject != null && commaList.Length >= 6)
|
if (headPositionObject != null && commaList.Length >= 6)
|
||||||
{
|
{
|
||||||
@ -361,19 +394,24 @@ public class UnityRecieve_FACEMOTION3D_and_iFacialMocap : MonoBehaviour
|
|||||||
|
|
||||||
if (mirrorMode)
|
if (mirrorMode)
|
||||||
{
|
{
|
||||||
posX = -posX; // X축 위치 반전
|
posX = -posX;
|
||||||
}
|
}
|
||||||
|
|
||||||
headPositionObject.localPosition = new Vector3(posX, posY, posZ);
|
Vector3 newPos = new Vector3(posX, posY, posZ);
|
||||||
|
if (enableFiltering)
|
||||||
|
{
|
||||||
|
newPos = FilterHeadPosition(newPos);
|
||||||
|
}
|
||||||
|
headPositionObject.localPosition = newPos;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "rightEye" when rightEyeBone != null:
|
case "rightEye" when rightEyeBone != null:
|
||||||
rightEyeBone.localRotation = Quaternion.Euler(x, y, z);
|
rightEyeBone.localRotation = Quaternion.Euler(rotation.x, rotation.y, rotation.z);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "leftEye" when leftEyeBone != null:
|
case "leftEye" when leftEyeBone != null:
|
||||||
leftEyeBone.localRotation = Quaternion.Euler(x, y, z);
|
leftEyeBone.localRotation = Quaternion.Euler(rotation.x, rotation.y, rotation.z);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -522,6 +560,82 @@ public class UnityRecieve_FACEMOTION3D_and_iFacialMocap : MonoBehaviour
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// BlendShape 값 필터링: 스파이크 제거 + EMA 스무딩
|
||||||
|
/// </summary>
|
||||||
|
float FilterBlendShapeValue(string name, float rawValue)
|
||||||
|
{
|
||||||
|
if (prevBlendShapeValues.TryGetValue(name, out float prevValue))
|
||||||
|
{
|
||||||
|
float delta = Mathf.Abs(rawValue - prevValue);
|
||||||
|
|
||||||
|
// 스파이크 감지: 변화량이 임계값 초과 시 이전 값 유지
|
||||||
|
if (delta > maxBlendShapeDelta)
|
||||||
|
{
|
||||||
|
return prevValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// EMA 스무딩 적용
|
||||||
|
float smoothed = Mathf.Lerp(rawValue, prevValue, smoothingFactor);
|
||||||
|
prevBlendShapeValues[name] = smoothed;
|
||||||
|
return smoothed;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 첫 프레임은 그대로 저장
|
||||||
|
prevBlendShapeValues[name] = rawValue;
|
||||||
|
return rawValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 본 회전 필터링: 스파이크 제거 + EMA 스무딩
|
||||||
|
/// </summary>
|
||||||
|
Vector3 FilterBoneRotation(string boneName, Vector3 rawRotation)
|
||||||
|
{
|
||||||
|
if (prevBoneRotations.TryGetValue(boneName, out Vector3 prevRot))
|
||||||
|
{
|
||||||
|
float delta = Vector3.Distance(rawRotation, prevRot);
|
||||||
|
|
||||||
|
// 스파이크 감지
|
||||||
|
if (delta > maxRotationDelta)
|
||||||
|
{
|
||||||
|
return prevRot;
|
||||||
|
}
|
||||||
|
|
||||||
|
// EMA 스무딩
|
||||||
|
Vector3 smoothed = Vector3.Lerp(rawRotation, prevRot, smoothingFactor);
|
||||||
|
prevBoneRotations[boneName] = smoothed;
|
||||||
|
return smoothed;
|
||||||
|
}
|
||||||
|
|
||||||
|
prevBoneRotations[boneName] = rawRotation;
|
||||||
|
return rawRotation;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 머리 위치 필터링
|
||||||
|
/// </summary>
|
||||||
|
Vector3 FilterHeadPosition(Vector3 rawPos)
|
||||||
|
{
|
||||||
|
if (hasFirstFrame)
|
||||||
|
{
|
||||||
|
float delta = Vector3.Distance(rawPos, prevHeadPosition);
|
||||||
|
|
||||||
|
// 위치 스파이크 감지 (0.1 단위 기준)
|
||||||
|
if (delta > maxRotationDelta * 0.01f)
|
||||||
|
{
|
||||||
|
return prevHeadPosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector3 smoothed = Vector3.Lerp(rawPos, prevHeadPosition, smoothingFactor);
|
||||||
|
prevHeadPosition = smoothed;
|
||||||
|
return smoothed;
|
||||||
|
}
|
||||||
|
|
||||||
|
hasFirstFrame = true;
|
||||||
|
prevHeadPosition = rawPos;
|
||||||
|
return rawPos;
|
||||||
|
}
|
||||||
|
|
||||||
private bool HasBlendShapes(SkinnedMeshRenderer skin)
|
private bool HasBlendShapes(SkinnedMeshRenderer skin)
|
||||||
{
|
{
|
||||||
if (!skin.sharedMesh)
|
if (!skin.sharedMesh)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user