random vector on plane

8c58c966787228871e085f7ffa0bdaeb
0
Remdul 101 May 18, 2007 at 16:45

Hi,

How can one generate a random vector that diverges from an arbitrary vector to a given range (angle)?

In case I’m not clear, I want something like a particle emitter that has a certain ‘spread’ angle.

So, given are:

-vector x,y,z (or plane normal)
-spread angle

I’ve read something about turning the vector into a quaternion (how?), then rotating a vector by a (random?) number of degrees, and something similar with a (rotation?) matrix, but I don’t have a clue how/if this works.

Thanks!

8 Replies

Please log in or register to post a reply.

6aa952514ff4e5439df1e9e6d337b864
0
roel 101 May 18, 2007 at 18:28

edit: Sorry, I didn’t read carefully enough; I thought that you asked for an exact angle, not a range.

A8433b04cb41dd57113740b779f61acb
0
Reedbeta 167 May 18, 2007 at 19:25

Try starting with the equations for a random point on a sphere; you can easily modify the equations to generate a random vector within a cone by scaling the polar angle (denoted phi on that page) to the range you want. The cone will be centered the north pole of the sphere (usually (0, 0, 1)), but as roel said you can then rotate it to position the cone along whatever vector you want.

However, this gives you a uniform distribution over the cone and you might rather want the distribution to be biased more towards the center and fade out around the edges. You can easily acheive a cosine-weighted distribution by removing the arc-cosine in the sphere-point-picking equations. You could also apply a Gaussian function or something to the polar angle for more control over the spread.

8c58c966787228871e085f7ffa0bdaeb
0
Remdul 101 May 21, 2007 at 14:25

I guess it all comes down to converting the vector (plane) to quaternion/matrix, then use that to rotate an up-vector by a random value (which lies within a range).

What is the easiest way to convert a vector into a quaternion? I can compute a 3x3 rotation matrix from the vector with a few cross products (lookat), then convert the matrix to quaternion. Is there a shortcut?

B91eae75cd6245bd8074bd0c3f1cc495
0
Nils_Pipenbrinck 101 May 21, 2007 at 19:12

Hi Remdul.

I just wrote some code that somewhat works the way you thought up.

I built an orthogonal-basis (coordinate system) from the normal, calculated a random point on a circle in that coordinate system and just applied some basic trigonometry to build the new vector.

If you precompute the orthogonal basis and the sin/cos of the spread angle it boils down to some muls, adds and a square-root.

Works with spread-angles up to, but not reaching 180°. Afterwards it will break down.

typedef struct 
//////////////
{
  float x,y,z;
} Vector3;


void random_point_unitcircle (float *a_x, float *a_y)
/////////////////////////////////////////////////////
{
  const float InvRandMax = 2.0f / (float)RAND_MAX;
  float r,x,y;
  do {
    // get two randoms between -1 and 1:
    x = ((float)rand() * InvRandMax) - 1.0f;
    y = ((float)rand() * InvRandMax) - 1.0f;
    // get radius (squared)
    r = x*x+y*y;
    // and repeat while point is not inside the unitcircle:
  } while (r > 1.0f);
  *a_x = x;
  *a_y = y;
}

void normalize (Vector3 * vec)
///////////////////////////////
// normalized a vector
{
  float len = (float)sqrt (vec->x * vec->x + vec->y * vec->y + vec->z * vec->z);
  vec->x /= len;
  vec->y /= len;
  vec->z /= len;
}

void unitcross (Vector3 * out, Vector3 * a, Vector3 * b)
///////////////////////////////////////////////////////
// Builds cross product of A and B, returns normalized result.
{
  out->x = a->y * b->z - a->z * b->y;
  out->y = a->z * b->x - a->x * b->z;
  out->z = a->x * b->y - a->y * b->x;
  normalize (out);
}


void build_orthogonal_basis (Vector3 * Normal, Vector3 * Tangent, Vector3 * Binormal)
/////////////////////////////////////////////////////////////////////////////////////
// Builds a crude orthogonal-basis. 
// Assumes, that Normal is a unit-vector (e.g. length(Normal) == 1)
{
  // Pick any Unit-Vector that's not orthogonal to Normal:
  if (fabs(Normal->x) > fabs(Normal->y))
  {
    Tangent->x = 0;
    Tangent->y = 1;
    Tangent->z = 0;
  } else {
    Tangent->x = 1;
    Tangent->y = 0;
    Tangent->z = 0;
  }

  // build a binormal from tangent and normal:
  unitcross (Binormal, Tangent, Normal);

  // And correct the tangent from the binormal and the normal.
  unitcross (Tangent, Normal, Binormal);
}


void random_vector (Vector3 * result, Vector3 * normal, float theta)
{
  Vector3 t,b;
  float x,z; 
  float sin_theta, cos_theta;

  // we need half the spread-angle only:
  sin_theta = sin(theta * 0.5f);
  cos_theta = cos(theta * 0.5f);

  // build/guess a orthogonal-basis from the normal: 
  build_orthogonal_basis  (normal, &t, &b);

  // get random point in 2D:
  random_point_unitcircle (&x, &z);

  // scale the random point to apply the spread:
  x *= sin_theta;
  z *= sin_theta;

  // transform point into orthogonal basis:
  result->x = t.x       * x + t.z       * z;
  result->y = normal->x * x + normal->z * z;
  result->z = b.x       * x + b.z       * z;

  // move the now rotated point along the normal.
  // apply scaling to apply the spread:
  result->x += normal->x * cos_theta;
  result->y += normal->y * cos_theta;
  result->z += normal->z * cos_theta;
  
  // normalize it:
  normalize (result);
}
Ba6d7baec0ab5f2b75723cb3301a09a0
0
Jinji 101 May 20, 2010 at 10:02

That looks very complicated.

What about something like this:

http://dzindzinovic.blogspot.com/2010/05/xna-random-point-on-plane.html

EDIT:
According to this solution

http://www.devmaster.net/forums/showthread.php?t=9776
the radius for a random point in a circle should be a square root of a random number.
Since the cross product in that function is normalized (has unit length), the distribution should be uniform.

340bf64ac6abda6e40f7e860279823cb
0
_oisyn 101 May 20, 2010 at 10:11

That has no uniform distribution.

316cf5620d41dfc55bf42e879a52f457
0
tuohirv 101 Aug 05, 2010 at 11:05

You are thinking too complicated if you just want to spread a particle’s emitted in some power.

I would do something like this (note, code is written directly in here and not checked for errors.. the idea matters only).

void emitparticle( flost pos[3], float posrandom, float dir[3], float dirrandom ) {
float rx = (float)(rand()&255)-127);
float ry = (float)(rand()&255)-127);
float rz = (float)(rand()&255)-127);
// normalize
float len = sqrtf( rx*rx+ry*ry+rz*rz);
rx /= len; ry/=len; rz/=len;

emitted_particle.posx = pos[0]+rx*posrandom;
emitted_particle.posy = pos[1]+ry*posrandom;
emitted_particle.posz = pos[2]+rz*posrandom;

emitted_particle.dirx = dir[0] + rx *dirrandom;
emitted_particle.diry = dir[1] + ry *dirrandom;
emitted_particle.dirz = dir[2] + rz *dirrandom;
}

So, the point is that you provide a direction vector for the particle (dir[3]) and then change it with a random factor. There are no specific angle of spread easily calculated, but you dont need such in most cases like these.

45degree distribution would be
dirrandom = length_of_dir3

Tuomo

46462f88a1670d7e9cbbfa360aa20134
0
juhnu 101 Aug 14, 2010 at 11:54

There are no specific angle of spread easily calculated, but you dont need such in most cases like these.

A dot product along the principal axis will do - simply reject/ignore all the vectors that are outside the preferred angle. For orientation of the whole emitter one can use normal matrices for coordinate transformations (transforming from emitter to world space).