插值技术之Catmull-Rom Spline Interpolating

作者:i_dovelemon

来源:CSDN,http://www.dxstudio.com/guide_content.aspx?id=70a2b2cf-193e-4019-859c-28210b1da81f

日期:2015 / 8 / 25

主题:Catmull-Rom Spline Interpolating

引言

在游戏开发中,我们经常会遇到如下的情景:我们已知了一些路径点,我们希望我们的角色能够平滑的沿着这些路径点进行移动,而不是“直直的”从一个点走到另外一个点上去。对于这样的问题,在游戏开发中大多使用一个名为Catmull-Rom Spline的曲线来贯穿这条路径,然后只要沿着这条曲线进行移动即可。今天,我们就先从理论上来了解下什么是Catmull-Rom Spline。

特别申明

由于,我本人也是在进行开发的时候,遇到这样的问题,自然而然想要从网络上寻找解决方案。在寻找的过程中,也走了一些弯路,但是功夫不负有心人,最终还是找到了解决的方法。对于这个问题的理解,我主要是参考网络上的一篇文章,我觉得它的讲解十分的不错,所以这里的文章就不再自己解释给大家听,而是将原文翻译出来,分享给大家。

数学基础

当存在一些点的时候,我们往往会根据这些点来绘制一条样条线,而这条样条线需要平滑的穿过这些点。如果你是想要这么做的,那么你可以看一下Catmull-Rom样条线。Catmull-Rom样条线的数学背景十分的复杂,也超出了本文的范围,这里不再讲解,感兴趣的读者可以自行搜索相关的文章去了解。

我们可以认为,Catmull-Rom样条线,是一根比较特殊的Bezier曲线,而这条Bezier曲线能够保证它会穿过从控制点的第二个点到控制点的倒数第二点之间的所有点。所以说,Catmull-Rom样条线最少需要4个控制点来进行控制。

下面的公式给出了,如何计算某两个指定点之间的点:

Output_point = P0 * (-0,5*t*t*t + t*t – 0,5*t) +

P1 * (1,5*t*t*t - 2,5*t*t + 1,0) +

P2 * (-1,5*t*t*t + 2,0*t*t + 0,5*t) +

P3 * (0,5*t*t*t – 0,5*t*t);

这里的P0,P1,P2,P3都是曲线上的点,但是要知道,上面公式只能够计算出从点P1到点P2之间的点。

公式中的t取值范围为[0,1],当t从0到1线性变化的时候,曲线就会从点P1(此时t=0),慢慢移动到P2(此时t=1)。这个曲线另外一个特点就是,我们计算出来的点P的切向量和这个点的周围两个起点和终点的切向量是平行的。

如果你想要处理更多的点,那么只有用前面的点,当前的点和接下来的两个点来构造这条曲线就可以了。举例来说,如果你现在有5个点,分别为P1,P2,P3,P4,P5,那么我们就可以构造两条不同的曲线,他们的控制点分别为[P1,P2,P3,P4]和[P2,P3,P4,P5]。当我们使用这两条曲线去分别从t=[0,1]上去绘制,将得到P2-P3,P3-P4这两条曲线。

补充说明

细心的读者可能发现了,我们的控制点虽然有4个,但是绘制的曲线却只能够通过中间的两个点。这就导致了,如果我想曲线同时过这四个点,该怎么处理了。其实处理的方法,十分的简单,我们只要认为的构造一个起点和终点来构成四个控制点即可。比如现在有P0,P1,P2,P3,如果用[P0,P1,P2,P3]构造曲线,曲线将只能够通过P1-P2,为了让曲线能够通过P0和P3,我们可以人为的构造出如下的控制点[2P0 - P1, P0, P1, P2],以及[P1,P2,P3,2P3 - P2]。通过这样的方法,就能够绘制一条经过所有控制点的曲线了。

    for (int index = 1; index < points.count - 2 ; index++) {

        

        CGPoint point0 = [self pointAtIndex:index - 1 ofArray:points];

        CGPoint point1 = [self pointAtIndex:index ofArray:points];

        CGPoint point2 = [self pointAtIndex:index + 1 ofArray:points];

        CGPoint point3 = [self pointAtIndex:index + 2 ofArray:points];

        

        for (int i = 1; i < granularity ; i++) {

            float t = (float) i * (1.0f / (float) granularity);

            float tt = t * t;

            float ttt = tt * t;

            

            CGPoint pi;

            pi.x = 0.5 * (2*point1.x+(point2.x-point0.x)*t + (2*point0.x-5*point1.x+4*point2.x-point3.x)*tt + (3*point1.x-point0.x-3*point2.x+point3.x)*ttt);

            pi.y = 0.5 * (2*point1.y+(point2.y-point0.y)*t + (2*point0.y-5*point1.y+4*point2.y-point3.y)*tt + (3*point1.y-point0.y-3*point2.y+point3.y)*ttt);

            

            if (pi.y > self.graphView.frame.size.height) {

                pi.y = self.graphView.frame.size.height;

            }

            else if (pi.y < 0){

                pi.y = 0;

            }

            

            if (pi.x > point0.x) {

                [path addLineToPoint:pi];

            }

        }

        

        [path addLineToPoint:point2];

    }

原文地址:https://www.cnblogs.com/iOSJason/p/4945827.html