3
votes

I have a unit vector in 3D space whose direction I wish to perturb by some angle within the range 0 to theta, with the position of the vector remaining the same. What is a way I can accomplish this?

Thanks.

EDIT: After thinking about the way I posed the question, it seems to be a bit too general. I'll attempt to make it more specific: Assume that the vector originates from the surface of an object (i.e. sphere, circle, box, line, cylinder, cone). If there are different methods to finding the new direction for each of those objects, then providing help for the sphere one is fine.

EDIT 2: I was going to type this in a comment but it was too much.

So I have orig_vector, which I wish to perturb in some direction between 0 and theta. The theta can be thought of as forming a cone around my vector (with theta being the angle between the center and one side of the cone) and I wish to generate a new vector within that cone. I can generate a point lying on the plane that is tangent to my vector and thus creating a unit vector in the direction of the point, call it rand_vector. At this time, I orig_vector and trand_vector are two unit vectors perpendicular to each other.

I generate my first angle, angle1 between 0 and 2pi and I rotate rand_vector around orig_vector by angle1, forming rand_vector2. I looked up a resource online and it said that the second angle, angle2 should be between 0 and sin(theta) (where theta is the original "cone" angle). Then I rotate rand_vector2 by acos(angle2) around the vector defined by the cross product between rand_vector2 and orig_vector.

When I do this, I don't obtain the desired results. That is, when theta=0, I still get perturbed vectors, and I expect to get orig_vector. If anyone can explain the reason for the angles and why they are the way they are, I would greatly appreciate it.

EDIT 3: This is the final edit, I promise =). So I fixed my bug and everything that I described above works (it was an implementation bug, not a theory bug). However, my question about the angles (i.e. why is angle2 = sin(theta)*rand() and why is perturbed_vector = rand_vector2.Rotate(rand_vector2.Cross(orig_vector), acos(angle2)). Thanks so much!

6
You want the angle to change, but the vector position to remain unaltered? What else can you alter to change the angle?Zaid
I just want the direction of my vector to change but the position from which the vector originates to remain unaltered.Myx
You multiply your vector by a rotation matrix. If it was not centered at origin, subtract the offset first, then multiply, then put it back. Strictly mathematically speaking, all vectors start at (0,0,0) in 3D.Hamish Grubijan
@JustJeff: Imagine I have my vector, which originates from some point on an object and goes in a particular direction. Then, form a cone around that vector, which has its apex at the same position as the vector. I want to change the direction of my vector to lie anywhere within that cone.Myx
Myx: vectors do not originate from anywhere, or have a position. A vector is just a magnitude and direction.davidtbernal

6 Answers

8
votes

Here's the algorithm that I've used for this kind of problem before. It was described in Ray Tracing News.

1) Make a third vector perpendicular to the other two to build an orthogonal basis:

cross_vector = unit( cross( orig_vector, rand_vector ) )

2) Pick two uniform random numbers in [0,1]:

s = rand( 0, 1 )
r = rand( 0, 1 )

3) Let h be the cosine of the cone's angle:

h = cos( theta )

4) Modify uniform sampling on a sphere to pick a random vector in the cone around +Z:

phi = 2 * pi * s
z = h + ( 1 - h ) * r
sinT = sqrt( 1 - z * z )
x = cos( phi ) * sinT
y = sin( phi ) * sinT

5) Change of basis to reorient it around the original angle:

perturbed = rand_vector * x + cross_vector * y + orig_vector * z
1
votes

If you have another vector to represent an axis of rotation, there are libraries that will take the axis and the angle and give you a rotation matrix, which can then be multiplied by your starting vector to get the result you want.

However, the axis of rotation should be at right angles to your starting vector, to get the amount of rotation you expect. If the axis of rotation does not lie in the plane perpendicular to your vector, the result will be somewhat different than theta.

That being said, if you already have a vector at right angles to the one you want to perturb, and you're not picky about the direction of the perturbation, you can just as easily take a linear combination of your starting vector with the perpendicular one, adjust for magnitude as needed.

I.e., if P and Q are vectors having identical magnitude, and are perpendicular, and you want to rotate P in the direction of Q, then the vector R given by R = [Pcos(theta)+Qsin(theta)] will satisfy the constraints you've given. If P and Q have differing magnitude, then there will be some scaling involved.

0
votes

You may be interested in 3D-coordinate transformations to change your vector angle.

I don't know how many directions you want to change your angle in, but transforming your Cartesian coordinates to spherical coordinates should allow you to make your angle change as you like.

0
votes

Actually, it is very easy to do that. All you have to do is multiply your vector by the correct rotation matrix. The resulting vector will be your rotated vector. Now, how do you obtain such rotation matrix? That depends on the 3d framework/engine you are using. Any 3d framework must provide functions for obtaining rotation matrices, normally as static methods of the Matrix class.

Good luck.

0
votes

Like said in other comments you can rotate your vector using a rotation matrix.

The rotation matrix has two angles you rotate your vector around. You can pick them with a random number generator, but just picking two from a flat generator is not correct. To ensure that your rotation vector is generated flat, you have to pick one random angle φ from a flat generator and the other one from a generator flat in cosθ ;this ensures that your solid angle element dcos(θ)dφ is defined correctly (φ and θ defined as usual for spherical coordinates).

Example: picking a random direction with no restriction on range, random() generates flat in [0,1]

angle1 = acos(random())
angle2 = 2*pi*random()
0
votes

My code in unity - tested and working:

/*
 *  this is used to perturb given vector 'direction' by changing it by angle not more than 'angle' vector from 
 *  base direction. Used to provide errors for player playing algorithms 
 * 
 */ 
Vector3 perturbDirection( Vector3 direction, float angle ) {
    // division by zero protection 
    if( Mathf.Approximately( direction.z, 0f )) {
        direction.z = 0.0001f; 
    }
    // 1 get some orthogonal vector to direction ( solve direction and orthogonal dot product = 0, assume x = 1, y = 1, then z = as below )) 
    Vector3 orthogonal = new Vector3( 1f, 1f, - ( direction.x + direction.y ) / direction.z );
    // 2 get random vector from circle on flat orthogonal to direction vector. get full range to assume all cone space randomization (-180, 180 )
    float orthoAngle = UnityEngine.Random.Range( -180f, 180f );
    Quaternion rotateTowardsDirection = Quaternion.AngleAxis( orthoAngle, direction );
    Vector3 randomOrtho = rotateTowardsDirection * orthogonal;
    // 3 rotate direction towards random orthogonal vector by vector from our available range 
    float perturbAngle = UnityEngine.Random.Range( 0f, angle );   // range from (0, angle), full cone cover guarantees previous (-180,180) range   
    Quaternion rotateDirection = Quaternion.AngleAxis( perturbAngle, randomOrtho );
    Vector3 perturbedDirection = rotateDirection * direction;
    return perturbedDirection;
}