Random Perpendicular 3D Vector

Started by
17 comments, last by Kest 17 years, 5 months ago
Quote:Original post by mooserman352
depending on how many such particles you want, it might be faster for you to generate a basis for the orthogonal plane first. then you only need to call rand twice and do very little arithmetic.

You mean to obtain the right and up vectors and then simply randomly scale them for each particle?

That's a great idea. But I'm not sure if my original code accurately obtains those vectors. Can anyone confirm that?

I could also use some help with accurately scaling the two and using them together without breaking the unit scale value of the whole operation. For example, I'm pretty sure that right * 1.0 + up * 1.0 will be the top right corner of a square shape, not a circle. Is that incorrect?
Advertisement
You code for finding the up and right vectors looks a-okay to me, though it is somewhat unnecessarily complex. you need to be careful for special cases, when forward.y^2 = 1 or -1. you might as well choose between <1,0,0> and <0,1,0> for whacko.

I gather that you also want the random outward velocity to be 1. If this is the case, then all you need to do is generate a random angle between 0 and 2*pi. then you have x = cos(theta) y = sin(theta), and so your final vector is x*up + y*right.

Note, just for principle, that you can also adapt "smashing into planes" algorithm with this, except you would want to use spherical coordinates to generate vectors only on the unit sphere.

[Edited by - mooserman352 on November 7, 2006 11:24:28 PM]
Quote:Original post by Kest
Is there a way to compute a random perp vector to an original 3D vector without crossing?


Vector3 forward = (x,y,z), perp;float angle = random_angle, cs = cos(angle), sn = sin(angle);if (|x| >= |y|){    perp = (cs*(-z,0,x)+sn*(x*y,-x*x-z*z,y*z))/sqrt(x*x+z*z);}else{    perp = (cs*(0,z,-y)+sn*(-y*y-z*z,x*y,x*z)/sqrt(y*y+z*z);}

Assuming m is the desired magnitude of the random vector,
r = 1-2*rand();
s = 1-2*rand();
t = sqrt(m*m-r*r-s*s) * -rand()%2;

Vector perp(y*t-z*s,z*r-x*t,x*s-y*r);

It's still the cross product, but in four lines and normalized.

Overall:
3 random numbers,
1 square root
1 modulus
12 multiplications
1 unary minus
7 subtractions
1 Vector constructor

Even for hundreds of particles, I'm sure it pales in comparison to the number of flops you're doing updating the rest of the system.

Edit: Seriously, Admiral isn't just making this up as he goes along.
We''re sorry, but you don''t have the clearance to read this post. Please exit your browser at this time. (Code 23)
Technically, the following is an equivalent solution using cross-products:
Vector random(rand(), rand(), rand());Velocity = Cross(forward, random);

The cross-product of any two vectors always produces a vector that is orthogonal to two input vectors, and since one of these vectors is random the orthogonal vector is random, and just as good as any other orthogonal random vector you could produce via other means (again ignoring the specifics of the distribution, which can be fixed/modified using other PRNGs). I actually prefer this solution to orthogonalization, since it's always less operations regardless of vector length. And it's quite clear to anyone looking at this code exactly what's going on.
Quote:Original post by erissian
Assuming m is the desired magnitude of the random vector,
r = 1-2*rand();
s = 1-2*rand();
t = sqrt(m*m-r*r-s*s) * -rand()%2;

Vector perp(y*t-z*s,z*r-x*t,x*s-y*r);

Beautiful [smile].

Quote:Original post by Zipster
Vector random(rand(), rand(), rand());
Velocity = Cross(forward, random);

Very concise, but doesn't produce a normalised result. If you throw a normalise & scale operation onto the end of this, it would probably be fractionally slower than erissian's method, but it looks bug-proof.

Two excellent answers. Can we go to the pub now?

Regards
Admiral
Ring3 Circus - Diary of a programmer, journal of a hacker.
Yeah I just made the assumption that the magnitude should be random too, since that appears to be what Kest wanted judging by his original post.

And you're buying [smile]
I normally refrain from these efficiency related discussions, but, with all due respect, I can't see how the cross product method could possibly be faster than the alternate method.

the OP's initial method (after being cleaned up a bit) has, for each particle past the first one, approximately:

1 random numbers
0 square roots
0 modulus
5-6 explicit multiplactions at max
5-6 explicit additions as max
1 cosine (evaluating a polynomail at worse, a look-up table at best)
1 sine
1 vector constructor

I honestly have no idea about the cost of each operation, but I would think that generating a random number, or computing a square root, is more costly than evaluating a cosine. If elegance is a concern, then there should be no need to call rand() any more than once, as there is only one degree of freedom here.
Even though I was curious to hear of both, I probably should have mentioned from the beginning that the forward vector doesn't change for each particle. It is generated when a projectile (bullet) makes contact with another object. The particles spew off of the collision surface. In this case, when a bullet smashes into marble tiling, pieces of marble and dust go flying.

Quote:Original post by mooserman352
..then all you need to do is generate a random angle between 0 and 2*pi. then you have x = cos(theta) y = sin(theta), and so your final vector is x*up + y*right.

This is the solution I'm looking for. And this little bit of math is the only math needed for each particle. Just generate the vectors and randomly rotate around them. Perfect.

Quote:Note, just for principle, that you can also adapt "smashing into planes" algorithm with this, except you would want to use spherical coordinates to generate vectors only on the unit sphere.

If you mean to smash the velocity vector to a half-unit sphere facing the forward vector, then that would introduce some forward velocity (if I understand correctly). The scaling for velocity is plugged in from an outside source, and the forward velocity is a seperate scaler value from the outward velocity. So I don't want to introduce any of one through the other's scale.

Quote:Original post by erissian
Assuming m is the desired magnitude of the random vector,
r = 1-2*rand();
s = 1-2*rand();
t = sqrt(m*m-r*r-s*s) * -rand()%2;

Vector perp(y*t-z*s,z*r-x*t,x*s-y*r);

It's still the cross product, but in four lines and normalized.

I appreciate the time you spent on this, but the math envolved is pretty much the same that's been previously posted. Just yanked out of explicit functions. As I've stated, I'm not trying to optimize. I'm looking for a simpler operation. This doesn't seem to be moving in that direction.

Quote:Original post by erissian
Even for hundreds of particles, I'm sure it pales in comparison to the number of flops you're doing updating the rest of the system.

Your assumptions are irrelevant to the topic, Mr. Negative [grin]

Quote:Original post by Zipster
Technically, the following is an equivalent solution using cross-products:
Vector random(rand(), rand(), rand());Velocity = Cross(forward, random);

This is nice as well. Just a normalize at the end and it's all good.

I appreciate all of the help. I think I'll try the clock-face spin-on-the-plane-dot approach.

This topic is closed to new replies.

Advertisement