Unity 之Vector3的球形插值Slerp详解

Vector3.Slerp 球形插值详解


首先上官方的文档信息,方面还没有看过的同学学习。

        static function Vector3 Slerp (Vector3 from, Vector3to, float t)

        Spherically interpolates between two vectors.

        球形插值在两个向量之间。

    我感觉叫“弧线插值”更直观一些。

        Interpolates from towards to by amount t. The returned vector's magnitude will be interpolated between magnitudesof from and to.

        通过t数值在from和to之间插值。返回的向量的长度将被插值到from到to的长度之间。

    另外官方API上面还给了一个例子如下

 

 
  1. //在日出和日落之间动画弧线

  2. usingUnityEngine;

  3. usingSystem.Collections;

  4.  
  5. publicclass example : MonoBehaviour

  6. {

  7. publicTransform sunrise;

  8. publicTransform sunset;

  9. voidUpdate()

  10. {

  11. //弧线的中心

  12. Vector3 center = sunrise.position + sunset.position * 0.5f;

  13. //向下移动中心,垂直于弧线

  14. center -= newVector3(0, 1, 0);

  15. //相对于中心在弧线上插值

  16. Vector3 riseRelCenter = sunrise.position - center;

  17. Vector3 setRelCenter = sunset.position - center;

  18. transform.position = Vector3.Slerp(riseRelCenter,

  19. setRelCenter, Time.time);

  20. transform.position += center;

  21. }

  22. }


 

下面进入对这个函数的详细解释。

    初识这个函数的同学们对这个函数都不是很理解,既然是球形插值了,那么为什么用这个函数的时候却这么复杂呢,又要找中心点,又要中心点偏移的弄了半天。其实这是从这个函数的实现方法所决定的。咱们还是上例子来看比较清楚。


首先定义两个向量 a(2,1,0); b(-2,1,0); 然后咱们以这两个坐标点和原点来构建一个三角形,如下图所示:



 

咱们以a和b两个向量来做插值,假设分10等份,代码比较简单,如下代码

 
  1. for(inti = 1; i < 10; ++i)

  2. {

  3. Vector3 drawVec = Vector3.Slerp(a, b, 0.1f * i);

  4. Debug.DrawLine(Vector3.zero, drawVec, Color.yellow);

  5. }


 

 

可以看到咱们并没有像官方给的例子那样,做那么多的操作,效果也是杠杠滴!

但是,你不能被表象所欺骗,这样的效果虽然可以,但是却无法控制插值的曲线,也就是那个弧度,虽然你可以调整向量a和向量b的值来调节弧度,比方说a(1,1,0),b(-1,1,0)效果如下,可以看到弧度已经明显变平很多。

但是我们在实际运用这个函数的时候往往向量a和向量b是固定的,我们想要的是控制这个弧度,那么怎么办呢,其实也就是改变画这个弧度的中心点位置。上面两个示意图上面中心点我们都是用的坐标原点,我们现在想要在不改变a和a的情况下来改变插值的弧度,就只能自己找出一个中心点,这也就是官方实例中求中心点的由来了。

 
  1. //弧线的中心

  2. Vector3 center = (a + b) * 0.5f;

  3. //我们把中心点向下移动中心,垂直于弧线

  4. center -= newVector3(0, 0.5f, 0);

  5. // 求出新的中心点到向量a和向量b的

  6. Vector3 vecA = a - center;

  7. Vector3 vecB = b - center;

  8. for(inti = 0; i <= 10; ++i)

  9. {

  10. Vector3 drawVec = Vector3.Slerp(vecA, vecB, 0.1f * i);

  11. Debug.DrawLine(center, drawVec, Color.yellow);

  12. }


 

 

 

求出中心点后我们再来画一个示意图看看

(至于为什么要 求出新的中心点到向量a和向量b的vecA和vecB是因为,我们在球形插值的时候要的是两个vector3,而这个vector3是要向量a和向量b到中心点的向量,如果我们不求出vecA和vecB的话不论你怎么插值,其实都是从坐标原点进行的插值,你是控制不了插值的弧度的。)

从上面的效果图我们可以看到插值出来的弧度开始和结束点并不是a、b两点,而是这两个点向下的偏移量,而这个偏移量正好是向量conter的负值,所以我们在求出drawVec之后需要对其做修正处理。

 

在求出drawVec之后加上下面的代码

drawVec += center;

 

 

然后再看效果图

现在的效果图就是我们想要的插值效果了,要想控制弧度,只用调节centor的偏移量就可以了。

比方说我们加上这样一条

center -= new Vector3(0, 2f, 0);

可以看到效果如下

这个弧度是不是就更平了呢。

 

好,看到现在还没有睡着的同学们,我只能说一句你们有福了,下面可是大餐哦。

 

上面咱们介绍的都是有局限性的,比方说向量a和向量b对于Y轴可是左右对称的,并且X,Y轴的值也是相等的,这在实际运用中可是非常不常见的,现在咱们对向量a做一个比较小的改动看看,把向量a改为(2,1,0),那么效果如下所示

哇咔咔,居然还是好好的球形插值啊,哈哈,各位同学别激动,咱们把向量a的Y轴也调整一下看看,把a改为(2,4,0)效果如下图所示

 

哇咔咔。。效果是不是非常明显啊,说好的球形插值呢?怎么成了这个样子!!哈哈,各位同学别着急啊,咱们这个球形插值和核心其实就是center点的位置,只要我们求的这个center点的位置在a和b连线中心点的垂线上面,那么就是一个完整的左右对称的插值了。


 

