The usual Newtonian definition of velocity V is the time
derivative of position P:
V = dP/dt
If we have a nice functional form for V or P, this is extremely
mathematically useful, since we can use all the tools of calculus
on a differential definition like this. If we don't have a
nice functional form, like with many real problems, we can't use
calculus at all (this is also the case if we've forgotten
calculus).
This is why we use computers, but computers don't do
infinitesimals. There are a variety of ways to "discretize"
this continuous differential equation into computational steps;
but the simplest is to replace the infinitesimal derivatives with
a finite difference:
V = delta_P / delta_t
which means
V = (new_P - old_P) / delta_t
Given a set of positions, this finite difference lets us estimate
the velocity between known positions. We can also solve for new_P,
to get a classic equation known as an Euler timestep:
new_P = old_P + V * delta_t
In a programming language, we'd usually write this as:
P += V * delta_t;
One big problem here is to best approximate the underlying
partial differential equation, "delta_t" should be as small as
possible. Small enough, and roundoff begins to change your
simulation results. Also, making the timestep one-tenth as
big needs ten times as many steps for the same simulation, so you
quickly run into computational limitations: even a simple partial
differential equation can consume arbitrary computational power
and still not be perfect!
The other problem is our finite difference equation isn't a very
accurate approximation to the continuous equation, usually causing
energy to bleed off the system. You can do better by taking
the average V, known as Leapfrog
integration (more next week!).
In the finite difference above, if P and V are vectors with xyz
components, we can just update each component of the vector:
P.x += V.x * delta_t;
P.y += V.y * delta_t;
P.z += V.z * delta_t;
It's always possible to break down 3D computations into a set of
(coupled) 1D computations, but this ".x .y .z" copy and paste is
quite error prone--I always end up with ".x .y .y" somewhere,
which gives bizarre results and is very hard to locate and
fix.
In the OpenGL shader language GLSL, or in C++ with a 3D
vector class, you can avoid the copy and paste by defining P
and V as 3D vectors, of type "vec3". You can then operate on
3D vectors just like they were scalars:
P += V * delta_t;
JavaScript is missing the operator overloading that lets a class
vec3 handle multiplication and addition nicely, but in PixAnvil I
added some little helpers to the existing class vec3 for these
operations:
C++ or GLSL |
JavaScript/PixAnvil |
Operation |
|
Vector sum |
vec3 vecC = vecA+vecB; |
var vecC=vecA.p(vecB); |
plus |
Vector addition |
vecA += vecB; |
vecA.pe(vecB); |
plus-equal |
Vector * scalar |
vecA *= 2.0; |
vecA.te(2.0); |
times-equal |
Energy is conserved in both the universe and our simulations mostly because: