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.
Answer
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)
.
No comments:
Post a Comment