Interpolation Tricks

Interpolation Tricks

or How I Learned to Stop Worrying and Love the 0..1 Range

Contents

  1. Why 0..1 Range
  2. Getting to the 0..1 Range
  3. Linear Interpolation
  4. Smoothstep
  5. Higher Powers
  6. Sin
  7. Weighted Average
  8. Splines
  9. Conclusion

1. Why 0..1 Range

While making demos I've found different interpolation tricks to be extremely valuable. Adding little smoothness to all kinds of movement, be it actual movement of the camera, some object, fading of lights, fading in and out etc, makes things much more enjoyable to watch. Sharp movements and changes are jarring and should be avoided. (*1)

Generally speaking, when making some kind of animation, we know the starting and ending positions, and want to transition between these. All of these can be converted to interpolation from 0 to 1.

Values between 0 and 1 have some rather interesting properties, including the fact that you can multiply any value between 0 and 1 with another value between 0 and 1, and the result is guaranteed to be between 0 and 1. (*2)

These properties can be used to tweak the way we move from 0 to 1 in various ways.

2. Getting to the 0..1 Range

Let's say we want to move the variable X between points A and B in N steps. This could be expressed by, for example:

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

Or, put another way, this becomes:

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

where v ranges from 0 to 1. (*3)

3. Linear Interpolation

Moving from 0 to 1 in N discrete steps is called linear interpolation, or "lerp", for short:

http://sol.gfxile.net/interpolation/linear.gif

 

Relying on linear interpolation alone solves the basic problem of getting from point A to point B, but continuing the motion from there, thus moving from one interpolation to the next (or from motion to full stop) may seem jarring. Luckily, there's more than one way from A to B (actually infinite ways, but we'll concentrate on just a few).

4. Smoothstep

If there's nothing else you take away from this little tutorial, it's this:

#define SMOOTHSTEP(x) ((x) * (x) * (3 - 2 * (x)))

The smoothstep (*4) is the magic salt you can sprinkle over everything to make them better. You can replace most, if not all, of your linear interpolations with smoothstepped version of the same. It, simply put, rules:

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

There's also "smootherstep" which may be an overkill.. or not.

#define SMOOTHERSTEP(x) ((x) * (x) * (x) * ((x) * ((x) * 6 - 15) + 10))

Smoothstep looks like this:

http://sol.gfxile.net/interpolation/smoothstep.gif

 

It takes a while to get up to speed, but also slows down before reaching the end.

5. Higher Powers

There are, however, times when you just want to have slow acceleration. This is where simple powers come into play:

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

If you'd rather have some decelleration action, the above is easy to inverse:

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

These look like this:

squared.gifuploading.4e448015.gif轉存失敗重新上傳取消http://sol.gfxile.net/interpolation/squared.gif

 

You can naturally rise the power. Cubed, the curves look like this:

http://sol.gfxile.net/interpolation/cubed.gif

 

The same idea can also be applied to smoothstep. Applying smoothstep to already smoothstepped value rounds the curve even further; the result is not as easily applied to everything, as you'll be standing still at the corners, but it can be useful sometimes:

http://sol.gfxile.net/interpolation/smoothstepx.gif

 

6. Sin

Another handy curve is sin(). Sin() can be applied just like a power, and the result is also similar.

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

This curve can also be inverted just like the higher powers. The results look like this:

http://sol.gfxile.net/interpolation/sin.gif

 

If we use the whole curve, we get pretty close to smoothstep:

http://sol.gfxile.net/interpolation/cos.gif

 

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

This is, however, usually more expensive than using smoothstep.

7. Weighted Average

One rather handy algorithm, especially when you don't necessarily know how the target will behave in the future (such as a camera tracking the player's character), is to apply weighted average to the value.(*5)

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

where v is the current value, w is the value towards which we want to move, and N is the slowdown factor. The higher N, the slower v approaches w.

http://sol.gfxile.net/interpolation/wtavg.gif

 

The closer v gets to w, the slower it moves; the further away they are, the faster v changes. In ideal world, v will never actually reach w, as the steps towards the goal get smaller and smaller. Then again, computers aren't ideal.

8. Splines

Finally, if you need more control, you can always use splines. Catmull-Rom spline is handy in the way that it always goes through the control points, and as such it can be easily applied to these interpolators. Other spline functions may give more natural curves, but are also less predictable. Here's an implementation of the Catmull-Rom interpolation function:

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));
} 

In normal use, you'd have plenty of control points, and while interpolating between points 5 and 6, you'd feed the points 4 and 7 as the Q and T (or p0 and p3) parameters. Anyway, here we're using the function just to get different interpolations between 0 and 1, so the parameters are (Q, 0, 1, T). Here's a plot for some values:

http://sol.gfxile.net/interpolation/catmullrom.gif

 

The parameter values in the plot are the Q and T (or p0 and p3) values.

Note that while the values do cross the 0 and 1 points, they do not necessarily stay between 0 and 1!

9. Conclusion

I hope the techniques outlined in this short tutorial help shave the sharpest corners off your movements. As always, comments are appreciated.

Further reading:

Footnotes:

  1. Except, of course, if you want it. But in that case, you'd better get rid of all the other jarring movements, or your effect is ruined.
  2. Similarly, any value between -1 and 1 multiplied with one with the same range results in a value between -1 and 1.
  3. Or well, from 0 to (N-1)/N, but close enough, for large numbers of N).
  4. ref: Texturing and Modelling, a Procedural Approach, Ebert et al, Second edition, Pages 26-27 - buy the book, it has plenty of cool stuff in it.
  5. Also known, in more general terms, as low pass filter. Handy in more places than you can imagine.

//////////////////////////////////////////////////////////////////////////////////////////

easing.gifuploading.4e448015.gif轉存失敗重新上傳取消http://www.gizma.com/easing/easing.gif

 

// simple linear tweening - no easing, no acceleration

 

Math.linearTween = function (t, b, c, d) {

        return c*t/d + b;

};

              

 

// quadratic easing in - accelerating from zero velocity

 

Math.easeInQuad = function (t, b, c, d) {

        t /= d;

        return c*t*t + b;

};

              

 

// quadratic easing out - decelerating to zero velocity

 

Math.easeOutQuad = function (t, b, c, d) {

        t /= d;

        return -c * t*(t-2) + b;

};

 

              

 

// quadratic easing in/out - acceleration until halfway, then deceleration

 

Math.easeInOutQuad = function (t, b, c, d) {

        t /= d/2;

        if (t < 1) return c/2*t*t + b;

        t--;

        return -c/2 * (t*(t-2) - 1) + b;

};

 

// cubic easing in - accelerating from zero velocity

 

Math.easeInCubic = function (t, b, c, d) {

        t /= d;

        return c*t*t*t + b;

};

 

              

 

// cubic easing out - decelerating to zero velocity

 

Math.easeOutCubic = function (t, b, c, d) {

        t /= d;

        t--;

        return c*(t*t*t + 1) + b;

};

 

              

 

// cubic easing in/out - acceleration until halfway, then deceleration

 

Math.easeInOutCubic = function (t, b, c, d) {

        t /= d/2;

        if (t < 1) return c/2*t*t*t + b;

        t -= 2;

        return c/2*(t*t*t + 2) + b;

};

       

 

// quartic easing in - accelerating from zero velocity

 

Math.easeInQuart = function (t, b, c, d) {

        t /= d;

        return c*t*t*t*t + b;

};

 

              

 

// quartic easing out - decelerating to zero velocity

 

Math.easeOutQuart = function (t, b, c, d) {

        t /= d;

        t--;

        return -c * (t*t*t*t - 1) + b;

};

 

              

 

// quartic easing in/out - acceleration until halfway, then deceleration

 

Math.easeInOutQuart = function (t, b, c, d) {

        t /= d/2;

        if (t < 1) return c/2*t*t*t*t + b;

        t -= 2;

        return -c/2 * (t*t*t*t - 2) + b;

};

 

// quintic easing in - accelerating from zero velocity

 

Math.easeInQuint = function (t, b, c, d) {

        t /= d;

        return c*t*t*t*t*t + b;

};

 

              

 

// quintic easing out - decelerating to zero velocity

 

Math.easeOutQuint = function (t, b, c, d) {

        t /= d;

        t--;

        return c*(t*t*t*t*t + 1) + b;

};

 

              

 

// quintic easing in/out - acceleration until halfway, then deceleration

 

Math.easeInOutQuint = function (t, b, c, d) {

        t /= d/2;

        if (t < 1) return c/2*t*t*t*t*t + b;

        t -= 2;

        return c/2*(t*t*t*t*t + 2) + b;

};

              

 

// sinusoidal easing in - accelerating from zero velocity

 

Math.easeInSine = function (t, b, c, d) {

        return -c * Math.cos(t/d * (Math.PI/2)) + c + b;

};

 

              

 

// sinusoidal easing out - decelerating to zero velocity

 

Math.easeOutSine = function (t, b, c, d) {

        return c * Math.sin(t/d * (Math.PI/2)) + b;

};

 

              

 

// sinusoidal easing in/out - accelerating until halfway, then decelerating

 

Math.easeInOutSine = function (t, b, c, d) {

        return -c/2 * (Math.cos(Math.PI*t/d) - 1) + b;

};

 

              

 

// exponential easing in - accelerating from zero velocity

 

Math.easeInExpo = function (t, b, c, d) {

        return c * Math.pow( 2, 10 * (t/d - 1) ) + b;

};

 

              

 

// exponential easing out - decelerating to zero velocity

 

Math.easeOutExpo = function (t, b, c, d) {

        return c * ( -Math.pow( 2, -10 * t/d ) + 1 ) + b;

};

 

              

 

// exponential easing in/out - accelerating until halfway, then decelerating

 

Math.easeInOutExpo = function (t, b, c, d) {

        t /= d/2;

        if (t < 1) return c/2 * Math.pow( 2, 10 * (t - 1) ) + b;

        t--;

        return c/2 * ( -Math.pow( 2, -10 * t) + 2 ) + b;

};

              

 

// circular easing in - accelerating from zero velocity

 

Math.easeInCirc = function (t, b, c, d) {

        t /= d;

        return -c * (Math.sqrt(1 - t*t) - 1) + b;

};

 

              

 

// circular easing out - decelerating to zero velocity

 

Math.easeOutCirc = function (t, b, c, d) {

        t /= d;

        t--;

        return c * Math.sqrt(1 - t*t) + b;

};

 

              

 

// circular easing in/out - acceleration until halfway, then deceleration

 

Math.easeInOutCirc = function (t, b, c, d) {

        t /= d/2;

        if (t < 1) return -c/2 * (Math.sqrt(1 - t*t) - 1) + b;

        t -= 2;

        return c/2 * (Math.sqrt(1 - t*t) + 1) + b;

};

//////////////////////////////////////////////////////////////////////////////////////////

Quadratic/Cubic versus Linear interpolations


by Alain Brobecker (aka baah/Arm's Tech)
([email protected] or rte de Dardagny; 01630 CHALLEX; FRANCE)

Please note this text assumes you have some basic Mathematical knowledges concerning functions. Of course you can apply the formulaes without knowing what continuity or derivability is, but i think it should be most valuable to learn what they are for computer works.

Also note that this text is aimed at assembly language programming (though one can use another language) and integer arithmetic, because this is the only way to go fast with small expense of transitors (like in my lovely ARM processor). 


Linear interpolation

Suppose you're programming something that requires loads of processing time, like a synthesis sample (1d) or a raytracer (2d)... But you would like it to run faster, even if you must have a lesser quality. A standard method to speed things up is to perform the precise computation on less points, and to add extra values with linear interpolation.

Let's see how we proceed for when dimension is 1, as shown in "linear.gif". The red points are the one computed with the precise (and heavy) algorithm, and the green ones are added afterhand with a linear interpolation.

uploading.4e448015.gif轉存失敗重新上傳取消

In our example, if red points' heights are called h0; h1 and h2, the heights of green points will be (2*h0+h1)/3; (h0+2*h1)/3; (2*h1+h2)/3 and (h1+2*h2)/3. This values were found by calculating the equation of the line (hence the name linear interpolation) passing by (0;h0) and (1;h1), which is h(x)=h0+x*(h1-h0), and computing h(1/3); h(2/3). The same was done on the second segment.

As usual, if we take a power of two for the denominator of the x values, things will be much easier and the interpolations will need additions only since we'll use fixed point arithmetics. If we want to add 2^p%-1 green points the code will look like this:

  ... Compute the precise edges in h%(j%) for j%=1 to n% ...
        x%=0
        FOR j%=0 TO n%-1
          h%=h%(j%)
          dh%=h%(j%+1)-h%       :REM (h(1)-h(0)/2^p%) <<p%
          h%=h%<<p%             :REM Convert h0 to fixed point
          FOR i%=0 TO 2^p%-1
            POINT x%,h%>>p%
            x%+=1
            h%+=dh%             :REM h+=h(1)-h(0)/2^p%
          NEXT i%
        NEXT j%

A better version would probably make some rounding before plotting the point. But all this sounds nice and fast, isn't it?

Hummm! Fast it sure is, but nice... It depends upon what you are doing. Many demos feature the standard tunnel effect, and they use this technique by performing "raytracing" on a small 41*33 grid and then enlarging the values with bichannel (they need two coords u;v to acces the map) bilinear (because we interpolate on x and y for each block) interpolation on 8*8 blocks. In this case the result is ok, but it won't always be so, especially when you have quickly changing values (when you enlarge a raytraced image?). 


Cubic interpolation

The idea is to use a better interpolation function. In this chapter we'll use a cubic (ie of degree 3) polynomial, and the difference is stunning.

For explanation purposes, we'll assume from now on that we are in dimension 1, and that h(0);...;h(n) are our original points. Our aim is to replace each segment [h(i);h(i+1)], by a function fi(x)=ai*x3+bi*x2+ci*x+di so that fi(0)=h(i) and fi(1)=h(i+1) (note that i will be in [1;n-1]).

Obviously, we have 4 parameters (ai;bi;ci and di), and only 2 conditions, so we can set up two more conditions. As you can see, the linear interpolation produces sharp edges on the original points, and this is what we want to get rid off, isn't it?

Some mathematics would tell us that a sufficient condition to have a regular curve, without those sharp edges, is that the derivative of the function (exists and ;) is continuous. So we'll try to set up 2 more conditions on the derivatives, so that the slope on the left of h(i) is the same as the one on its right (which is obviously not the case with linear interpolation). All the work was to find those conditions, and here they are:

In "cubic1.gif", you can see the original points in red and the tangents as red segments, which always are parallel to the grey dotted segments joining h(i-1) and h(i+1). Thus we can conclude that the derivative for each point is (h(i+1)-h(i-1))/2 (we divide by 2 because i+1 and i-1 are distant of 2).

uploading.4e448015.gif轉存失敗重新上傳取消

So, now we have 4 conditions for the function fi(x)=ai*x3+bi*x2+ci*x+di:

        fi(0)=h(i)
        fi(1)=h(i+1)
        fi'(0)=(h(i+1)-h(i-1))/2
        fi'(1)=(h(i+2)-h(i))/2

With some calculii, and knowing that fi'(x)=3*ai*x2+2*bi*x+ci, we find:

        di=h(i)
        ci=(h(i+1)-h(i-1))/2
        bi=(-h(i+2)+4*h(i+1)-5*h(i)+2*h(i-1))/2
        ai=(h(i+2)-3*h(i+1)+3*h(i)-h(i-1))/2

And now we have the whole function which is continuous, derivable and with a continuous derivative (because fi'(1)=fi+1'(0) etc...). And the resulting interpolation looks much better than a stupid linear one (see "cubic2.gif").

uploading.4e448015.gif轉存失敗重新上傳取消

Ok, now moving on the hard subject of the implementation in fixed point arithmetic (and assembly of course ;). First, go have a look at my article dealing with incremental square/cubes computation... Done? Ok, i assume you did. The four coefficients to compute all this incrementaly are:

        val=a0
        inc1=a3*h^3+a2*h^2+a1*h
        inc2=6*a3*h^3+2*a2*h^2
        inc3=6*a3*h^3

Of course for an assembly version you'll use h=1/2n and shifts instead of divisions, as explained in the squares.txt article. The drawing loop will look like the following:

        val+=inc1
        inc1+=inc2
        inc2+=inc3
        plot val

So you need 3 additions (and some shifts on processor that, unlike ARM, don't have shifts for free) and 4 registers to implement this one. Bigger and slower than linear interpolation, but worth the prize anyway i think.

Oh, btw, it can be mathematically proven (this is a classic when playing with Bezier curve) that the curve is entirely contained in the convex polygon formed by the points (i;h(i)); (i+1;h(i+1)) and by (i+1;h(i)+fi'(0)) and (i;h(i+1)-fi'(1)). So to ensure the interpolated values stay in the choosen range (probably [0;255]) you'll need to set conditions so that h(i)+fi'(0) and h(i+1)-fi'(1) also are. 


Quadratic interpolation

This one was much harder to do (fasten your seatbelt), but the base idea is the same, ie replace each segment by a function fi(x)=aix2+bi*x+ci of degree two. The gain lies in the implemenation which needs only two additions (only one more than linear!) and 2 (TWO!) registers under certain circumstances (yes, the same amount as for linear interpolation, probably only possible on ARM ;).

This time we need three conditions, since we have three parameters. As usual we'll have two conditions dealing with the extremities, and the choice is left to us for the third condition. Sounds a bit annoying to have only one extra condition, because we would like to control two derivatives, isn't it? (he, he, says the one who knows more! ;)

One could try something a bit different, like fi(0)=h(2*i), fi(1)=h(2*i+2) and fi(1/2)=h(2*i+1) which makes for three conditions, but this wouldn't allow a continuous derivative. And there could be more choices, but i only found one sensible choice which i will expose to you right now.

We must have at most 3 control points (or 3 conditions), and to simplify a bit let's call them h0;h0.5 and h1 (in orange) and have a look at "quad1.gif". One can notice that with three points organised in this way (which is not the standard one, but more on this later), we could set the conditions so that the function goes through h0 and h1, and then both derivatives would only need h0.5 to be controlled.

uploading.4e448015.gif轉存失敗重新上傳取消

The derivative in h0 will be 2*(h0.5-h0) (we multiply by 2, ie divide by 1/2 because the distance is 1/2) and the one in h1 will be 2*(h1-h0.5), so we have:

        f(0)=h0
        f(1)=h1
        f'(0)=2*(h0.5-h0)
        f'(1)=2*(h1-h0.5)

Knowing that f(x)=a*x2+b*x+c, and so f'(x)=2*a*x+b, we can calculate:

        c=h0
        b=2*(h0.5-h0)
        a=h1-2*h0.5+h0

And you will admit that with such formulaes, we could easily compute the interpolated points between the [0;1] interval, again with the tricks explained in the article dealing with incremental squares/cubes computation.

But in the original problem we were having segments defined by two points (in red) to replace with another interpolation function. For cubic interpolation it was quite easy to know which set of four points to take. But the symmetry of the problem don't seem to allow to take three points... I bypassed this problem by changing it a bit: instead of interpolating between [i;i+1], i worked in [i-0.5;i+0.5], thus changing the symmetry of the problem as shown in "quad2.gif". I needed two fake point i-0.5 and i+0.5 (orange), which i choosed so that h(i-0.5)=(h(i-1)+h(i))/2 and h(i+0.5)=(h(i)+h(i+1))/2.

uploading.4e448015.gif轉存失敗重新上傳取消

The unoptimised algorithm looks like this. You can improve it by developping the expressions of a;b and c which depends only upon h(i-1); h(i) and h(i+1). 

        FOR i=1 TO n-1

 
          REM Compute the three heights for quadratic interpolation
          y0=(h(i-1)+h(i))/2
          ym=h(i)               :REM Can't call it y0.5 or so
          y1=(h(i)+h(i+1))/2

 
          REM Compute the coefficients for quadratic interpolation
          c=y0
          b=2*(ym-y0)
          a=y1-2*ym+y0

 
          REM Compute the coefficients for incremental version
          value=c<<(2*m)
          inc1=a+(b<<m)
          inc2=a<<1

 
          REM Interpolate
          x=2^m*i
          FOR j=0 TO 2^m-1
            PLOT x,value>>(2*m)
            value+=inc1
            inc1+=inc2
            x+=1
          NEXTj

 
        NEXTi

So, we have an easy way of computing quadratic interpolation, with only two additions per point, and by choosing m<=5 (ie at most 32 points interpolated) i was able to mix inc1 and inc2 in a single register. I must say the whole is really fast (provided you have an ARM and are programming in assembly ;) and very nice. In fact, i really doubt one can make the difference between cubic and quadratic interpolation visually. ("quad3.gif")

uploading.4e448015.gif轉存失敗重新上傳取消

It also has an advantage. As for cubic interpolation, the interpolated function will be completely enclosed in the triangle formed by the points (i-0.5;(h(i-1)+h(i))/2); (i;h(i)) and (i+0.5;(h(i)+h(i+1))/2), and this tells us that provided our original points h(i) are in the range [0;255], so will the be the quadratic curve. No need to cope with more conditions as in cubic interpolation. The inconvenient being that nothing ensures the original values will be reached (and they probably won't be), so there's a kind of smooth performed by the quadratic interpolation. 


And the winner is

Ok, now have a look at both those images. The raytraced one must be viewed in 16 millions colors, or the rounding or dithering will spoil the effect. They both feature quadratic vs linear interpolation. We can see the standard linear interpolation artifacts (a value reached on one column/line only), and the difference leaves absolutely no chances to linear interpolation, except for some effects that won't require it because the model is regular enough (mapped tunnels for example).

uploading.4e448015.gif轉存失敗重新上傳取消

uploading.4e448015.gif轉存失敗重新上傳取消

I have not made much tests with cubic vs quadratic, because the difference is too small to be noticed visually. But the difference exists all the same. Ok, it will be a bit technical here.

Quadratic interpolation is made with polynomials of degree two, while cubic uses degree 3 polynomials. So the derivative of the quadratic functions will be a succession of connected segments (french mathematicians would tell the derivative is "affine par morceaux") while the derivative of the cubic interpolation will be composed of connected quadratic functions. Moreover this latter derivative function is itself derivable, and its derivative is continuous. To sum up we have:

  • quadratic: derivable and with continuous derivative (mathematicians say C1).
  • cubic: twice derivable and 2nd order derivative is continuous (C2).

This is not very important for visual appearance, but in case you want to take the derivative (or approximate it) with for example an emboss functions (bump mapping is not far away) you must take cubic interpolation to have something nice. Otherwise quadratic is more than effective.

Ok, i think i've said it all. You can have some ARM assembly source code for dim=1 quadratic interpolation here ("quad1.bas"), and if you want source for the dim=2 case, you can have it alongside my demo Wish You Were Beer. I hope to see quadratic interpolation more frequently in demos, because it's far better than linear one. And don't forget to credit/thank me in case you found this article valuable.

////////////////////////////////////////////////////////////////////////////////////////

Interpolation methods

Written by Paul Bourke
December 1999


 

Discussed here are a number of interpolation methods, this is by no means an exhaustive list but the methods shown tend to be those in common use in computer graphics. The main attributes is that they are easy to compute and are stable. Interpolation as used here is different to "smoothing", the techniques discussed here have the characteristic that the estimated curve passes through all the given points. The idea is that the points are in some sense correct and lie on an underlying but unknown curve, the problem is to be able to estimate the values of the curve at any position between the known points.

Linear interpolation is the simplest method of getting values at positions in between the data points. The points are simply joined by straight line segments. Each segment (bounded by two data points) can be interpolated independently. The parameter mu defines where to estimate the value on the interpolated line, it is 0 at the first point and 1 and the second point. For interpolated values between the two points mu ranges between 0 and 1. Values of mu outside this range result in extrapolation. This convention is followed for all the subsequent methods below. As with subsequent more interesting methods, a snippet of plain C code will server to describe the mathematics.

double LinearInterpolate(
   double y1,double y2,
   double mu)
{
   return(y1*(1-mu)+y2*mu);
}

Linear interpolation results in discontinuities at each point. Often a smoother interpolating function is desirable, perhaps the simplest is cosine interpolation. A suitable orientated piece of a cosine function serves to provide a smooth transition between adjacent segments.

double CosineInterpolate(
   double y1,double y2,
   double mu)
{
   double mu2;

 
   mu2 = (1-cos(mu*PI))/2;
   return(y1*(1-mu2)+y2*mu2);
}

Cubic interpolation is the simplest method that offers true continuity between the segments. As such it requires more than just the two endpoints of the segment but also the two points on either side of them. So the function requires 4 points in all labelled y0, y1, y2, and y3, in the code below. mu still behaves the same way for interpolating between the segment y1 to y2. This does raise issues for how to interpolate between the first and last segments. In the examples here I just haven't bothered. A common solution is the dream up two extra points at the start and end of the sequence, the new points are created so that they have a slope equal to the slope of the start or end segment.

double CubicInterpolate(
   double y0,double y1,
   double y2,double y3,
   double mu)
{
   double a0,a1,a2,a3,mu2;

 
   mu2 = mu*mu;
   a0 = y3 - y2 - y0 + y1;
   a1 = y0 - y1 - a0;
   a2 = y2 - y0;
   a3 = y1;

 
   return(a0*mu*mu2+a1*mu2+a2*mu+a3);
}

Paul Breeuwsma proposes the following coefficients for a smoother interpolated curve, which uses the slope between the previous point and the next as the derivative at the current point. This results in what are generally referred to as Catmull-Rom splines.

   a0 = -0.5*y0 + 1.5*y1 - 1.5*y2 + 0.5*y3;
   a1 = y0 - 2.5*y1 + 2*y2 - 0.5*y3;
   a2 = -0.5*y0 + 0.5*y2;
   a3 = y1;

Hermite interpolation like cubic requires 4 points so that it can achieve a higher degree of continuity. In addition it has nice tension and biasing controls. Tension can be used to tighten up the curvature at the known points. The bias is used to twist the curve about the known points. The examples shown here have the default tension and bias values of 0, it will be left as an exercise for the reader to explore different tension and bias values.

/*
   Tension: 1 is high, 0 normal, -1 is low
   Bias: 0 is even,
         positive is towards first segment,
         negative towards the other
*/
double HermiteInterpolate(
   double y0,double y1,
   double y2,double y3,
   double mu,
   double tension,
   double bias)
{
   double m0,m1,mu2,mu3;
   double a0,a1,a2,a3;

 
        mu2 = mu * mu;
        mu3 = mu2 * mu;
   m0  = (y1-y0)*(1+bias)*(1-tension)/2;
   m0 += (y2-y1)*(1-bias)*(1-tension)/2;
   m1  = (y2-y1)*(1+bias)*(1-tension)/2;
   m1 += (y3-y2)*(1-bias)*(1-tension)/2;
   a0 =  2*mu3 - 3*mu2 + 1;
   a1 =    mu3 - 2*mu2 + mu;
   a2 =    mu3 -   mu2;
   a3 = -2*mu3 + 3*mu2;

 
   return(a0*y1+a1*m0+a2*m1+a3*y2);
}

While you may think the above cases were 2 dimensional, they are just 1 dimensional interpolation (the horizontal axis is linear). In most cases the interpolation can be extended into higher dimensions simply by applying it to each of the x,y,z coordinates independently. This is shown on the right for 3 dimensions for all but the cosine interpolation. By a cute trick the cosine interpolation reverts to linear if applied independently to each coordinate.

For other interpolation methods see the Bezier, Spline, and piecewise Bezier methods here.

   

Linear

http://paulbourke.net/miscellaneous/interpolation/linear.gif

Cosine

http://paulbourke.net/miscellaneous/interpolation/cosine.gif

Cubic

cubic.gifuploading.4e448015.gif轉存失敗重新上傳取消http://paulbourke.net/miscellaneous/interpolation/cubic.gif

Hermite

hermite.gifuploading.4e448015.gif轉存失敗重新上傳取消http://paulbourke.net/miscellaneous/interpolation/hermite.gif

3D linear

linear3d.gifuploading.4e448015.gif轉存失敗重新上傳取消http://paulbourke.net/miscellaneous/interpolation/linear3d.gif

3D cubic

cubic3d.gifuploading.4e448015.gif轉存失敗重新上傳取消http://paulbourke.net/miscellaneous/interpolation/cubic3d.gif

3D Hermite

http://paulbourke.net/miscellaneous/interpolation/hermite3d.gif


 

Trilinear Interpolation

Written by Paul Bourke
July 1997


 

Trilinear interpolation is the name given to the process of linearly interpolating points within a box (3D) given values at the vertices of the box. Perhaps its most common application is interpolating within cells of a volumetric dataset.

Consider a unit cube with the lower/left/base vertex at the origin as shown here on the right. 
The values at each vertex will be denoted V000, V100, V010, ....etc....V111

http://paulbourke.net/miscellaneous/interpolation/trilinear.gif

The value at position (x,y,z) within the cube will be denoted Vxyz and is given by

Vxyz =

V000 (1 - x) (1 - y) (1 - z) +
V100 x (1 - y) (1 - z) + 
V010 (1 - x) y (1 - z) + 
V001 (1 - x) (1 - y) z +
V101 x (1 - y) z + 
V011 (1 - x) y z + 
V110 x y (1 - z) + 
V111 x y z

In general the box will not be of unit size nor will it be aligned at the origin. Simple translation and scaling (possibly of each axis independently) can be used to transform into and then out of this simplified situation.


 

Linear Regression

Written by Paul Bourke
October 1998


Linear regression is a method to best fit a linear equation (straight line) of the form y(x) = a + b x to a collection of N points (xi,yi). Where b is the slope and a the intercept on the y axis.

The result will be stated below without derivation, that requires minimisation of the sum of the squared distance from the data points and the proposed line. This function is minimised by calculating the derivative with respect to a and b and setting these to zero. For a more complete derivation see the "Numerical Recipes in C".

The solution is clearer if we define the following

http://paulbourke.net/miscellaneous/interpolation/linregress2.gif

linregress3.gifuploading.4e448015.gif轉存失敗重新上傳取消http://paulbourke.net/miscellaneous/interpolation/linregress3.gif

linregress4.gifuploading.4e448015.gif轉存失敗重新上傳取消http://paulbourke.net/miscellaneous/interpolation/linregress4.gif

Then

http://paulbourke.net/miscellaneous/interpolation/linregress5.gif

and

linregress6.gifuploading.4e448015.gif轉存失敗重新上傳取消http://paulbourke.net/miscellaneous/interpolation/linregress6.gif

And finally the regression coefficient is

linregress7.gifuploading.4e448015.gif轉存失敗重新上傳取消http://paulbourke.net/miscellaneous/interpolation/linregress7.gif

This is 0 if there is no linear trend, 1 for perfect linear fit.

Note

  • This discussion assumes there is no known variance for the x and y values. There are solutions which can take this into account, this is particularly important if some values are known with less error than others.
  • The solution above requires that the slope is not infinite, Sxx is not zero.

Example

The following example shows the points and the best fit line as determined using the techniques discussed here.

http://paulbourke.net/miscellaneous/interpolation/linregress1.gif

Source

C
linregress.c

C++ contributed by Charles Brown
RegressionLine.cpp
RegressionLine.hpp


 

Curve Fit Through Arbitrary Points

Written by Paul Bourke
August 1991


 

The following introduces a method of immediately deriving a polynomial that passes through an arbitrary number of points. That is, find a polynomial f(x) that passes through the N points

(x0,y0), (x1,y1), (x2,y2), ..... (xN-1,yN-1)

The key to this solution is that we want an exact fit at the points given and we don't care what happens in between those points. The general solution is

curvefit1.gifuploading.4e448015.gif轉存失敗重新上傳取消http://paulbourke.net/miscellaneous/interpolation/curvefit1.gif

To see how this works, consider the product term. When x = xi the product term has the same denominator and numerator and thus equals 1 and therefore contributes yi to the sum. All other terms in the summation contribute 0 since there exists a (xj - xj) in the numerator. Thanks to Simon Stegmaier for pointing out that this is known as a Lagrange Polynomial.

For a numerical example consider the polynomial that passes through the following points

(0,2)
(1,1)
(3,3)
(4,0)
(6,5)

http://paulbourke.net/miscellaneous/interpolation/curvefit2.gif

The function using the above formula is

   f(x) = 2 * (x-1) * (x-3) * (x-4) * (x-6) / [ (0-1) * (0-3) * (0-4) * (0-6) ]
        + 1 * (x-0) * (x-3) * (x-4) * (x-6) / [ (1-0) * (1-3) * (1-4) * (1-6) ]
        + 3 * (x-0) * (x-1) * (x-4) * (x-6) / [ (3-0) * (3-1) * (3-4) * (3-6) ]
        + 0 * (x-0) * (x-1) * (x-3) * (x-6) / [ (4-0) * (4-1) * (4-3) * (4-6) ]
        + 5 * (x-0) * (x-1) * (x-3) * (x-4) / [ (6-0) * (6-1) * (6-3) * (6-4) ]

 
   f(x) = (x-1) * (x-3) * (x-4) * (x-6) / 36
        - (x-0) * (x-3) * (x-4) * (x-6) / 30
        + (x-0) * (x-1) * (x-4) * (x-6) / 6 
        + (x-0) * (x-1) * (x-3) * (x-4) / 36

 
   f(x) = 17*x4/90 - 181*x3/90 + 563*x2/90 - 163*x/30 + 2

By substituting the values of x for the points the function must pass through (x=0,1,3,4,6) it is easy to see that the expression above achieves the result, namely y=2,1,3,0,5 respectively.

What happens at other points?

All bets are off regarding the behaviour between the fixed points. The polynomial is of degree N and could violently fly off anywhere. The continuous curve for the numerical example above is shown below.

curvefit3.gifuploading.4e448015.gif轉存失敗重新上傳取消http://paulbourke.net/miscellaneous/interpolation/curvefit3.gif

A competition in the Mathematics news groups in October 1998

From: "John Santos" <[email protected]>
Newsgroups: alt.sci.math.probability,alt.tv.mathnet,aus.mathematics
Subject: $100.00 prize for the solution
Date: Tue, 15 Sep 1998 20:56:50 -0700
X-Newsreader: Microsoft Outlook Express 4.72.3110.1
X-MimeOLE: Produced By Microsoft MimeOLE V4.72.3110.3
NNTP-Posting-Host: 209.239.197.111
X-NNTP-Posting-Host: 209.239.197.111
Message-ID: <[email protected]>
X-NNTP-Posting-Host: 209.63.114.134

 
Hi everyone,
My name is John Santos and I am willing to pay anyone $100.00 for the first
person to solve this problem. I am providing some hints. This works as follows:
you will see nine digits - the tenth digit or letter is the answer. 
What I need is the formula or mathematical equation to get there...

 
   Number          Answer 
---------------------------
   749736637         1     
   713491024         8     
   523342792         D     
   749236871         P     
   727310078         E     
   746261832         4     
   733237527         L     
   743510589         9     
   715240338         K     
   722592910         1     
   739627071         R     

 
The first one with the answer and emails it to me wins the $100.00
Email address: [email protected]
Good Luck !!!!

They refused to pay up for this solution !!!!

My reply to this posting was

The following is the solution to the posted problem, although it probably 
doesn't offer the insight you are seeking, it certainly falls within the 
scope of competition. To illustrate my solution I will use the following 
symbols instead of the numbers you used simply to save space. For your 
second column with letters and numbers use their ASCII values instead, 
this has no loss of generality as it is a simple 1 to 1 mapping.

 
x1 y1
x2 y2
x3 y3
x4 y4
etc

 
The following is a general method of making a function that passes 
through any pair of values (xi,yi).

 
        y1 (x-x2) (x-x3) (x-x4)
f(x) = --------------------------- 
         (x1-x2) (x1-x3) (x1-x4)

 
        y2 (x-x1) (x-x3) (x-x4)
     + --------------------------- 
         (x2-x1) (x2-x3) (x2-x4)

 
        y3 (x-x1) (x-x2) (x-x4)
     + --------------------------- 
         (x3-x1) (x3-x2) (x3-x4)

 
        y4 (x-x1) (x-x2) (x-x3)
     + --------------------------- 
         (x4-x1) (x4-x2) (x4-x3)

 
etc etc. As you can see, at x=x1 all the terms disappear except the 
first which equals y1, at x=x2 all the terms disappear except the
second which equals y2, etc etc.

 
   X                           Y
   Number           Answer     ASCII
---------------------------------------
   749736637        1          49
   713491024        8          56
   523342792        D          68
   749236871        P          80
   727310078        E          69
   746261832        4          52
   733237527        L          76
   743510589        9          57
   715240338        K          75
   722592910        1          49
   739627071        R          82

The "lovely" expression in this case is
f(x) = + 49 ((x - 713491024) / 36245613) ((x - 523342792) / 226393845) ((x - 749236871) / 499766) ((x - 727310078) / 22426559) ((x - 746261832) / 3474805) ((x - 733237527) / 16499110) ((x - 743510589) / 6226048) ((x - 715240338) / 34496299) ((x - 722592910) / 27143727) ((x - 739627071) / 10109566) + 56 ((x - 749736637) / -36245613) ((x - 523342792) / 190148232) ((x - 749236871) / -35745847) ((x - 727310078) / -13819054) ((x - 746261832) / -32770808) ((x - 733237527) / -19746503) ((x - 743510589) / -30019565) ((x - 715240338) / -1749314) ((x - 722592910) / -9101886) ((x - 739627071) / -26136047) + 68 ((x - 749736637) / -226393845) ((x - 713491024) / -190148232) ((x - 749236871) / -225894079) ((x - 727310078) / -203967286) ((x - 746261832) / -222919040) ((x - 733237527) / -209894735) ((x - 743510589) / -220167797) ((x - 715240338) / -191897546) ((x - 722592910) / -199250118) ((x - 739627071) / -216284279) + 80 ((x - 749736637) / -499766) ((x - 713491024) / 35745847) ((x - 523342792) / 225894079) ((x - 727310078) / 21926793) ((x - 746261832) / 2975039) ((x - 733237527) / 15999344) ((x - 743510589) / 5726282) ((x - 715240338) / 33996533) ((x - 722592910) / 26643961) ((x - 739627071) / 9609800) + 69 ((x - 749736637) / -22426559) ((x - 713491024) / 13819054) ((x - 523342792) / 203967286) ((x - 749236871) / -21926793) ((x - 746261832) / -18951754) ((x - 733237527) / -5927449) ((x - 743510589) / -16200511) ((x - 715240338) / 12069740) ((x - 722592910) / 4717168) ((x - 739627071) / -12316993) + 52 ((x - 749736637) / -3474805) ((x - 713491024) / 32770808) ((x - 523342792) / 222919040) ((x - 749236871) / -2975039) ((x - 727310078) / 18951754) ((x - 733237527) / 13024305) ((x - 743510589) / 2751243) ((x - 715240338) / 31021494) ((x - 722592910) / 23668922) ((x - 739627071) / 6634761) + 76 ((x - 749736637) / -16499110) ((x - 713491024) / 19746503) ((x - 523342792) / 209894735) ((x - 749236871) / -15999344) ((x - 727310078) / 5927449) ((x - 746261832) / -13024305) ((x - 743510589) / -10273062) ((x - 715240338) / 17997189) ((x - 722592910) / 10644617) ((x - 739627071) / -6389544) + 57 ((x - 749736637) / -6226048) ((x - 713491024) / 30019565) ((x - 523342792) / 220167797) ((x - 749236871) / -5726282) ((x - 727310078) / 16200511) ((x - 746261832) / -2751243) ((x - 733237527) / 10273062) ((x - 715240338) / 28270251) ((x - 722592910) / 20917679) ((x - 739627071) / 3883518) + 75 ((x - 749736637) / -34496299) ((x - 713491024) / 1749314) ((x - 523342792) / 191897546) ((x - 749236871) / -33996533) ((x - 727310078) / -12069740) ((x - 746261832) / -31021494) ((x - 733237527) / -17997189) ((x - 743510589) / -28270251) ((x - 722592910) / -7352572) ((x - 739627071) / -24386733) + 49 ((x - 749736637) / -27143727) ((x - 713491024) / 9101886) ((x - 523342792) / 199250118) ((x - 749236871) / -26643961) ((x - 727310078) / -4717168) ((x - 746261832) / -23668922) ((x - 733237527) / -10644617) ((x - 743510589) / -20917679) ((x - 715240338) / 7352572) ((x - 739627071) / -17034161) + 82 ((x - 749736637) / -10109566) ((x - 713491024) / 26136047) ((x - 523342792) / 216284279) ((x - 749236871) / -9609800) ((x - 727310078) / 12316993) ((x - 746261832) / -6634761) ((x - 733237527) / 6389544) ((x - 743510589) / -3883518) ((x - 715240338) / 24386733) ((x - 722592910) / 17034161) f( 749736637) = 49 (1) f( 713491024) = 56 (8) f( 523342792) = 68 (D) f( 749236871) = 80 (P) f( 727310078) = 69 (E) f( 746261832) = 52 (4) f( 733237527) = 76 (L) f( 743510589) = 57 (9) f( 715240338) = 75 (K) f( 722592910) = 49 (1) f( 739627071) = 82 (R) 


 

Nearest neighbour weighted interpolation

Written by Paul Bourke
April 1998


The following describes perhaps the simplest method of "smoothly" approximating height values on a surface given a collection of randomly distributed samples. It is often used to derive estimates of the surface height at the vertices of a regular grid from irregularly spaced samples. While the example given here is based on determining the height of a surface (x,y), the same general technique can be used in higher dimensions. For example, estimating the density with a volume (x,y,z) given irregular density measurements.

Consider N height samples, that is, we have N triples (xi, yi,zi). We want to estimate the height z given a position on the plane (x,y). The general form of the so called "nearest neighbour weighted interpolation" also sometimes called the "inverse distance method" for estimating z is given by the following.

weightinterp1.gifuploading.4e448015.gif轉存失敗重新上傳取消http://paulbourke.net/miscellaneous/interpolation/weightinterp1.gif

where p generally determines relative importance of distant samples. Note the denominator above gives a measure of how close the point being estimated is from the samples. Naturally if a sample is close then it has a greater influence on the estimate than if the sample is distant.

The following shows an example of reconstructing a surface from 1000 samples. The approximation is generally better with increased values of p.

The original surface from which samples are taken for this example is shown on the right.

http://paulbourke.net/miscellaneous/interpolation/weightinterp2.gif

p = 1

weightinterp3.gifuploading.4e448015.gif轉存失敗重新上傳取消http://paulbourke.net/miscellaneous/interpolation/weightinterp3.gif

p = 2

weightinterp4.gifuploading.4e448015.gif轉存失敗重新上傳取消http://paulbourke.net/miscellaneous/interpolation/weightinterp4.gif

p = 4

weightinterp5.gifuploading.4e448015.gif轉存失敗重新上傳取消http://paulbourke.net/miscellaneous/interpolation/weightinterp5.gif

p = 6

weightinterp6.gifuploading.4e448015.gif轉存失敗重新上傳取消http://paulbourke.net/miscellaneous/interpolation/weightinterp6.gif

 

Colour and Normal Interpolation

As it applies to triangles and quadrilaterals in the rendering of 3D surfaces

Written by Paul Bourke
September 2002

 

It is frequently desirable to estimate the colour or normal at a point in the interior of a 3 or 4 vertex planar polygon given only the colour and normal at each of the vertices. The most common application of this is smooth rendering of surfaces approximated by a finite number of triangular facets or quadrilaterals. Without colour and/or normal interpolation each planar piece of the surface has the same colour and normal, this results in a visible discontinuity between adjacent faces. The following illustrates a part of a sphere made up of quadrilaterals and rendered using a single normal applied to the whole face or 4 normals at each vertex interpolated across the face.

The approach most commonly used by 3D rendering packages, both real-time such as OpenGL and more CPU intensive algorithms such as raytracing, is called Phong normal interpolation. A often used efficient implementation is called barycentric interpolation. The idea is the same for both colour and normal interpolation, a line is extended from the point in question to two edges of the polygon. The estimate of the colour or normal at those points is made by linear interpolation between the values at the vertices of the edge. The estimate at the point in question is linearly interpolated from the estimates at the ends of the extended line.

This is illustrated in the sequence below, while this is for normals the method is identical for colours which are after all generally a (r,g,b) triple instead of a (x,y,z) triple. In (A) the point P is where the colour or normal is to be estimated, a line is extended (in any direction but shown as horizontal in this diagram) until it intersects two edges. In (B) the normals at the intersection points of the extended line are shown in red, they are calculated by linear interpolation. In (C) the two normals in (B) are linearly interpolated to give the estimate of the normal at point P.

uploading.4e448015.gif轉存失敗重新上傳取消

Note

  • The colour or normal estimate at the vertices is always the same as the vertex value.
  • The colour or normals along the edges only depends on the colour or normal at the edge vertices and not on the values at the other vertices. It is this that ensures that adjacent faces with the same colour or normal along a joining edge will join smoothly even though their other vertices may have very different values.
  • The direction in which the line is extended out from the point being estimated doesn't matter except that it must be the same for all points within a face. One way is to choose a major axis by specifying a normal. The plane with this normal that passes though the point in question cuts two of the polygon edges, this is used as the extended line.
  • One difference between interpolation of normals and colours is that the normals estimated at the end of the extended lines and the final normal at P are normalised to unit length. In colour interpolation each r,g,b component is treated independently.

/////////////////////////////////////////////////////////////////////////////////////

Transition Types

uploading.4e448015.gif正在上傳…重新上傳取消

///////////////////////////////////////////////////////////////////////////

Hermite Curve Interpolation

Hamburg (Germany), the 30th March 1998. Written by Nils Pipenbrinck aka Submissive/Cubic & $eeN

Introduction

Hermite curves are very easy to calculate but also very powerful. They are used to smoothly interpolate between key-points (like object movement in keyframe animation or camera control). Understanding the mathematical background of hermite curves will help you to understand the entire family of splines. Maybe you have some experience with 3D programming and have already used them without knowing that (the so called kb-splines, curves with control over tension, continuity and bias are just a special form of the hermite curves).

The Math

To keep it simple we first start with some simple stuff. We also only talk about 2-dimensional curves here. If you need a 3D curve just do with the z-coordinate what you do with y or x. Hermite curves work in in any number of dimensions.
To calculate a hermite curve you need the following vectors:

  • P1: the startpoint of the curve
  • T1: the tangent (e.g. direction and speed) to how the curve leaves the startpoint
  • P2: he endpoint of the curve
  • T2: the tangent (e.g. direction and speed) to how the curves meets the endpoint

uploading.4e448015.gif轉存失敗重新上傳取消

These 4 vectors are simply multiplied with 4 hermite basis functions and added together.

h1(s) =  2s^3 - 3s^2 + 1
h2(s) = -2s^3 + 3s^2
h3(s) =   s^3 - 2s^2 + s
h4(s) =   s^3 -  s^2

Below are the 4 graphs of the 4 functions (from left to right: h1, h2, h3, h4).

uploading.4e448015.gif轉存失敗重新上傳取消 uploading.4e448015.gif轉存失敗重新上傳取消 uploading.4e448015.gif轉存失敗重新上傳取消 uploading.4e448015.gif轉存失敗重新上傳取消
(all graphs except the 4th have been plotted from 0,0 to 1,1)

Take a closer look at functions h1 and h2:

  • h1 starts at 1 and goes slowly to 0.
  • h2 starts at 0 and goes slowly to 1.

Now multiply the startpoint with h1 and the endpoint with h2. Let s go from 0 to 1 to interpolate between start and endpoint. h3 and h4 are applied to the tangents in the same manner. They make sure that the curve bends in the desired direction at the start and endpoint.

The Math in Matrix Form

All this stuff can be expessed with some vector and matrix algebra. I think the matrix-form is much easier to understand.

Vector S: The interpolation-point and it's powers up to 3:
Vector C: The parameters of our hermite curve:
Matrix h: The matrix form of the 4 hermite polynomials:

 

 
     | s^3 |            | P1 |             |  2  -2   1   1 |
S =  | s^2 |       C =  | P2 |        h =  | -3   3  -2  -1 |
     | s^1 |            | T1 |             |  0   0   1   0 |
     | 1   |            | T2 |             |  1   0   0   0 |

To calculate a point on the curve you build the Vector S, multiply it with the matrix h and then multiply with C.
P = S * h * C

A little side-note: Bezier-Curves

This matrix-form is valid for all cubic polynomial curves. The only thing that changes is the polynomial matrix. For example, if you want to draw a Bezier curve instead of hermites you might use this matrix:

     | -1   3  -3   1 |
b =  |  3  -6   3   0 |
     | -3   3   0   0 |
     |  1   0   0   0 |

I wrote a separate page about bezier curves.

Some Pseudocode

Sure, this C-style pseudo-code won't compile. C doesn't come with a power function, and unless you wrote yourself a vector-class any compiler would generate hundreds of errors and make you feel like an idiot. I think it's better to present this code in a more abstract form.

moveto (P1);                            // move pen to startpoint
for (int t=0; t < steps; t++)
{
  float s = (float)t / (float)steps;    // scale s to go from 0 to 1
  float h1 =  2s^3 - 3s^2 + 1;          // calculate basis function 1
  float h2 = -2s^3 + 3s^2;              // calculate basis function 2
  float h3 =   s^3 - 2*s^2 + s;         // calculate basis function 3
  float h4 =   s^3 -  s^2;              // calculate basis function 4
  vector p = h1*P1 +                    // multiply and sum all funtions
             h2*P2 +                    // together to build the interpolated
             h3*T1 +                    // point along the curve.
             h4*T2;
  lineto (p)                            // draw to calculated point on the curve
}

Getting rid of the Tangents

I know... controlling the tangents is not easy. It's hard to guess what a curve will look like if you have to define it. Also, to make a sharply bending curve you have to drag the tangent-points far away from the curve. I'll now show you how you can turn the hermite curves into cardinal splines.

Cardinal splines

Cardinal splines are just a subset of the hermite curves. They don't need the tangent points because they will be calculated from the control points. We'll lose some of the flexibility of the hermite curves, but as a tradeoff the curves will be much easier to use. The formula for the tangents for cardinal splines is:
Ti = a * ( Pi+1 - Pi-1 )
a is a constant which affects the tightness of the curve. Write yourself a program and play around with it. ( a should be between 0 and 1, but this is not a must).

Catmull-Rom splines

The Catmull-Rom spline is again just a subset of the cardinal splines. You only have to define a as 0.5, and you can draw and interpolate Catmull-Rom splines.
Ti = 0.5 * ( P i+1 - Pi-1 )
Easy, isn't it? Take a math-book and look for Catmull-Rom splines. Try to understand how they work! It's damn difficult, but when they are derived from hermite curves the cardinal splines turn out to be very easy to understand. Catmull-Rom splines are great if you have some data-points and just want to interpolate smoothly between them.

The Kochanek-Bartels Splines (also called TCB-Splines)

Now we're going down to the guts of curve interpolation. The kb-splines (mostly known from Autodesk's 3d-Studio Max and Newtek's Lightwave) are nothing more than hermite curves and a handfull of formulas to calculate the tangents. These curves have been introduced by D. Kochanek and R. Bartels in 1984 to give animators more control over keyframe animation. They introduced three control-values for each keyframe point:

  • Tension: How sharply does the curve bend?
  • Continuity: How rapid is the change in speed and direction?
  • Bias: What is the direction of the curve as it passes through the keypoint?

I won't try to derive the tangent-formulas here. I think just giving you something you can use is a better idea. (if you're interested you can ask me. I can write it down and send it to you via email.)

The "incoming" Tangent equation:

 
          (1-t)*(1-c)*(1+b)
TS    =   -----------------  * ( P   -  P    )
  i              2                i      i-1

 
          (1-t)*(1+c)*(1-b)
      +   -----------------  * ( P   -  P    )
                 2                i+1    i

 
The "outgoing" Tangent equation:

 
          (1-t)*(1+c)*(1+b)
TD    =   -----------------  * ( P   -  P    )
  i              2                i      i-1

 
          (1-t)*(1-c)*(1-b)
      +   -----------------  * ( P   -  P    )
                 2                i+1    i

 
When you want to interpolate the curve you should use this vector:

 
    |  P(i)    |
C = |  P(i+1)  |
    |  TD(i)   |
    |  TS(i+1) |

You might notice that you always need the previous and next point if you want to calculate the curve. This might be a problem when you try to calculate keyframe data from Lightwave or 3D-Studio. I don't know exactly how these programs handle the cases of the first and last point, but there are enough sources available on the internet. Just search around a little bit. (Newtek has a good developer section. You can download the origignal Lightwave motion code on their web-site).

Speed Control

If you write yourself keyframe-interpolation code and put it into a program you'll notice one problem. Unless you have your keyframes in fixed intervals you will have a sudden change of speed and direction whenever you pass a keyframe-point. This can be avoided if you take the number of key-positions (frames) between two keyframes into account:

N is the number of frames (seconds, whatever) between two keypoints.

 
                    2 * N
                         i-1
TD  =  TD *     ---------------       adjustment of outgoing tangent
  i      i          N    + N
                     i-1    i

 

 
                    2 * N
                         i
TS  =  TS *     ---------------       adjustment of incomming tangent
  i      i          N    + N
                     i-1    i

What about the "normal" Splines?

The other spline-types, beta-splines, uniform nonrational splines and all the others are a completely different thing and are not covered here. They share one thing with the hermite curves: They are still cubic polynomials, but the way they are calculated is different.

Final Words

Why does no-one care that it's almost impossible to do nicely formatted math-formulas in HTML? What's more important? Good design or good content? As always you might want to contact me.

//////////////////////////////////////////////////////////////////////////////

 

Wireframe
wire.jpguploading.4e448015.gif正在上傳…重新上傳取消http://paulbourke.net/miscellaneous/interpolation/wire.jpg

Single normal across face
http://paulbourke.net/miscellaneous/interpolation/flat.jpg

Normal interpolated across face
http://paulbourke.net/miscellaneous/interpolation/smooth.jpg

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章