下面咱们加入如下代码

 

 
  1. Vector3 centorProject = Vector3.Project(centor, mStart - mEnd); // 中心点在两点之间的投影

  2. centor = Vector3.MoveTowards(centor, centorProject, 1f); // 沿着投影方向移动移动距离(距离越大弧度越小)


 

效果如下所示(中心的垂线和起始两条线用蓝色标示出来了)

所以我们完整的运用Vector3.Slerp的代码应该是这样子滴

 
  1. //在日出和日落之间动画弧线

  2. usingUnityEngine;

  3. usingSystem.Collections;

  4.  
  5. publicclass example : MonoBehaviour

  6. {

  7. publicTransform sunrise;

  8. publicTransform sunset;

  9. voidUpdate()

  10. {

  11. //弧线的中心

  12. Vector3 center = sunrise.position + sunset.position * 0.5f;

  13. Vector3 centorProject = Vector3.Project(centor, sunrise.position - sunset.position); // 中心点在两点之间的投影

  14. centor = Vector3.MoveTowards(centor, centorProject, 1f); // 沿着投影方向移动移动距离(距离越大弧度越小)

  15. //相对于中心在弧线上插值

  16. Vector3 riseRelCenter = sunrise.position - center;

  17. Vector3 setRelCenter = sunset.position - center;

  18. transform.position = Vector3.Slerp(riseRelCenter, setRelCenter, Time.time);

  19. transform.position += center;

  20. }

  21. }


 

 

看到这里各位同学是不是觉得已经掌握了Vector3.Slerp了呢?O(∩_∩)O哈哈~

 

NONONO!!!!


 

咱们上面的实例还是有局限性滴,谁告诉你了Vector3的Z轴必须是0了?,咱们对a和b的Z轴在做修改。a((2, 4, -1)),b((-1, 1, 2)),看下效果图

哇咔咔。。是不是又出问题了?还需要改代码?NO!这是因为我们锁定了视角方向来看的,看上面的2D 选项是不是已经选择了啊,哈哈。那么既然Z轴有值了我们就不能已平面视角来看了,等我们把2D锁定给关闭了转换个视角看看。

这样看是不是就顺眼多了呢?哈哈,至此我们的讲解总算结束了,大家可以洗洗了,哈哈。。。

等下!谁说可以睡了?我可没说哦,难道大家对我说的都这么信吗?有句古话说的好啊,尽信书则不如无书。。

 

 
  1. // 沿着投影方向移动移动距离(距离越大弧度越小)

  2. centor = Vector3.MoveTowards(centor, centorProject, 1f);

 

在这句代码中我写了个注释(距离越大弧度越小)那么谁能告诉我距离越小会怎么样呢?大家是不是觉得距离越小弧度就越大呢?嘿嘿。。这就掉坑里了吧。

    这个距离的小是相对的,尽量是不能小于0.01的,至于为啥是0.01呢,我就试了下0.01和0.001。。。哈哈。。就是这么不负责。。

    如果这个距离小于0.01的话插值的方向就是不可控了,至于为什么呢?因为过于小的话就是a到b两点之间的一条线了,垂直于一条线的平面可是海里去了,谁知道在哪里呢。所以大家在用的时候切记这点啊,不能过于追求极限导致结果不可控。还有。。我是不是比较啰嗦啊。。上面我说的是距离啊,是距离,不是值,这个值是可以为负值的啊,负值的话插值的弧线就在这边了,转个向而已。

 

好了,讲解总算是可以结束了。累死我了。。哇咔咔。。

下面是测试源码,有兴趣的同学玩玩吧

 
  1. privateVector3 mStart = newVector3(2, 4, -1);

  2. privateVector3 mEnd = newVector3(-1, 1, 2);

  3. // Update is called once per frame

  4. privatevoid Update()

  5. {

  6. Debug.DrawLine(newVector3(-100, 0, 0), newVector3(100, 0, 0), Color.green);

  7. Debug.DrawLine(newVector3(0, -100, 0), newVector3(0, 100, 0), Color.green);

  8.  
  9.  
  10. Debug.DrawLine(Vector3.zero, mStart, Color.red);

  11. Debug.DrawLine(Vector3.zero, mEnd, Color.red);

  12.  
  13. Debug.DrawLine(mStart, mEnd, Color.red);

  14.  
  15. Vector3 centor = (mStart + mEnd) * 0.5f;

  16. Vector3 centorProject = Vector3.Project(centor, mStart - mEnd); // 中心点在两点之间的投影

  17. centor = Vector3.MoveTowards(centor, centorProject, 1f); // 沿着投影方向移动移动距离(距离越大弧度越小)

  18.  
  19. Debug.DrawLine(centor, mStart, Color.blue);

  20. Debug.DrawLine(centor, mEnd, Color.blue);

  21.  
  22. Debug.Log(string.Format("{0} : {1}", Vector3.Distance(centor, mStart), Vector3.Distance(centor, mEnd)));

  23.  
  24. for(inti = 1; i < 10; ++i)

  25. {

  26. Vector3 drawVec = Vector3.Slerp(mEnd - centor, mStart - centor, 0.1f * i);

  27. drawVec += centor;

  28. Debug.DrawLine(centor, drawVec, 5 == i ? Color.blue : Color.yellow);

  29. }

  30. }


 

转载:http://www.manew.com/thread-43314-1-1.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值