[计算机动画] 线性插值 矢量线性插值


    线性插值,也就是给定初始点集合和终止点集合,然后给定一个映射关系。对一一对应的点的位置,即对x,y坐标进行线性插值。

 

x = x0*t + x1*(1-t);

y = y0*t + y1*(1-t);

 

    而矢量线性插值,是在给定初始点和终止点集合后,将n个点转换为n-1个按顺序首尾相连的向量。然后,再将向量转化为极坐标,并对长度和角度进行线性插值。相当于将其转换到另外一个空间,维护了其角度和长度上的连续性。

 

a = a*t + a*(1-t);

p = p*t + p*(1-t);


        线性插值

void interpolation::setLinearPoint()
{
    interPoint = new point*[num+1];//num为中间插值的个数
    for(int t=0;t<=num;t++){
        interPoint[t] = new point[size];
        for(int i=0;i<size;i++){//size为图形的点个数,需要对每个点都进行插值
            interPoint[t][i].x = (1-1.0f*t/num)*startPoint[i].x
                    + 1.0f*t/num*endPoint[i].x;
            interPoint[t][i].y = (1-1.0f*t/num)*startPoint[i].y
                    + 1.0f*t/num*endPoint[i].y;

        }
    }
}

        而对于矢量线性插值,要考虑的问题则相对复杂。

        (1)矢量是没有位置的,它只有大小和方向信息。所以在对极坐标两个参数插值后,还需要确定它的位置。要获取位置信息,我们采用的方法是对第一个点的坐标进行线性插值。

//对第一个点的位置线性插值
    interPoint = new point*[num+1];
    for(int t=0;t<=num;t++){
        interPoint[t] = new point[size];
        interPoint[t][0].x = (1-1.0f*t/num)*startPoint[0].x
                + 1.0f*t/num*endPoint[0].x;
        interPoint[t][0].y = (1-1.0f*t/num)*startPoint[0].y
                + 1.0f*t/num*endPoint[0].y;
}
   //根据矢量定义,依次求出其余点坐标
    for(int t=0;t<=num;t++){
        for(int i=1;i<size;i++){
            interPoint[t][i].x = interPoint[t][i-1].x
                    + interVec[t][i-1].p*cos(interVec[t][i-1].a);
            interPoint[t][i].y = interPoint[t][i-1].y
                    + interVec[t][i-1].p*sin(interVec[t][i-1].a);
        }
    }

        (2)矢量线性插值会出现二义性

       

 

         如图,从一个矢量到另外一个矢量,有两种可能的旋转方式。如果直接对角度进行插值,而不做任何处理,会出现错误(与预期不符合)的旋转方向。具体表现并不是按相反方向旋转,而是一种参差不齐的现象:有的边按预期方向旋转,但有的边却不按预期方向旋转。

         一种解决方法:强制保证旋转角度小于180度,这样能在极大多数情况下避免旋转方向不统一的现象(虽然不一定保证按预期方向)。例外在于旋转角度在180度附近时,依然会出现问题。

    //矢量线性插值
    for(int t=0;t<=num;t++){
        interVec[t] = new vec[size];
        for(int i=0;i<size-1;i++){
            //case 1:
            if(endVec[i].a-startVec[i].a<-pi){
                interVec[t][i].a = (1-1.0f*t/num)*startVec[i].a
                        + 1.0f*t/num*(endVec[i].a+2*pi);
            }
            //case 2:
            else if(endVec[i].a-startVec[i].a>pi){
                interVec[t][i].a = (1-1.0f*t/num)*(startVec[i].a+2*pi)
                        + 1.0f*t/num*endVec[i].a;
            }
            //case 3:
            else{
                interVec[t][i].a = (1-1.0f*t/num)*startVec[i].a
                        + 1.0f*t/num*endVec[i].a;
            }
            interVec[t][i].p = (1-1.0f*t/num)*startVec[i].p
                    + 1.0f*t/num*endVec[i].p;
        }
    }

        首先需要说明的一点,计算角度时,是通过反正切得到的,获得角度的范围在[-pi,pi]。为了保证旋转角小于180度,需要在大于180度时对某一角度强制加上2*pi,相当于多旋转一个周期,这样就改变了它们的相对位置,也就保证了角度的差值将小于180度。

 

        另外一种解决方法:既然程序无法判断我们理想中的旋转方向,可以让用户告诉程序是需要顺时针还是逆时针旋转。

        指定顺时针旋转(保证末角度始终大于初角度)

    //矢量线性插值
    for(int t=0;t<=num;t++){
        interVec[t] = new vec[size];
        for(int i=0;i<size-1;i++){

            if(endVec[i].a-startVec[i].a<0){
                interVec[t][i].a = (1-1.0f*t/num)*startVec[i].a
                        + 1.0f*t/num*(endVec[i].a+2*pi);
            }
            else{
                interVec[t][i].a = (1-1.0f*t/num)*startVec[i].a
                        + 1.0f*t/num*endVec[i].a;
            }
            interVec[t][i].p = (1-1.0f*t/num)*startVec[i].p
                    + 1.0f*t/num*endVec[i].p;
        }
    }


        指定逆时针旋转(保证末角度小于初角度)

    for(int t=0;t<=num;t++){
        interVec[t] = new vec[size];
        for(int i=0;i<size-1;i++){

            if(endVec[i].a-startVec[i].a>0){
                interVec[t][i].a = (1-1.0f*t/num)*(startVec[i].a+2*pi)
                        + 1.0f*t/num*(endVec[i].a);
            }
            else{
                interVec[t][i].a = (1-1.0f*t/num)*startVec[i].a
                        + 1.0f*t/num*endVec[i].a;
            }
            interVec[t][i].p = (1-1.0f*t/num)*startVec[i].p
                    + 1.0f*t/num*endVec[i].p;
        }
    }



  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值