UnityのベクトルVectorクラスとquaternionQuaternionクラスには、線形補間Lerp関数と球面補間Slerp関数があります。したがって、この2つの違いは、次の例にあります。
図1で黄色い線と赤い線が交差する点は、点Aから点Bへの線形補間の結果であり、図2は、わかりやすくするために円弧補間と呼ばれる球形補間の結果です。両者の違いは図からはっきりとわかります。クォータニオンの観点からは、各フレームの線形補間によって得られた回転結果は均一ではありません。図1の線形補間などの補間を実行すると、得られたクォータニオンは均一ではありません。単位クォータニオンであるため、長さを変更しないため、球面補間の方が合理的です。
テストコードは次のとおりです。
using UnityEngine;
using UnityEditor;
/// <summary>
/// 线性插值
/// </summary>
public class LerpExample : MonoBehaviour
{
[SerializeField] private Transform a; //点A
[SerializeField] private Transform b; //点B
private void OnDrawGizmos()
{
Handles.Label(transform.position, "O");
Handles.Label(a.position, "A");
Handles.Label(b.position, "B");
Handles.color = Color.cyan;
//以transform.position作为点O
//绘制OA线段
Handles.DrawLine(transform.position, a.position);
//绘制OB线段
Handles.DrawLine(transform.position, b.position);
for (int i = 1; i < 10; i++)
{
//插值点
Vector3 l = Vector3.Lerp(a.position, b.position, i * .1f);
Handles.color = Color.red;
//绘制点O到插值点的线段
Handles.DrawLine(transform.position, l);
Handles.color = Color.yellow;
//绘制插值点之间的线段
Handles.DrawLine(l, Vector3.Lerp(a.position, b.position, (i - 1) * .1f));
Handles.Label(l, $"线性插值{i}");
}
Handles.DrawLine(b.position, Vector3.Lerp(a.position, b.position, .9f));
}
}
using UnityEngine;
using UnityEditor;
/// <summary>
/// 球形插值
/// </summary>
public class SlerpExample : MonoBehaviour
{
[SerializeField] private Transform a; //点A
[SerializeField] private Transform b; //点B
private void OnDrawGizmos()
{
Handles.Label(transform.position, "O");
Handles.Label(a.position, "A");
Handles.Label(b.position, "B");
Handles.color = Color.cyan;
//以transform.position作为点O
//绘制OA线段
Handles.DrawLine(transform.position, a.position);
//绘制OB线段
Handles.DrawLine(transform.position, b.position);
for (int i = 1; i < 10; i++)
{
//插值点
Vector3 l = Vector3.Slerp(a.position, b.position, i * .1f);
Handles.color = Color.red;
//绘制点O到插值点的线段
Handles.DrawLine(transform.position, l);
Handles.color = Color.yellow;
//绘制插值点之间的线段
Handles.DrawLine(l, Vector3.Slerp(a.position, b.position, (i - 1) * .1f));
Handles.Label(l, $"球形插值{i}");
}
Handles.DrawLine(b.position, Vector3.Slerp(a.position, b.position, .9f));
}
}
Transformコンポーネントで位置座標と回転回転を補間する場合、通常、Vector3の補間関数を使用して位置を処理し、Quaternionの補間関数を使用して回転を処理します。
Vector3の補間関数を使用して回転を処理すると、次の状況が発生します。
コードは次のように表示されます。
using UnityEngine;
using System.Collections;
public class Example : MonoBehaviour
{
[SerializeField] private Transform point1;
[SerializeField] private Transform point2;
private IEnumerator ExampleCoroutine(Transform target)
{
float beginTime = Time.time;
Vector3 beginPosition = transform.position;
Vector3 beginRotation = transform.eulerAngles;
for (var duration = 2f; (Time.time - beginTime) < duration;)
{
float t = (Time.time - beginTime) / duration;
transform.position = Vector3.Lerp(beginPosition, target.position, t);
transform.eulerAngles = Vector3.Lerp(beginRotation, target.eulerAngles, t);
yield return null;
}
transform.position = target.position;
transform.eulerAngles = target.rotation.eulerAngles;
}
private void OnGUI()
{
if (GUILayout.Button("POINT1", GUILayout.Width(200f), GUILayout.Height(50f)))
{
StartCoroutine(ExampleCoroutine(point1));
}
if (GUILayout.Button("POINT2", GUILayout.Width(200f), GUILayout.Height(50f)))
{
StartCoroutine(ExampleCoroutine(point2));
}
}
}
Quaternion.Lerpを使用した結果は次のとおりです。
コードは次のように表示されます。
using UnityEngine;
using System.Collections;
public class Example : MonoBehaviour
{
[SerializeField] private Transform point1;
[SerializeField] private Transform point2;
private IEnumerator ExampleCoroutine(Transform target)
{
float beginTime = Time.time;
Vector3 beginPosition = transform.position;
Quaternion beginRotation = transform.rotation;
for (var duration = 2f; (Time.time - beginTime) < duration;)
{
float t = (Time.time - beginTime) / duration;
transform.position = Vector3.Lerp(beginPosition, target.position, t);
transform.rotation = Quaternion.Lerp(beginRotation, target.rotation, t);
yield return null;
}
transform.position = target.position;
transform.rotation = target.rotation;
}
private void OnGUI()
{
if (GUILayout.Button("POINT1", GUILayout.Width(200f), GUILayout.Height(50f)))
{
StartCoroutine(ExampleCoroutine(point1));
}
if (GUILayout.Button("POINT2", GUILayout.Width(200f), GUILayout.Height(50f)))
{
StartCoroutine(ExampleCoroutine(point2));
}
}
}
パブリックアカウント「ContemporaryWildProgrammer」へようこそ