particle-based viscoelastic fluid simulation
marcus • April 11th, 2008
amazing video by a clever canadian who has coded up a fluid simulation using particles, similar to what i’m currently working on in p5 – only that his one seems to be working ;-)
amazing video by a clever canadian who has coded up a fluid simulation using particles, similar to what i’m currently working on in p5 – only that his one seems to be working ;-)
hell, yeah!
– while working on the liquids code i realized that i need LOTS more particles to generate the level of detail i found in the analog photographs i did before. so after a few days of reading hundreds of opengl tutorials, research papers, game developers articles and 20+ p5 tests i'm finally getting closer to writing a particle system that can simulate 65k - 262k particles in real-time at 60+ fps. on my laptop (!), not a specialized über-workstation – in case you wonder.
that's a drastic increase over my previous java based system that could realistically do about 2k particles (10k was pushing it - ran at about 14 fps).
ill post some details on the new system soon, when its ready ...
Dynamic 3D Voronoi Demo from Moniker
when i saw the fountain a while ago, i was stunned by the amazing liquid effects photography chris parks did for their surreal space backgrounds and since then wanted to get my head around the dynamics in these systems.
now having finished our patashnik piece (will be blogging about it soon) i had some time to get into that subject. but before going directly to the code i got hands on ... with ink, olive oil, washing detergent, soy sauce.
here's what i got out of it after playing with it for a few hours.
» "liquids" on flickr.
its definitively amazing for me because normally i'd spend three months writing code before seeing a single image thats close to one of these photos. not exactly what you'd call "agile" ...
however speaking about code =)
i currently see two strategies to simulate this "liquid effect" in code:
a) do all the calculations for the complete area/ volume of the liquid.
this is the traditional approach and has been implemented a number of times.
however since your calculation costs increase exponentially with increasing resolution, it is impossible to generate hd images/ animations in realtime using this method, which is what i'm aiming for.
(i wrote a GLSL GPU-feedback based implementation a while ago, which works fine, even at higher resolutions, but is difficult to combine with other physical forces/ particle effects)
b) another approach i'm currently pursuing is simulating the fluid with a very large particle system where each particle varies in size to cover the complete area, therefore i'm currently rewriting/ optimizing my particle system to use multiple worker threads and improved opengl performance tricks (display lists, point sprites etc). that way i can now simulate 40.000 particles at 125 fps on my macbook pro – in java!
lets see where this goes...
by the way; the very talented maxim zhestkov also makes use of liquid effects in his latest video installation – very nice!
how to do a slerp with angles?
that caused me some headache this morning. however, the solution is blatantly simple.
» example sketch
1 2 3 4 5 6 7 8 9 10 |
float angleSlerp(float cur, float to, float delta)
{
if(cur < -HALF_PI && to > HALF_PI) {
cur += TWO_PI;
} else if(cur > HALF_PI && to < -HALF_PI) {
cur -= TWO_PI;
}
return cur * (1-delta) + to*delta;
} |
clean & naked
my first experiment with 3d model printing. these pictures show an isometric view-projection of a 4-space hypercube aka tesseract resulting in a 3d-model. the brown plastic sticking to it is the support material the printer requires to build up the model. it’s currently recieving an alkaline bath over the weeked after that the raw model will appear.
actual dimensions are 5×5x5cm; costs ~16,00€; printing time 4:16h
see the complete photo-set on flickr
Fiddled a bit with distance calculation methods using various ‘fast square root’ algorithms vs. javas’ Math.sqrt today. So far, this one is about 2-5x faster than the default processing dist(...) method. (tested on osx only; power pc + intel)
public static final float fastDist (int x1, int y1, int x2, int y2)
{
return (float) Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
}Here’s the code for all tests: DistancePerformanceTests.pde
void setup ()
{
int runs = 100000;
long ts = System.currentTimeMillis();
for ( int i=0; i < runs; i++ )
{
fsqrt( random( 1000 ) );
}
long ts1 = System.currentTimeMillis() - ts;
ts = System.currentTimeMillis();
for ( int i=0; i < runs; i++ )
{
ffsqrt( random( 1000 ) );
}
long ts2 = System.currentTimeMillis() - ts;
ts = System.currentTimeMillis();
for ( int i=0; i < runs; i++ )
{
fastSqrt( random( 1000 ) );
}
long ts3 = System.currentTimeMillis() - ts;
ts = System.currentTimeMillis();
for ( int i=0; i < runs; i++ )
{
Math.sqrt( random( 1000 ) );
}
long ts4 = System.currentTimeMillis() - ts;
ts = System.currentTimeMillis();
for ( int i=0; i < runs; i++ )
{
invSqrt( random( 1000 ) );
}
long ts5 = System.currentTimeMillis() - ts;
println( "fsqrt: "+(ts1/(double)1000) );
println( "ffsqrt: "+(ts2/(double)1000) );
println( "fastSqrt: "+(ts3/(double)1000) );
println( "Math.sqrt: "+(ts4/(double)1000) );
println( "invSqrt: "+(ts5/(double)1000) );
}
public static float fsqrt(float x)
{
if (x ==0) return 0;
float root = x / 2;
for (int k = 0; k < 10; k++)
root = (root + (x / root)) / 2;
return root;
}
public static float ffsqrt(float n)
{
if(n==0) return 0;
if(n==1) return 1;
float guess = n/2.0;
float oldguess = 0;
while(guess!=oldguess)
{
oldguess=guess;
guess = (guess+n/guess)/2.0;
}
return guess;
}
public static float fastSqrt (float val)
{
// http://en.wikipedia.org/wiki/Methods_of_computing_square_roots
//
int tmp = Float.floatToIntBits(val);
tmp -= 1<<23; /* Remove last bit to not let it go to mantissa */
/* tmp is now an approximation to logbase2(val) */
tmp = tmp >> 1; /* divide by 2 */
tmp += 1<<29; /* add 64 to exponent: (e+127)/2 =(e/2)+63, */
/* that represents (e/2)-64 but we want e/2 */
return Float.intBitsToFloat(tmp);
}
public static float invSqrt(float x)
{
float xhalf = 0.5f*x;
int i = Float.floatToIntBits(x);
i = 0x5f3759df - (i >> 1);
x = Float.intBitsToFloat(i);
x = x * (1.5f - xhalf * x * x);
return 1.0f/x;
}Eine kompakte Einführung in die Geometrie des Hyperwürfels.
“Der n-dimensionale Hyperwürfel” von Hans Walser, Mathematisches Institut Basel
For the most part, vector operations in four space are simple extensions of their three-space counterparts. For example, computing the addition of two four-vectors is a matter of forming a resultant vector whose components are the sum of the pairwise coordinates of the two operand vectors. In the same fashion, subtraction, scaling, and dot-products are all simple extensions of their more common three-vector counterparts.
In addition, operations between four-space points and vectors are also simple extensions of the more common three-space points and vectors. For example, computing the four-vector difference of four-space points is a simple matter of subtracting pairwise coordinates of the two points to yield the four coordinates of the resulting four-vector.
For completeness, the equations of the more common four-space vector operations follow. In these equations, U = <U0, U1, U2, U3> and V = <V0, V1, V2, V3> are two source four-vectors and k is a scalar value:
U + V = <U0 + V0, U1 + V1, U2 + V2, U3 + V3>
U - V = <U0 - V0, U1 - V1, U2 - V2, U3 - V3>
kV = <kU0, kU1, kU2, kU3>
U·V = U0V0 + U1V1 + U2V2 + U3V3
The main vector operation that does not extend trivially to four-space is the cross product. A three-dimensional space is spanned by three basis vectors, so the cross-product in three-space computes an orthogonal three-vector from two linearly independent three-vectors. Hence, the three-space cross product is a binary operation.
In N-space, the resulting vector must be orthogonal to the remaining N-1 basis vectors. Since a four-dimensional space requires four basis vectors, the four-space cross product requires three linearly independent four-vectors to determine the remaining orthogonal vector. Hence, the four-space cross product is a trinary operation; it requires three operand vectors and yields a single resultant vector. In the remainder of this paper, the four-dimensional cross product will be represented in the form X4(U,V,W).
To find the equation of the four-dimensional cross product, we must first establish criteria of the cross product. These are as follows:
It turns out that a somewhat simple-minded approach to computing the four-dimensional cross product is the correct one. To motivate this idea, we first consider the three-dimensional cross product. The 3D cross product can be thought of as the determinant of a 3x3 matrix whose entries are as follows:
+- -+
| i j k |
X3(U,V) = | U0 U1 U2 |
| V0 V1 V2 |
+- -+
where U and V are the operand vectors, and i, j & k represent the unit components of the resultant vector. The determinant of this matrix is
i (U1V2 - U2V1) - j (U0V2 - U2V0) + k (U0V1 - U1V0)
which is the three-dimensional cross product. Using this idea, we'll form the analogous 4x4 matrix, and see if it meets the four cross product properties listed above:
[2.1a]
+- -+
| i j k l |
X4(U,V,W) = | U0 U1 U2 U3 |
| V0 V1 V2 V3 |
| W0 W1 W2 W3 |
+- -+
The determinant of this matrix is
[2.1b]
|U1 U2 U3| |U0 U2 U3| |U0 U1 U3| |U0 U1 U2|
i|V1 V2 V3| - j|V0 V2 V3| + k|V0 V1 V3| - l|V0 V1 V2|
|W1 W2 W3| |W0 W2 W3| |W0 W1 W3| |W0 W1 W2|
If the operand vectors are linearly dependent, then the vector rows of the 4x4 matrix will be linearly dependent, and the determinant of this matrix will be zero. This satisfies the first condition. The third condition is also satisfied, since a scalar multiple of one of the vectors yields a scalar multiple of one of the rows of the 4x4 matrix. This results in a determinant that is scaled by that factor, so condition three is also met.
The fourth condition falls out as a property of determinants, i.e. when two rows of a determinant matrix are interchanged, only the sign of the determinant changes. Hence, the fourth condition is also met.
The second condition is proven by calculating the dot product of the resultant vector with each of the operand vectors. These dot products will be zero if and only if the resultant vector is orthogonal to each of the operand vectors.
The dot product of the resultant vector X4(U,V,W) with the operand vector U is the following (refer to equation [2.1b]):
U·X4(U,V,W) =
|U1 U2 U3| |U0 U2 U3| |U0 U1 U3| |U0 U1 U2|
U0|V1 V2 V3| - U1|V0 V2 V3| + U2|V0 V1 V3| - U3|V0 V1 V2|
|W1 W2 W3| |W0 W2 W3| |W0 W1 W3| |W0 W1 W2|
This dot product can be rewritten as the determinant
| U0 U1 U2 U3 |
| U0 U1 U2 U3 |
| V0 V1 V2 V3 |
| W0 W1 W2 W3 |,
which is zero, since the first two rows are identical. Hence, the resultant vector X4(U,V,W) is orthogonal to the operand vector U. In the same way, the dot products of V·X4(U,V,W) and W·X4(U,V,W) are given by the determinants
| V0 V1 V2 V3 | | W0 W1 W2 W3 |
| U0 U1 U2 U3 | | U0 U1 U2 U3 |
| V0 V1 V2 V3 | and | V0 V1 V2 V3 |
| W0 W1 W2 W3 | | W0 W1 W2 W3 |,
which are each zero.
Therefore, the second condition is also met, and equation [2.1a] meets all four of the criteria for the four-dimensional cross product.
Since the calculation of the four-dimensional cross product involves 2x2 determinants that are used more than once, it is best to store these values rather than re-calculate them. The following algorithm uses this idea.
// Cross4 computes the four-dimensional cross product of the three vectors
// U, V and W, in that order. It returns the resulting four-vector.
Vector4 *Cross4 (Vector4 *result, Vector4 U, Vector4 V, Vector4 W)
{
double A, B, C, D, E, F; // Intermediate Values
// Calculate intermediate values.
A = (V[0] * W[1]) - (V[1] * W[0]);
B = (V[0] * W[2]) - (V[2] * W[0]);
C = (V[0] * W[3]) - (V[3] * W[0]);
D = (V[1] * W[2]) - (V[2] * W[1]);
E = (V[1] * W[3]) - (V[3] * W[1]);
F = (V[2] * W[3]) - (V[3] * W[2]);
// Calculate the result-vector components.
*result[0] = (U[1] * F) - (U[2] * E) + (U[3] * D);
*result[1] = - (U[0] * F) + (U[2] * C) - (U[3] * B);
*result[2] = (U[0] * E) - (U[1] * C) + (U[3] * A);
*result[3] = - (U[0] * D) + (U[1] * B) - (U[2] * A);
return result;
}
Source: Four-Space visualization of 4D Objects