Jump to content


- - - - -

random vector on plane


8 replies to this topic

#1 Remdul

    New Member

  • Members
  • PipPip
  • 29 posts

Posted 18 May 2007 - 04:45 PM

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!

#2 roel

    Senior Member

  • Members
  • PipPipPipPip
  • 698 posts

Posted 18 May 2007 - 06:28 PM

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

#3 Reedbeta

    DevMaster Staff

  • Administrators
  • 5311 posts
  • LocationSanta Clara, CA

Posted 18 May 2007 - 07:25 PM

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.
reedbeta.com - developer blog, OpenGL demos, and other projects

#4 Remdul

    New Member

  • Members
  • PipPip
  • 29 posts

Posted 21 May 2007 - 02:25 PM

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?

#5 Nils Pipenbrinck

    Senior Member

  • Members
  • PipPipPipPip
  • 597 posts

Posted 21 May 2007 - 07:12 PM

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);

}


My music: http://myspace.com/planetarchh <-- my music

My stuff: torus.untergrund.net <-- some diy electronic stuff and more.

#6 Jinji

    New Member

  • Members
  • Pip
  • 1 posts

Posted 20 May 2010 - 10:02 AM

That looks very complicated.

What about something like this:

http://dzindzinovic....t-on-plane.html

EDIT:
According to this solution
http://www.devmaster...read.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.

#7 .oisyn

    DevMaster Staff

  • Moderators
  • 1842 posts

Posted 20 May 2010 - 10:11 AM

That has no uniform distribution.
C++ addict
-
Currently working on: the 3D engine for Tomb Raider.

#8 tuohirv

    Member

  • Members
  • PipPip
  • 33 posts

Posted 05 August 2010 - 11:05 AM

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

#9 juhnu

    Valued Member

  • Members
  • PipPipPip
  • 292 posts

Posted 14 August 2010 - 11:54 AM

Quote

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).





1 user(s) are reading this topic

0 members, 1 guests, 0 anonymous users