vanCopper bio photo

vanCopper

Try Your Best.

Email Github

原创博文,转载请声明

插值

在离散数据的基础上补差连续函数,使得这条连续曲线通过全部的离散数据点。插值是离散函数逼近的重要方法,利用它可通过函数在有限个点处的取值状况,估算出函数在其他点处的近似值 。

插值比较常用在骨骼动画,物体移动,灯光渐隐,摄像机动画,图形渲染中。

Linear Interpolation(线性插值)

假如我们需要将物体X通过N步从A点移动到B点,可以使用下面的代码:

for(i = 0; i < N; i++)
{    
   X = ((A * i) + (B * (N - i))) / N;
}

或者:

for(i = 0; i < N; i++)
{
	v = i / N;
  	X = (A * v) + (B * (1 - v));
}

可以看到v的取值范围是0-1,插值中我们均可归一化至0-1范围内。

用N个离散数将0移动至1称为线性插值(lerp)。

是不是感觉这生硬的移动非常不和谐?如果将此移动用到游戏中人物行走,保证策划会过来和你撕五百回合。

Smoothstep

利用此函数平滑:

function smoothstep(x)
{
	return (x * x * (3 - 2 * x));  
}

不要问一个学渣这函数到底怎么解释,如果真想知道建议Google。那么上面的lerp就可以改写为:

for(i = 0; i < N; i++)
{
	v = i / N;
  	v = smoothstep(v);
  	X = (A * v) + (B * (1 - v));
}

自然多了,有没有?当快接近目标时移动会慢下来。

Higher Powers(高次幂)

如果运动时想有缓慢加速效果,简单利用N次幂就可以:

for(i = 0; i < N; i++)
{
  v = i / N;
  v = v * v;
  X = (A * v) + (B * (1 - v));	
}

如果运动时想有缓慢减速,上面的公式反相即可:

for(i = 0; i < N; i++)
{
  v = i / N;
  v = 1 - (1 - v) * (1 - v);
  X = (A * v) + (B * (1 - v));
}

如果把次幂调整到三次方,曲线如图:

同样的,我们可以对已经Smoothstep 再次应用Smoothstep

正弦

正弦插值和次幂函数近似:

for(i = 0; i < N; i++)
{
  v = i / N;
  v = sin(v * Pi / 2);
  X = (A * v) + (B * ( 1 - v));
} 

反向后也同次幂函数近似:

如果使用整条曲线,那么将更接近smoothstep。但性能会打折。

for (i = 0; i < N; i++)
{
    v = i / N;
    v = 0.5 - cos(-v * Pi) * 0.5;
    X = (A * v) + (B * (1 - v));
}

Weighted Average(加权平均数)

非常方便的算法,特别是当你无法预知未来目标行为时(比如当摄像机跟随目标角色,而角色的位置是一直在改变的)。

v = (( v * (N-1)) + w )/N;

其中v代表当前值,w代表目标点,N是缓动因子,N值越大,v接近w就越慢:

Splines(样条)

样条函数支持你控制更多的插值点,也就是说你可以让运动轨迹更多样化。

float catmullrom(float t, float p0, float p1, float p2, float p3)
{
    return 0.5f * (
        (2 * p1) +
        (-p0 + p2) * t +
        (2 * p0 - 5 * p1 + 4 * p2 - p3) * t * t +
        (-p0 + 3 * p1 - 3 * p2 + p3) * t * t * t
    );
}

for (i = 0; i < N; i++)
{
    v = i / N;
    v = catmullrom(v, Q, 0, 1, T);
    X = (A * v) + (B * (1 - v));
}

原文:http://sol.gfxile.net/interpolation/

更多内容:


扫码分享该文