[UE5] Control Rig 에서 Alpha Interpolate 노드 내부 구현 (5.3)

2024. 2. 20. 23:20강의/UE5 기타 강의들

- 언리얼 5의 기본 Foot IK를 살펴보면, Alpha Interpolate 노드를 찾아 볼 수 있습니다.

 

 

 

- 여기서 ZOffset_R_Target은 발에서 쏜 레이의 HitLocation을 의미합니다.

- 하지만, 인풋값이 하나라서 이것이 보간한다는건 알겠지만, 어떻게 동작하는지 이해하기 어려웠습니다.

- 언리얼 내부 코드(5.3)에서 Alpha Interpolate 의 구현을 엿볼 수 있습니다.

 

 1. Alpha Interpolate

 

	FRigVMFunction_AlphaInterp()
	{
		Value = Result = 0.f;
		ScaleBiasClamp = FInputScaleBiasClamp();
		bMapRange = ScaleBiasClamp.bMapRange;
		bClampResult = ScaleBiasClamp.bClampResult;
		bInterpResult = ScaleBiasClamp.bInterpResult;
		InRange = ScaleBiasClamp.InRange;
		OutRange = ScaleBiasClamp.OutRange;
		Scale = ScaleBiasClamp.Scale;
		Bias = ScaleBiasClamp.Bias;
		ClampMin = ScaleBiasClamp.ClampMin;
		ClampMax = ScaleBiasClamp.ClampMax;
		InterpSpeedIncreasing = ScaleBiasClamp.InterpSpeedIncreasing;
		InterpSpeedDecreasing = ScaleBiasClamp.InterpSpeedDecreasing;
	}
FRigVMFunction_AlphaInterp_Execute()
{
	ScaleBiasClamp.bMapRange = bMapRange;
	ScaleBiasClamp.bClampResult = bClampResult;
	ScaleBiasClamp.bInterpResult = bInterpResult;

	ScaleBiasClamp.InRange = InRange;
	ScaleBiasClamp.OutRange = OutRange;
	ScaleBiasClamp.ClampMin = ClampMin;
	ScaleBiasClamp.ClampMax = ClampMax;
	ScaleBiasClamp.Scale = Scale;
	ScaleBiasClamp.Bias = Bias;
	ScaleBiasClamp.InterpSpeedIncreasing = InterpSpeedIncreasing;
	ScaleBiasClamp.InterpSpeedDecreasing = InterpSpeedDecreasing;

	Result = ScaleBiasClamp.ApplyTo(Value, ExecuteContext.GetDeltaTime());
}

 

- 위 코드를 보면, 이 노드는 FInputScaleBiasClamp라는 구조체를 통해, 알고리즘을 실행한다는 것을 알 수 있습니다.

- 구조체에서 변수들을 저장해서 가지고 있다는 것 또한 알 수 있죠. (그래서 인풋값이 하나여도 동작)

 

2. FInputScaleBiasClamp

 

https://github.com/EpicGames/UnrealEngine/blob/072300df18a94f18077ca20a14224b5d99fee872/Engine/Source/Runtime/Engine/Classes/Animation/InputScaleBias.h#L77

FInputScaleBiasClamp()
		: bMapRange(false)
		, bClampResult(false)
		, bInterpResult(false)
		, bInitialized(false)
		, Scale(1.0f)
		, Bias(0.0f)
		, ClampMin(0.f)
		, ClampMax(1.f)
		, InterpSpeedIncreasing(10.f)
		, InterpSpeedDecreasing(10.f)
		, InterpolatedResult(0.f)

 

// https://github.com/EpicGames/UnrealEngine/blob/072300df18a94f18077ca20a14224b5d99fee872/Engine/Source/Runtime/Engine/Private/Animation/InputScaleBias.cpp#L116
float FInputScaleBiasClamp::ApplyTo(float Value, float InDeltaTime) const
{
	float Result = Value;

	if (bMapRange)
	{
		Result = FMath::GetMappedRangeValueUnclamped(InRange.ToVector2f(), OutRange.ToVector2f(), Result);
	}

	Result = Result * Scale + Bias;

	if (bClampResult)
	{
		Result = FMath::Clamp<float>(Result, ClampMin, ClampMax);
	}

	if (bInterpResult)
	{
		if (bInitialized)
		{
			const float InterpSpeed = (Result >= InterpolatedResult) ? InterpSpeedIncreasing : InterpSpeedDecreasing;
			Result = FMath::FInterpTo(InterpolatedResult, Result, InDeltaTime, InterpSpeed);
		}

		InterpolatedResult = Result;
	}

	bInitialized = true;
	return Result;
}

 

- FInputScaleBiasClamp는 내부적으로 InterpolateResult를 가지고 있으며, 초기값은 0.0 입니다.

- 그리고 ApplyTo가 실행될 때 마다 이 값과 Target 사이를 보간해나갑니다.

- 그렇게 우리는 최종적으로 Target이라는 값을 부드럽게 얻어낼 수 있습니다.

 

	/** Interpolate float from Current to Target. Scaled by distance to Target, so it has a strong start speed and ease out. */
	template<typename T1, typename T2 = T1, typename T3 = T2, typename T4 = T3>
	UE_NODISCARD static auto FInterpTo( T1  Current, T2 Target, T3 DeltaTime, T4 InterpSpeed )
	{
		using RetType = decltype(T1() * T2() * T3() * T4());
	
		// If no interp speed, jump to target value
		if( InterpSpeed <= 0.f )
		{
			return static_cast<RetType>(Target);
		}

		// Distance to reach
		const RetType Dist = Target - Current;

		// If distance is too small, just set the desired location
		if( FMath::Square(Dist) < UE_SMALL_NUMBER )
		{
			return static_cast<RetType>(Target);
		}

		// Delta Move, Clamp so we do not over shoot.
		const RetType DeltaMove = Dist * FMath::Clamp<RetType>(DeltaTime * InterpSpeed, 0.f, 1.f);

		return Current + DeltaMove;				
	}

 

- FInterpTo 함수는 위와 같이 거리를 구하고, 이 거리를 일정 비율만큼 이동시킵니다.

- 여기서, Current는 항상 업데이트되므로, Current 와 Target이 가까울 수 록 ease out

- 즉 처음엔 빠르지만, 점점 속도가 낮아지는 보간을 수행합니다.

 

 

---

소스코드 링크

 

https://github.com/EpicGames/UnrealEngine/blob/072300df18a94f18077ca20a14224b5d99fee872/Engine/Plugins/Runtime/RigVM/Source/RigVM/Public/RigVMFunctions/Simulation/RigVMFunction_AlphaInterp.h#L7

 

https://github.com/EpicGames/UnrealEngine/blob/072300df18a94f18077ca20a14224b5d99fee872/Engine/Plugins/Runtime/RigVM/Source/RigVM/Private/RigVMFunctions/Simulation/RigVMFunction_AlphaInterp.cpp#L7

 

 

---

 

 

https://github.com/EpicGames/UnrealEngine/blob/072300df18a94f18077ca20a14224b5d99fee872/Engine/Source/Runtime/Engine/Classes/Animation/InputScaleBias.h#L77

 

https://github.com/EpicGames/UnrealEngine/blob/072300df18a94f18077ca20a14224b5d99fee872/Engine/Source/Runtime/Engine/Private/Animation/InputScaleBias.cpp#L116