Unity【Lerp & Slerp】- 线性插值与球形插值的区别

举报
CoderZ1010 发表于 2022/09/25 04:59:46 2022/09/25
【摘要】 在Unity的向量Vector和四元数Quaternion类中,均包含线性插值Lerp和球形插值Slerp的函数,那么两者之间有何区别,通过下面的例子进行观察: 图一中黄色线与红色线相交的点是从点A到点B进行线性插值得出的结果,图二则是球形插值得出的结果,或许称之为弧形插值更容易理解。二者的区别从图中可以明显看出,从四元数的角...

在Unity的向量Vector和四元数Quaternion类中,均包含线性插值Lerp和球形插值Slerp的函数,那么两者之间有何区别,通过下面的例子进行观察:

图一中黄色线红色线相交的点是从点A到点B进行线性插值得出的结果,图二则是球形插值得出的结果,或许称之为弧形插值更容易理解。二者的区别从图中可以明显看出,从四元数的角度来看,线性插值每帧得出的旋转结果是不均匀的,从代数的角度思考,如果两个单位四元数之间进行插值,如图一中的线性插值,得到的四元数并不是单位四元数,因此球形插值更为合理,因为它是不改变长度的。

测试代码如下:


  
  1. using UnityEngine;
  2. using UnityEditor;
  3. /// <summary>
  4. /// 线性插值
  5. /// </summary>
  6. public class LerpExample : MonoBehaviour
  7. {
  8. [SerializeField] private Transform a; //点A
  9. [SerializeField] private Transform b; //点B
  10. private void OnDrawGizmos()
  11. {
  12. Handles.Label(transform.position, "O");
  13. Handles.Label(a.position, "A");
  14. Handles.Label(b.position, "B");
  15. Handles.color = Color.cyan;
  16. //以transform.position作为点O
  17. //绘制OA线段
  18. Handles.DrawLine(transform.position, a.position);
  19. //绘制OB线段
  20. Handles.DrawLine(transform.position, b.position);
  21. for (int i = 1; i < 10; i++)
  22. {
  23. //插值点
  24. Vector3 l = Vector3.Lerp(a.position, b.position, i * .1f);
  25. Handles.color = Color.red;
  26. //绘制点O到插值点的线段
  27. Handles.DrawLine(transform.position, l);
  28. Handles.color = Color.yellow;
  29. //绘制插值点之间的线段
  30. Handles.DrawLine(l, Vector3.Lerp(a.position, b.position, (i - 1) * .1f));
  31. Handles.Label(l, $"线性插值{i}");
  32. }
  33. Handles.DrawLine(b.position, Vector3.Lerp(a.position, b.position, .9f));
  34. }
  35. }

  
  1. using UnityEngine;
  2. using UnityEditor;
  3. /// <summary>
  4. /// 球形插值
  5. /// </summary>
  6. public class SlerpExample : MonoBehaviour
  7. {
  8. [SerializeField] private Transform a; //点A
  9. [SerializeField] private Transform b; //点B
  10. private void OnDrawGizmos()
  11. {
  12. Handles.Label(transform.position, "O");
  13. Handles.Label(a.position, "A");
  14. Handles.Label(b.position, "B");
  15. Handles.color = Color.cyan;
  16. //以transform.position作为点O
  17. //绘制OA线段
  18. Handles.DrawLine(transform.position, a.position);
  19. //绘制OB线段
  20. Handles.DrawLine(transform.position, b.position);
  21. for (int i = 1; i < 10; i++)
  22. {
  23. //插值点
  24. Vector3 l = Vector3.Slerp(a.position, b.position, i * .1f);
  25. Handles.color = Color.red;
  26. //绘制点O到插值点的线段
  27. Handles.DrawLine(transform.position, l);
  28. Handles.color = Color.yellow;
  29. //绘制插值点之间的线段
  30. Handles.DrawLine(l, Vector3.Slerp(a.position, b.position, (i - 1) * .1f));
  31. Handles.Label(l, $"球形插值{i}");
  32. }
  33. Handles.DrawLine(b.position, Vector3.Slerp(a.position, b.position, .9f));
  34. }
  35. }

在对Transform组件中的Position坐标和Rotation旋转进行插值运算时, 通常用Vector3中的插值函数去处理Position,用Quaternion中的插值函数去处理Rotation。

如果我们使用Vector3中的插值函数去处理Rotation,则会出现如下这种情况:

代码如下:


  
  1. using UnityEngine;
  2. using System.Collections;
  3. public class Example : MonoBehaviour
  4. {
  5. [SerializeField] private Transform point1;
  6. [SerializeField] private Transform point2;
  7. private IEnumerator ExampleCoroutine(Transform target)
  8. {
  9. float beginTime = Time.time;
  10. Vector3 beginPosition = transform.position;
  11. Vector3 beginRotation = transform.eulerAngles;
  12. for (var duration = 2f; (Time.time - beginTime) < duration;)
  13. {
  14. float t = (Time.time - beginTime) / duration;
  15. transform.position = Vector3.Lerp(beginPosition, target.position, t);
  16. transform.eulerAngles = Vector3.Lerp(beginRotation, target.eulerAngles, t);
  17. yield return null;
  18. }
  19. transform.position = target.position;
  20. transform.eulerAngles = target.rotation.eulerAngles;
  21. }
  22. private void OnGUI()
  23. {
  24. if (GUILayout.Button("POINT1", GUILayout.Width(200f), GUILayout.Height(50f)))
  25. {
  26. StartCoroutine(ExampleCoroutine(point1));
  27. }
  28. if (GUILayout.Button("POINT2", GUILayout.Width(200f), GUILayout.Height(50f)))
  29. {
  30. StartCoroutine(ExampleCoroutine(point2));
  31. }
  32. }
  33. }

下面是使用Quaternion.Lerp得出的结果:

代码如下:


  
  1. using UnityEngine;
  2. using System.Collections;
  3. public class Example : MonoBehaviour
  4. {
  5. [SerializeField] private Transform point1;
  6. [SerializeField] private Transform point2;
  7. private IEnumerator ExampleCoroutine(Transform target)
  8. {
  9. float beginTime = Time.time;
  10. Vector3 beginPosition = transform.position;
  11. Quaternion beginRotation = transform.rotation;
  12. for (var duration = 2f; (Time.time - beginTime) < duration;)
  13. {
  14. float t = (Time.time - beginTime) / duration;
  15. transform.position = Vector3.Lerp(beginPosition, target.position, t);
  16. transform.rotation = Quaternion.Lerp(beginRotation, target.rotation, t);
  17. yield return null;
  18. }
  19. transform.position = target.position;
  20. transform.rotation = target.rotation;
  21. }
  22. private void OnGUI()
  23. {
  24. if (GUILayout.Button("POINT1", GUILayout.Width(200f), GUILayout.Height(50f)))
  25. {
  26. StartCoroutine(ExampleCoroutine(point1));
  27. }
  28. if (GUILayout.Button("POINT2", GUILayout.Width(200f), GUILayout.Height(50f)))
  29. {
  30. StartCoroutine(ExampleCoroutine(point2));
  31. }
  32. }
  33. }

文章来源: coderz.blog.csdn.net,作者:CoderZ1010,版权归原作者所有,如需转载,请联系作者。

原文链接:coderz.blog.csdn.net/article/details/123281928

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。