找回密码
 注册帐号

扫一扫,访问微社区

抢楼 抢楼 本帖为抢楼帖,欢迎抢楼! 

基础知识 Vector3的Slerp详解

31
回复
8914
查看
[ 复制链接 ]
3偶尔光临
170/300
排名
19948
昨日变化

1

主题

30

帖子

170

积分

Rank: 3Rank: 3Rank: 3

UID
44220
好友
0
蛮牛币
155
威望
0
注册时间
2014-9-9
在线时间
37 小时
最后登录
2016-9-4
2015-10-12 15:20:06 显示全部楼层 阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有帐号?注册帐号

x
本帖最后由 小可小飞 于 2015-10-12 15:20 编辑

Vector3.Slerp 球形插值详解

首先上官方的文档信息,方面还没有看过的同学学习。
        static function Vector3 Slerp (Vector3 from, Vector3to, float t)
        Description描述(翻译信息来自 U_鹰)
        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上面还给了一个例子如下
[C#] 纯文本查看 复制代码
[/font][/size][/align][align=left][size=4][font=宋体]//在日出和日落之间动画弧线
using UnityEngine;
using System.Collections;

public class example : MonoBehaviour 
{
        public Transform sunrise;
        public Transform sunset;
        void Update() 
        {
                //弧线的中心
                Vector3 center = sunrise.position + sunset.position *                 0.5f;
                //向下移动中心,垂直于弧线
                center -= new Vector3(0, 1, 0);
                //相对于中心在弧线上插值
                Vector3 riseRelCenter = sunrise.position - center;
                Vector3 setRelCenter = sunset.position - center;
                transform.position = Vector3.Slerp(riseRelCenter,
                                      setRelCenter, Time.time);
                transform.position += center;
        }
}

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

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



咱们以ab两个向量来做插值,假设分10等份,代码比较简单,如下代码
[C#] 纯文本查看 复制代码
[/font][/align][align=left][font=宋体]        for (int i = 1; i < 10; ++i)
        {
            Vector3 drawVec = Vector3.Slerp(a, b, 0.1f * i);
            Debug.DrawLine(Vector3.zero, drawVec, Color.yellow);
        }

可以看到咱们并没有像官方给的例子那样,做那么多的操作,效果也是杠杠滴!
2.png
但是,你不能被表象所欺骗,这样的效果虽然可以,但是却无法控制插值的曲线,也就是那个弧度,虽然你可以调整向量a和向量b的值来调节弧度,比方说a(1,1,0),b(-1,1,0)效果如下,可以看到弧度已经明显变平很多。
3.png
但是我们在实际运用这个函数的时候往往向量a和向量b是固定的,我们想要的是控制这个弧度,那么怎么办呢,其实也就是改变画这个弧度的中心点位置。上面两个示意图上面中心点我们都是用的坐标原点,我们现在想要在不改变aa的情况下来改变插值的弧度,就只能自己找出一个中心点,这也就是官方实例中求中心点的由来了。
[C#] 纯文本查看 复制代码
[/font][/align][align=left][font=宋体]        //弧线的中心
        Vector3 center = (a + b) * 0.5f;
        //我们把中心点向下移动中心,垂直于弧线
        center -= new Vector3(0, 0.5f, 0);
        // 求出新的中心点到向量a和向量b的
        Vector3 vecA = a - center;
        Vector3 vecB = b - center;
        for (int i = 0; i <= 10; ++i)
        {
            Vector3 drawVec = Vector3.Slerp(vecA, vecB, 0.1f * i);
            Debug.DrawLine(center, drawVec, Color.yellow);
        }


求出中心点后我们再来画一个示意图看看
4.png
(至于为什么要 求出新的中心点到向量a和向量b的vecA和vecB是因为,我们在球形插值的时候要的是两个vector3,而这个vector3是要向量a和向量b到中心点的向量,如果我们不求出vecA和vecB的话不论你怎么插值,其实都是从坐标原点进行的插值,你是控制不了插值的弧度的。)
从上面的效果图我们可以看到插值出来的弧度开始和结束点并不是a、b两点,而是这两个点向下的偏移量,而这个偏移量正好是向量conter的负值,所以我们在求出drawVec之后需要对其做修正处理。

在求出drawVec之后加上下面的代码
[C#] 纯文本查看 复制代码
drawVec += center;

然后再看效果图
5.png
现在的效果图就是我们想要的插值效果了,要想控制弧度,只用调节centor的偏移量就可以了。
比方说我们加上这样一条center -= new Vector3(0, 2f, 0);
可以看到效果如下
6.png
这个弧度是不是就更平了呢。

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

上面咱们介绍的都是有局限性的,比方说向量a和向量b对于Y轴可是左右对称的,并且XY轴的值也是相等的,这在实际运用中可是非常不常见的,现在咱们对向量a做一个比较小的改动看看,把向量a改为(2,1,0),那么效果如下所示
7.png
哇咔咔,居然还是好好的球形插值啊,哈哈,各位同学别激动,咱们把向量aY轴也调整一下看看,把a改为(2,4,0)效果如下图所示
8.png

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


下面咱们加入如下代码
[C#] 纯文本查看 复制代码
Vector3 centorProject = Vector3.Project(centor, mStart - mEnd); // 中心点在两点之间的投影
     centor = Vector3.MoveTowards(centor, centorProject, 1f); // 沿着投影方向移动移动距离(距离越大弧度越小)
效果如下所示(中心的垂线和起始两条线用蓝色标示出来了)
9.png
所以我们完整的运用Vector3.Slerp的代码应该是这样子滴
[C#] 纯文本查看 复制代码
[/font][/align][align=left][font=宋体]//在日出和日落之间动画弧线
using UnityEngine;
using System.Collections;

public class example : MonoBehaviour 
{
        public Transform sunrise;
        public Transform sunset;
        void Update() 
        {
                //弧线的中心
                Vector3 center = sunrise.position + sunset.position * 0.5f;
                Vector3 centorProject = Vector3.Project(centor, sunrise.position - sunset.position); // 中心点在两点之间的投影
                centor = Vector3.MoveTowards(centor, centorProject, 1f); // 沿着投影方向移动移动距离(距离越大弧度越小)                
                //相对于中心在弧线上插值
                Vector3 riseRelCenter = sunrise.position - center;
                Vector3 setRelCenter = sunset.position - center;
                transform.position = Vector3.Slerp(riseRelCenter, setRelCenter, Time.time);
                transform.position += center;
        }
}

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

NONONO!!!!


咱们上面的实例还是有局限性滴,谁告诉你了Vector3Z轴必须是0了?,咱们对abZ轴在做修改。a((2, 4, -1)),b((-1, 1, 2)),看下效果图
10.png
哇咔咔。。是不是又出问题了?还需要改代码?NO!这是因为我们锁定了视角方向来看的,看上面的2D 选项是不是已经选择了啊,哈哈。 那么既然Z轴有值了我们就不能已平面视角来看了,等我们把2D锁定给关闭了转换个视角看看。
11.png
这样看是不是就顺眼多了呢?哈哈,至此我们的讲解总算结束了,大家可以洗洗了,哈哈。。。
等下!谁说可以睡了?我可没说哦,难道大家对我说的都这么信吗?有句古话说的好啊,尽信书则不如无书。。
[C#] 纯文本查看 复制代码
centor = Vector3.MoveTowards(centor, centorProject, 1f); // 沿着投影方向移动移动距离(距离越大弧度越小)

在这句代码中我写了个注释(距离越大弧度越小)那么谁能告诉我距离越小会怎么样呢?大家是不是觉得距离越小弧度就越大呢?嘿嘿。。这就掉坑里了吧。
    这个距离的小是相对的,尽量是不能小于0.01的,至于为啥是0.01呢,我就试了下0.01和0.001。。。哈哈。。就是这么不负责。。
    如果这个距离小于0.01的话插值的方向就是不可控了,至于为什么呢?因为过于小的话就是a到b两点之间的一条线了,垂直于一条线的平面可是海里去了,谁知道在哪里呢。所以大家在用的时候切记这点啊,不能过于追求极限导致结果不可控。还有。。我是不是比较啰嗦啊。。上面我说的是距离啊,是距离,不是值,这个值是可以为负值的啊,负值的话插值的弧线就在这边了,转个向而已。

好了,讲解总算是可以结束了。累死我了。。哇咔咔。。
下面是测试源码,有兴趣的同学玩玩吧
[C#] 纯文本查看 复制代码
[/size][/font][/align][align=left][font=宋体][size=14.0pt]    private Vector3 mStart = new Vector3(2, 4, -1);
    private Vector3 mEnd = new Vector3(-1, 1, 2);
    // Update is called once per frame
    private void Update()
    {
        Debug.DrawLine(new Vector3(-100, 0, 0), new Vector3(100, 0, 0), Color.green);
        Debug.DrawLine(new Vector3(0, -100, 0), new Vector3(0, 100, 0), Color.green);
    
    
        Debug.DrawLine(Vector3.zero, mStart, Color.red);
        Debug.DrawLine(Vector3.zero, mEnd, Color.red);
    
        Debug.DrawLine(mStart, mEnd, Color.red);

        Vector3 centor = (mStart + mEnd) * 0.5f;
        Vector3 centorProject = Vector3.Project(centor, mStart - mEnd); // 中心点在两点之间的投影
        centor = Vector3.MoveTowards(centor, centorProject, 1f);        // 沿着投影方向移动移动距离(距离越大弧度越小)

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

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

        for (int i = 1; i < 10; ++i)
        {
            Vector3 drawVec = Vector3.Slerp(mEnd - centor, mStart - centor, 0.1f * i);
            drawVec += centor;
            Debug.DrawLine(centor, drawVec, 5 == i ? Color.blue : Color.yellow);
         }
    }







参与人数 7鲜花 +37 收起 理由
CodeNoob + 2 很给力!
fishVD + 2
55555 + 2
a99677137 + 5
WorldEdit + 5 很给力!
ispostback + 1
朱迪 + 20 赞一个!

查看全部评分总评分 : 鲜花 +37

回复

使用道具 举报

7日久生情
3954/5000
排名
214
昨日变化

2

主题

304

帖子

3954

积分

Rank: 7Rank: 7Rank: 7Rank: 7

UID
5971
好友
1
蛮牛币
6974
威望
0
注册时间
2013-10-19
在线时间
1556 小时
最后登录
2020-9-24
2015-10-13 09:29:04 显示全部楼层
这才是好的,有用的。。。。。。
回复

使用道具 举报

7日久生情
2148/5000
排名
1031
昨日变化

3

主题

349

帖子

2148

积分

Rank: 7Rank: 7Rank: 7Rank: 7

UID
66284
好友
3
蛮牛币
5997
威望
0
注册时间
2015-1-6
在线时间
713 小时
最后登录
2019-6-24
2015-10-13 09:41:26 显示全部楼层
讲解的不错,加油!~~~~~~
回复

使用道具 举报

5熟悉之中
812/1000
排名
2664
昨日变化

4

主题

113

帖子

812

积分

Rank: 5Rank: 5

UID
8395
好友
7
蛮牛币
2033
威望
0
注册时间
2013-11-18
在线时间
148 小时
最后登录
2019-5-7
2015-10-13 10:09:11 显示全部楼层
//弧线的中心 下面的表达式没写对,是我看错了么?
回复

使用道具 举报

5熟悉之中
821/1000
排名
7540
昨日变化

0

主题

252

帖子

821

积分

Rank: 5Rank: 5

UID
66977
好友
0
蛮牛币
158
威望
0
注册时间
2015-1-8
在线时间
401 小时
最后登录
2020-8-21
2015-10-13 10:21:17 显示全部楼层
支持一下~~~~~~~~~~~~~~~~~~
回复

使用道具 举报

7日久生情
2581/5000
排名
639
昨日变化

0

主题

348

帖子

2581

积分

Rank: 7Rank: 7Rank: 7Rank: 7

UID
8899
好友
0
蛮牛币
4372
威望
0
注册时间
2013-11-25
在线时间
884 小时
最后登录
2020-4-28
2015-10-13 11:10:15 显示全部楼层
谢谢分心,讲解很透彻
回复

使用道具 举报

6蛮牛粉丝
1210/1500
排名
3345
昨日变化

2

主题

193

帖子

1210

积分

Rank: 6Rank: 6Rank: 6

UID
52792
好友
3
蛮牛币
2004
威望
0
注册时间
2014-11-3
在线时间
569 小时
最后登录
2020-9-29
2015-10-13 11:23:55 显示全部楼层
楼主你太不负责了,你有话不一次说完,故意挖坑,说话啰啰嗦嗦,颠三倒四,神经抽搐,哈哈哈。。。

上面是我体内邪恶的小恶魔说的,不代表本人观点,本人只想说:多谢分享!
回复

使用道具 举报

3偶尔光临
170/300
排名
19948
昨日变化

1

主题

30

帖子

170

积分

Rank: 3Rank: 3Rank: 3

UID
44220
好友
0
蛮牛币
155
威望
0
注册时间
2014-9-9
在线时间
37 小时
最后登录
2016-9-4
楼主 2015-10-13 12:25:22 显示全部楼层
追风虫 发表于 2015-10-13 10:09
//弧线的中心 下面的表达式没写对,是我看错了么?

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

sorry。。一时太激动了。应该这样写才对。。谢谢指正。。
回复

使用道具 举报

排名
10225
昨日变化

1

主题

105

帖子

248

积分

Rank: 7Rank: 7Rank: 7Rank: 7

UID
49229
好友
1
蛮牛币
263
威望
0
注册时间
2014-10-15
在线时间
36 小时
最后登录
2016-9-15
2015-10-13 12:38:47 显示全部楼层
牛逼了,搂住
回复

使用道具 举报

6蛮牛粉丝
1270/1500
排名
1681
昨日变化

2

主题

162

帖子

1270

积分

Rank: 6Rank: 6Rank: 6

UID
82017
好友
2
蛮牛币
2725
威望
0
注册时间
2015-3-19
在线时间
336 小时
最后登录
2020-7-4
2015-10-13 12:50:35 显示全部楼层
顶一下学习
回复

使用道具 举报

5熟悉之中
812/1000
排名
2664
昨日变化

4

主题

113

帖子

812

积分

Rank: 5Rank: 5

UID
8395
好友
7
蛮牛币
2033
威望
0
注册时间
2013-11-18
在线时间
148 小时
最后登录
2019-5-7
2015-10-13 16:18:12 显示全部楼层
Vector3 centor = (mStart + mEnd) * 0.5f;        
Vector3 centorProject = Vector3.Project(centor, mStart - mEnd); // 中心点在两点之间的投影        
centor = Vector3.MoveTowards(centor, centorProject, 1f);        // 沿着投影方向移动移动距离(距离越大弧度越小)
这3句话怎么就代表了 centor在mStart,mEnd的中垂线上面了?

看了半天没看明白,智商已成为了负值!
{:101:}
回复

使用道具 举报

7日久生情
2839/5000
排名
421
昨日变化

9

主题

203

帖子

2839

积分

Rank: 7Rank: 7Rank: 7Rank: 7

UID
55220
好友
5
蛮牛币
1191
威望
0
注册时间
2014-11-13
在线时间
955 小时
最后登录
2020-7-21

活力之星

QQ
2015-10-13 16:47:03 显示全部楼层
希望这样的 文章 多一些 ~~~
回复

使用道具 举报

7日久生情
2181/5000
排名
600
昨日变化

3

主题

140

帖子

2181

积分

Rank: 7Rank: 7Rank: 7Rank: 7

UID
44272
好友
3
蛮牛币
6152
威望
0
注册时间
2014-9-10
在线时间
658 小时
最后登录
2019-7-22
QQ
2015-10-13 17:59:37 显示全部楼层
挺深入的!。。。。赞~!。。。。。。
回复

使用道具 举报

5熟悉之中
812/1000
排名
2664
昨日变化

4

主题

113

帖子

812

积分

Rank: 5Rank: 5

UID
8395
好友
7
蛮牛币
2033
威望
0
注册时间
2013-11-18
在线时间
148 小时
最后登录
2019-5-7
2015-10-13 18:14:45 显示全部楼层
[C#] 纯文本查看 复制代码
public Transform sunrise;    
public Transform sunset;
public float m_moveTowardsValue = 1f;    
public int m_lineNum = 10;

private void tt2()
    {
        mStart = sunrise.position;
        mEnd = sunset.position;

        /// 绘制世界坐标系
        Debug.DrawLine(new Vector3(-100, 0, 0), new Vector3(100, 0, 0), Color.green);
        Debug.DrawLine(new Vector3(0, -100, 0), new Vector3(0, 100, 0), Color.green);
        Debug.DrawLine(new Vector3(0, 0, -100), new Vector3(0, 0, 100), Color.green);
        
        /// 求出起始点与终点的中心点
        Vector3 center = (mStart + mEnd) * 0.5f;

        Debug.DrawLine(new Vector3(center.x, 0f, center.z), center, Color.white);

        /// 绘制一个三角形
        Debug.DrawLine(new Vector3(center.x, 0f, center.z), mStart, Color.white);
        Debug.DrawLine(new Vector3(center.x, 0f, center.z), mEnd, Color.white);
        Debug.DrawLine(mStart, mEnd, Color.white);

        Vector3 normal = mEnd - mStart;
        ///只在垂直平面上做球面插值。
        Vector3 tangent = new Vector3(center.x, 0f, center.z) - center;
        /// 两个坐标轴的正交化。
        Vector3.OrthoNormalize(ref normal, ref tangent);
        Vector3 center2 = center + tangent * m_moveTowardsValue;

        Debug.DrawLine(center2, mStart, Color.blue);
        Debug.DrawLine(center2, mEnd, Color.blue);
        Debug.Log(string.Format("{0} : {1}", Vector3.Distance(center2, mStart), Vector3.Distance(center2, mEnd)));

        for (int i = 1; i < m_lineNum; ++i)
        {
            Vector3 drawVec = Vector3.Slerp(mEnd - center2, mStart - center2, 1f / m_lineNum * i);
            drawVec += center2;
            Debug.DrawLine(center2, drawVec, Color.yellow);
        }

        /// 绘制起始点与终点的中心点到计算出的插值的中心点
        Debug.DrawLine(center, center2, Color.black);
    }



受到启发,我也做了一个。
回复

使用道具 举报

3偶尔光临
170/300
排名
19948
昨日变化

1

主题

30

帖子

170

积分

Rank: 3Rank: 3Rank: 3

UID
44220
好友
0
蛮牛币
155
威望
0
注册时间
2014-9-9
在线时间
37 小时
最后登录
2016-9-4
楼主 2015-10-13 20:31:05 显示全部楼层
追风虫 发表于 2015-10-13 16:18
Vector3 centor = (mStart + mEnd) * 0.5f;        
Vector3 centorProject = Vector3.Project(centor, mS ...

嘿嘿,不好意思,写帖子时太着急了。这个细节忘记说了。

mStart-mEnd 可以求出来一个向量,画出来就可以看到是一个和从mStart点到mEnd点平行的向量,只是出发点是从坐标原点出发的。 然后那中心点的向量centor对这个向量求投影,可以得到一个坐标点,这个坐标点和中心点的连线就是一条垂直mStart-mEnd的一条直线, 然后centor沿着这条直线移动就可以了。希望能解决您的疑惑。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册帐号

本版积分规则

蛮牛教育10.1大促销!全站6折扣!