Saturday, October 1, 2016

ios - Adding an angle variance to a 3d vector


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

Simple past, Present perfect Past perfect

Can you tell me which form of the following sentences is the correct one please? Imagine two friends discussing the gym... I was in a good s...