首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Unity【Lerp & Slerp】- 线性插值与球形插值的区别

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

作者头像
CoderZ
发布2022-08-29 16:42:34
发布2022-08-29 16:42:34
2.1K00
代码可运行
举报
运行总次数:0
代码可运行

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

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

测试代码如下:

代码语言:javascript
代码运行次数:0
运行
复制
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));
    }
}
代码语言:javascript
代码运行次数:0
运行
复制
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组件中的Position坐标和Rotation旋转进行插值运算时, 通常用Vector3中的插值函数去处理Position,用Quaternion中的插值函数去处理Rotation。

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

代码如下:

代码语言:javascript
代码运行次数:0
运行
复制
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得出的结果:

代码如下:

代码语言:javascript
代码运行次数:0
运行
复制
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));
        }
    }
}
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-03-05,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 当代野生程序猿 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档