在Unity的向量Vector和四元数Quaternion类中,均包含线性插值Lerp和球形插值Slerp的函数,那么两者之间有何区别,通过下面的例子进行观察:
图一中黄色线与红色线相交的点是从点A到点B进行线性插值得出的结果,图二则是球形插值得出的结果,或许称之为弧形插值更容易理解。二者的区别从图中可以明显看出,从四元数的角度来看,线性插值每帧得出的旋转结果是不均匀的,从代数的角度思考,如果两个单位四元数之间进行插值,如图一中的线性插值,得到的四元数并不是单位四元数,因此球形插值更为合理,因为它是不改变长度的。
测试代码如下:
usingUnityEngine; usingUnityEditor; /// <summary>/// 线性插值/// </summary>publicclassLerpExample : MonoBehaviour{ [SerializeField] privateTransforma; //点A [SerializeField] privateTransformb; //点BprivatevoidOnDrawGizmos() { 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 (inti=1; i<10; i++) { //插值点Vector3l=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)); } }
usingUnityEngine; usingUnityEditor; /// <summary>/// 球形插值/// </summary>publicclassSlerpExample : MonoBehaviour{ [SerializeField] privateTransforma; //点A [SerializeField] privateTransformb; //点BprivatevoidOnDrawGizmos() { 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 (inti=1; i<10; i++) { //插值点Vector3l=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组件中的Position坐标和Rotation旋转进行插值运算时, 通常用Vector3中的插值函数去处理Position,用Quaternion中的插值函数去处理Rotation。
如果我们使用Vector3中的插值函数去处理Rotation,则会出现如下这种情况:
代码如下:
usingUnityEngine; usingSystem.Collections; publicclassExample : MonoBehaviour{ [SerializeField] privateTransformpoint1; [SerializeField] privateTransformpoint2; privateIEnumeratorExampleCoroutine(Transformtarget) { floatbeginTime=Time.time; Vector3beginPosition=transform.position; Vector3beginRotation=transform.eulerAngles; for (varduration=2f; (Time.time-beginTime) <duration;) { floatt= (Time.time-beginTime) /duration; transform.position=Vector3.Lerp(beginPosition, target.position, t); transform.eulerAngles=Vector3.Lerp(beginRotation, target.eulerAngles, t); yieldreturnnull; } transform.position=target.position; transform.eulerAngles=target.rotation.eulerAngles; } privatevoidOnGUI() { 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得出的结果:
代码如下:
usingUnityEngine; usingSystem.Collections; publicclassExample : MonoBehaviour{ [SerializeField] privateTransformpoint1; [SerializeField] privateTransformpoint2; privateIEnumeratorExampleCoroutine(Transformtarget) { floatbeginTime=Time.time; Vector3beginPosition=transform.position; QuaternionbeginRotation=transform.rotation; for (varduration=2f; (Time.time-beginTime) <duration;) { floatt= (Time.time-beginTime) /duration; transform.position=Vector3.Lerp(beginPosition, target.position, t); transform.rotation=Quaternion.Lerp(beginRotation, target.rotation, t); yieldreturnnull; } transform.position=target.position; transform.rotation=target.rotation; } privatevoidOnGUI() { 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)); } } }