knighty
Fractal Iambus
Posts: 819
|
|
« Reply #120 on: May 25, 2010, 10:24:00 PM » |
|
It's just a simplification of the one fold family. Nothing new. I've implemented it and it works. Short answer: The algorithm I already used was something like this (in vectorial form): // v is the input point i=0; r=dot(v,v); while (i<max_iterations && r<bailout_squared) do{ rotate1(v); fold(v); rotate2(v); v=scale*v-(scale-1)*offset; r=dot(v,v); i=i+1; } distance=sqrt(r)/scale^i;
the first rotation have the side effect of rotating the whole fractal. so we can place it after the scaling but the fractal will not rotate anymore, which is a good thing . As Syntopia has noticed, a fold by an arbitrary plane is equivalent to an abs() operation in a different coordinates frame. So we can do (+ some algebraic manipulations): // v is the input point i=0; r=dot(v,v); while (i<max_iterations && r<bailout_squared) do{ v.z=abs(v.z);//for example rotate2(v);//rotation v=v-offset*(scale-1)/scale;//translation, ((scale-1)/scale) is here just in order to keep the size of the fractal "constant" while changing the scale. v=scale*v;//scale rotate1(v);//rotation r=dot(v,v); i=i+1; } distance=sqrt(r)/scale^i;
As you know, using homogeneous coordinates make it possible to express translation operation as a matrix. thus, the transformation in the above code can be written: v=(v.x,v.y,abs(v.z)); v=R1*S*T*R2*v=A*v; Of course, it's possible to apply the abs() operation to x or y or z or x and y...etc. (See also this post)
|
|
|
Logged
|
|
|
|
Syntopia
|
|
« Reply #121 on: May 25, 2010, 11:18:49 PM » |
|
As Syntopia has noticed, a fold by an arbitrary plane is equivalent to an abs() operation in a different coordinates frame.
Actually I tried, and couldn't get it to work. What I wanted to do was to take the following (where n1,n2,n3 are not perpendicular): if (dot(p, n1)>0.0) { p *= n1Mat; } if (dot(p, n2)>0.0) { p *= n2Mat; } if (dot(p, n3)>0.0) { p *= n3Mat; } (repeated the necessary number of times)
And optimize it by using a coordinate system where n1,n2,n3 are axis-aligned: // The columns of the (skewed) transformation-matrix M should be the normals: n1,n2,n3 p = M*abs(M_inverse*p);
But it did not work. Suggestions are welcome :-)
|
|
« Last Edit: May 25, 2010, 11:20:55 PM by Syntopia, Reason: Formatting »
|
Logged
|
|
|
|
David Makin
|
|
« Reply #122 on: May 25, 2010, 11:24:08 PM » |
|
As you know, using homogeneous coordinates make it possible to express translation operation as a matrix. thus, the transformation in the above code can be written: v=(v.x,v.y,abs(v.z)); v=R1*S*T*R2*v=A*v; Of course, it's possible to apply the abs() operation to x or y or z or x and y...etc. (See also this post) So basically the single fold version is a single transform standard escape-time IFS with the addition of one or more abs() operators ? And the multi-fold version is essentially standard escape-time LRIFS but again with the addition of abs() operators and restriction based on dot products ? (Language Restricted IFS, see http://www.fractalforums.com/programming/escape-time-lrifs/)
|
|
« Last Edit: May 25, 2010, 11:37:33 PM by David Makin »
|
Logged
|
|
|
|
knighty
Fractal Iambus
Posts: 819
|
|
« Reply #123 on: May 26, 2010, 10:39:27 PM » |
|
Well, I don't know . I'll try to dig the subject . As Syntopia has noticed, a fold by an arbitrary plane is equivalent to an abs() operation in a different coordinates frame.
Actually I tried, and couldn't get it to work. What I wanted to do was to take the following (where n1,n2,n3 are not perpendicular): if (dot(p, n1)>0.0) { p *= n1Mat; } if (dot(p, n2)>0.0) { p *= n2Mat; } if (dot(p, n3)>0.0) { p *= n3Mat; } (repeated the necessary number of times)
And optimize it by using a coordinate system where n1,n2,n3 are axis-aligned: // The columns of the (skewed) transformation-matrix M should be the normals: n1,n2,n3 p = M*abs(M_inverse*p);
But it did not work. Suggestions are welcome :-) It works that way only if M is a combination of a rotation (+ eventually a symmetry) and a translation. This imply that the three planes have to be orthogonal to each other. For a sigle fold it always works. That's what I was talking about. ___________________________________________ Here is another version of the menger Sponge (that makes it a true "Kaleidoscopic IFS"): Menger3(x,y,z){ r=x*x+y*y+z*z; for(i=0;i<MI && r<bailout;i++){ rotate1(x,y,z);
x=abs(x);y=abs(y);z=abs(z); if(x-y<0){x1=y;y=x;x=x1;} if(x-z<0){x1=z;z=x;x=x1;} if(y-z<0){y1=z;z=y;y=y1;} z-=0.5*CZ*(scale-1)/scale; z=-abs(-z); z+=0.5*CZ*(scale-1)/scale;
rotate2(x,y,z); x=scale*x-CX*(scale-1); y=scale*y-CY*(scale-1); z=scale*z; r=x*x+y*y+z*z; } return sqrt(x*x+y*y+z*z)*scale^(-i); }
|
|
|
Logged
|
|
|
|
Softology
|
|
« Reply #124 on: May 28, 2010, 06:10:38 AM » |
|
|
|
« Last Edit: May 28, 2010, 07:53:08 AM by Softology »
|
Logged
|
|
|
|
Syntopia
|
|
« Reply #125 on: May 28, 2010, 12:24:31 PM » |
|
As you know, using homogeneous coordinates make it possible to express translation operation as a matrix. thus, the transformation in the above code can be written: v=(v.x,v.y,abs(v.z)); v=R1*S*T*R2*v=A*v;
Btw, I tried out using homogeneous coordinates (Pixel Bender supports 4-component matrices and vectors): // Prefolds. p = abs(p); t=dot(p,n1); if (t>0.0) { p-=2.0*t*n1; } t=dot(p,n2); if (t>0.0) { p-=2.0*t*n2; } while (n < maxIterations) { // folds p = abs(p); t=dot(p,n1); if (t>0.0) { p-=2.0*t*n1; } t=dot(p,n2); if (t>0.0) { p-=2.0*t*n2; } // rotate1,scale,rotate2 p4.xyz = p; p4.w = 1.0; p = (M*p4).xyz; n++; }
Where M is precalculated once-per-frame as: M = toMatrix4(rotationMatrix2) * translate(offset) * scale4x4(scale) * translate(-offset) * toMatrix4(rotationMatrix1);
The framerate went from 13.9 to 17.2 fps, or ~23% faster, so it might be worth implementing (at least on a GPU). However in order to combine the matrix, you'll have to move the first rotation below the folds, and I think you restrict the variety of shapes this way. I could imagine the rotation matrices becoming more dependent this way.
|
|
|
Logged
|
|
|
|
knighty
Fractal Iambus
Posts: 819
|
|
« Reply #126 on: May 28, 2010, 10:05:00 PM » |
|
It's just a simplification of the one fold family. Nothing new. I've implemented it and it works. OK, so the variations now come down to; option to pre-rotate before main loop for i=1 to maxiterations rotate1 (if not already set as a pre-rotate) fold (whichever fold method - tetra/cubic/icosa etc) rotate2 stretch if radius>bailout then break end I would say it comes down to: for i=1 to maxiterations fold (whichever fold planes set) Multiply by An affine (or projective?) transform matrix. (in general a composition of translations, rotations and uniform scaling. You can obtain interresting results with other types of transformation but the Distance estimation must be adapted in this case) if radius>bailout then break end
This formulation is good on the side of the computer because it's simple, general and fast. On the side of the user that's perhaps not the best representation. The rotatations can either be around the origin or around the stretch center. I originally had the rotations around the strech center which was incorrect but it leads to other unique images so I left it in as an option.
That was not incorrect just different . In fact you can use any center for the rotations. Does that cover all the variations so far?
Honestly, I don't know. The case of the menger sponge makes me think that there is an ingredient that is missing in the above "general" algorithm. Nice videos. Thanks for sharing. I like particulary the second: it look like a boiling cloud However in order to combine the matrix, you'll have to move the first rotation below the folds, and I think you restrict the variety of shapes this way. I could imagine the rotation matrices becoming more dependent this way.
I don't think so. The only difference I could see is an overall rotation. GPUs are optimized for matrix-vector operations. It seem that they are very well optimized . Have you tried the alternate formulation with rotations and abs() (without branchings)? Is it faster this way?
|
|
|
Logged
|
|
|
|
Syntopia
|
|
« Reply #127 on: May 29, 2010, 12:17:58 AM » |
|
However in order to combine the matrix, you'll have to move the first rotation below the folds, and I think you restrict the variety of shapes this way. I could imagine the rotation matrices becoming more dependent this way.
I don't think so. The only difference I could see is an overall rotation. I get different results? For instance, for the icosa, if I rotate 180 degrees around the x-axis before the folds, the icosa is unaffected. If I do the same rotation after the folds, but before the scaling, it completely disappears (which seems fair because we rotate away from the scaling center). Also, for the icosa I get no overall rotation from the first rotation - it happens for the dodeca, though. GPUs are optimized for matrix-vector operations. It seem that they are very well optimized . Have you tried the alternate formulation with rotations and abs() (without branchings)? Is it faster this way? Not yet, but the conditional reflections are expensive. If I omit them (and get a very crippled icosa) I get around 40 fps.
|
|
|
Logged
|
|
|
|
Nahee_Enterprises
|
|
« Reply #128 on: May 29, 2010, 03:43:42 AM » |
|
Here are another 2 sample movies. Both are created by only morphing/tweening the rotate1 and rotate2 values between frames. Both really need to be watched in the full HD resolution to see the finer fractal details. Really enjoyed seeing the various shapes possible in the first one of these two videos.
|
|
|
Logged
|
|
|
|
kram1032
|
|
« Reply #129 on: May 29, 2010, 09:57:29 AM » |
|
Softology: Those animations where great Both of them varried in brightness for just a second. Was that due to the surface or was it a bug?
|
|
|
Logged
|
|
|
|
Softology
|
|
« Reply #130 on: May 29, 2010, 12:21:50 PM » |
|
Softology: Those animations where great Both of them varried in brightness for just a second. Was that due to the surface or was it a bug? I think it comes down to the surface. When the surface is rough there are not as many smooth surfaces to catch the light so it darkens. Both of them use a single light phong shading model. Jason.
|
|
« Last Edit: May 29, 2010, 12:25:13 PM by Softology »
|
Logged
|
|
|
|
kram1032
|
|
« Reply #131 on: May 29, 2010, 12:44:59 PM » |
|
Ok, that's what I thought
|
|
|
Logged
|
|
|
|
AndyAlias
Forums Newbie
Posts: 9
|
|
« Reply #132 on: August 08, 2011, 06:58:12 AM » |
|
I found a small optimization for GPU implementations: t=dot(p,n1); if (t>0.0) { p-=2.0*t*n1; }
Can be rewritten as: p-=2.0 * max(0.0, dot(p, n1)) * n1;
Edit: I think it may depend a bit on a case by case basis if this is actually faster. ie if the branch condition mostly is true for the particular IFS.
|
|
« Last Edit: July 16, 2012, 05:12:07 AM by AndyAlias »
|
Logged
|
|
|
|
knighty
Fractal Iambus
Posts: 819
|
|
« Reply #133 on: August 26, 2011, 01:34:27 AM » |
|
Thanks.
|
|
|
Logged
|
|
|
|
Imagyx
|
|
« Reply #134 on: October 25, 2011, 10:20:28 PM » |
|
Thank you for these formulas.
|
|
|
Logged
|
During difficult times, keep steady and play the match.
|
|
|
|