I am converting a particle emitter from 2d to 3d.

In the 2d system the original coder used a random angle variance and added it to the angle. So it would be something like:

thisAngle = emitterAngle + angleVariance * RANDOM_MINUS_1_TO_1();

I want to do the same thing in 3d using an emission vector (vec3) and again an angleVariance. My specific variance for this particle will still be angleVariance * RANDOM_MINUS_1_TO_1(). But how do I apply that angle a 3d vector so that the new vector could be any value X degrees off of center. So if the original vector is straight up (0,1,0) the new vector could be off in the X or the Z and still normalized to 1.

Any hints? I have all the matrix and vector functions at my disposal.


Thanks to nathan I was able to write this code. Not sure if I've over complicated it somewhere. Once I test it I'll checkmark his answer:

    // first pick a random Z from [cos(angleVariance), 1]
    GLfloat varianceZ = [self randomFloatBetween:myCosf(angleVariance) andLargerFloat:1.0];
    // and a random angle for the azimuth
    GLfloat rndA = [self randomFloatBetween:0.0 andLargerFloat:360.0];

    // then calculate the X & Y to make this a vector off of the +Z axis with a max width of angleVariance
    GLfloat varianceX = sqrt(1 - varianceZ*varianceZ) * myCosf(rndA); // my functions input degrees not radians
    GLfloat varianceY = sqrt(1 - varianceZ*varianceZ) * mySinf(rndA);

Up to here the above code works. It makes a variance angle off center but it is aligned to the +z axis. The next part is supposed to rotate/transform the angle to my emissionVector angle so it's a variance off of that. But so far this part isn't working.

    // next find an axis to rotate the point around to put it in line with the emmissionVector
    vec3 rotationAxis = normalizeVec3(crossProduct(vec3Make(0.0, 0.0, 1.0), emissionVector));
    GLfloat rotationAngle = acos(degreesToRadians(emissionVector.z));

    // make a quaternion out of the rotation axis and angle
    vec4 q = quaternionVector(rotationAxis, rotationAngle);
    // turn the quaternion into a matrix
    mat4 rotMat4;
    mat3 rotMat3;
    matrixQuaternion(q, rotMat4);
    getMat3FromMat4(rotMat4, rotMat3);
    // multiply the variance vector by the matrix to get a resulting vector with the variance added
    vector = matrixTransformVec3(rotMat3, vec3Make(varianceX, varianceY, varianceZ));


The above code doesn't appear to be correct. I'm still working on the solution and will fix the above code when I get it working.


2 Answers 2


There is a formula for picking a random point on a sphere which can be adapted for your purposes. Generating a random point on a sphere can be done by picking a z-coordinate uniformly at random in [–1, 1], then selecting a random angle θ in [0, 2π] and calculating:

x = sqrt(1 - z^2) * cos θ
y = sqrt(1 - z^2) * sin θ

This can be extended to pick a random point on a spherical cap with any angular width, by simply choosing z uniformly at random within [cos(angleVariance), 1] instead of [–1, 1]. Then apply the preceding formulas for x and y as usual, and you'll end up with a random point distributed around the +Z axis but staying within angleVariance of it.

Finally, you can rotate the resulting point to align the +Z axis with your desired emission vector. To do this, you can rotate around the axis normalize(cross(float3(0, 0, 1), emissionVector)) by the angle acos(emissionVector.z).

  • \$\begingroup\$ Thanks for your answer. Ok I've wrapped my head around part 1. I understand picking the z between aces(angleVariance) and 1 is going to work. And I understand the random θ to get like an azimuth for that z. And I understand the concept of rotating my vector which is in relation to the +Z axis into the emissionVector axis. But I'm not grasping how to write the formula for that part. Is it a matrix rotate? \$\endgroup\$
    – badweasel
    Feb 15, 2014 at 7:55
  • \$\begingroup\$ I'm starting to get it. The "normalize..." is an arbitrary axis that I need to rotate my variance point around by that angle. How to do this rotation in code is what I'm searching for now. I hate finding the answers on the math stack exchange cause they're in formula speak and I read and understand code easier. If you have a hint or a link on that great. I'll checkmark the answer later tonight when the code is working :) \$\endgroup\$
    – badweasel
    Feb 15, 2014 at 8:10
  • \$\begingroup\$ Sorry. Seems like I should some sort of quaternion math. The axis,angle combo is a quaternion. \$\endgroup\$
    – badweasel
    Feb 15, 2014 at 8:17
  • \$\begingroup\$ @badweasel Quaternions are one way to do it, yes. You can construct the quaternion from the axis and angle, and use that to rotate the generated points. You can also construct a rotation matrix from an axis and angle. I find Martin Baker's site a good reference for rotation formulae. \$\endgroup\$ Feb 15, 2014 at 8:30
  • \$\begingroup\$ I don't think acos(angleVariance) is correct. Arc Cos takes a number between -1 & +1 and returns an angle. It seems more likely that I'd want cos there. If the angle of variance is 10 degrees, that's going to be spattered very close to the axis. Cos(10) is .98. \$\endgroup\$
    – badweasel
    Feb 16, 2014 at 10:40

In the 2D case your random values could be sampled from an arc/half a cricle and you have only a one degree of freedom. In the 3D case you can sample half a sphere, so in this case we need to use spherical coordinates.

Let's assume we have X,Y,Z in Cartesian coordinates, and r, theta, and alpha in spherical coordinates.

Recall that

  • x = r * sin(theta)*cos(alpha)
  • y = r * sin(theta)*sin(alpha)
  • z = r * cos(theta)

Assuming r is 1 (unit sphere), now we have 2 variables we can choose randomly theta and alpha. According to mathworld it is incorrect to sample alpha and theta to be [0,pi) and [0,2pi) because the samples will bunched on the polars. so they suggest the following function, which guarantees every part of the sphere to be applicable,

  • theta = 2 * pi* u
  • alpha = cos_inv( 2*v - 1)

Where u,v are chosen randomly between (0,1). Now inorder to limit the selection to the half of the sphere, you can simply drop 2 in the theta = 2 * pi* u to become theta = pi* u.

I also came by this interesting article which might be of use.

  • \$\begingroup\$ Thanks.. I want to be able to set the limit.. not be a full half sphere. For this emitter it's a jet engine exhaust and maybe some smoke. So really I only need a few degrees of randomness to the vector to give the exhaust a natural look. \$\endgroup\$
    – badweasel
    Feb 15, 2014 at 3:27
  • \$\begingroup\$ and while my example was a vector pointing straight up, in practice it will be moving around and won't be up. \$\endgroup\$
    – badweasel
    Feb 15, 2014 at 3:32
  • \$\begingroup\$ I think I understand.. but there are a couple of things I need to turn this in to a formula. I think instead of 2 pi or even pi for the half circle I can limit it by the number of degrees converted to radians. The formula would be theta = variance * u. Where variance is in radians. But the next question becomes HOW do I add or apply that variance to a random vector? \$\endgroup\$
    – badweasel
    Feb 15, 2014 at 6:01

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .