Advertisement

Random Vector Based On Cone Angle

Started by March 07, 2016 03:26 AM
5 comments, last by Nanoha 8 years, 11 months ago

I have the following function which generate a random vector:


D3DXVECTOR3 GetRandVector()
{
    D3DXVECTOR3 vVector;

    // Pick a random Z between -1.0f and 1.0f.
    vVector.z = RandomFloat( -1.0f, 1.0f );

    // Get radius of this circle
    float radius = (float)sqrt(1 - vVector.z * vVector.z)

    // Pick a random point on a circle.
    float t = RandomFloat( (float)-D3DX_PI, (float)D3DX_PI );

    // Compute matching X and Y for our Z.
    vVector.x = (float)cosf(t) * radius;
    vVector.y = (float)sinf(t) * radius;

    return vVector;
}

How do I change the above function to be able to generate a random vector based on cone angle?

e.g: GetRandVector(45.0f) // Maximum 45.0f Degree

I tried to change the line:


float t = RandomFloat( (float)-D3DX_PI, (float)D3DX_PI );

To:


float Degree = 45.0f; // Trying to limit the random vector to 45 degree
float t = Random( (float)-D3DXToRadian(Degree), (float)D3DXToRadian(Degree) );

But it doesn't work as expected.

Decide which way is 'forward', e.g. (0, 0, 1) might be forward. That's your start. Now create a random angle in the range you desire (+/- 45 degrees) Use that to rotate your start vector about one of the other axis (either x or y, doesn't really matter). That's 2D so far so to make it into a cone you need to create another random angle between +/- 180 and you rotate your current vector about the original axis (the original forward).

Of course that only points one way more or less and you might want to have a random vector pointing at a target. To do that you can generalize the above slightly. First you need a forward axis still ( which is pointing at your target) and you then need an orthogonal vector (right or up). First rotate the original vector about one of the orthogonal vectors (+/- angle) and then rotate the new vector about the original 'forward' vector by +/- 180. You could probably use a quaternion to do that.

Alternately you could work it out as in the first method then use a transformation matrix to transform it such that the original forward axis aligns with the 'to target' vector that you desire.

Interested in Fractals? Check out my App, Fractal Scout, free on the Google Play store.

Advertisement

@Nanoha:Here is what I'm doing:


void GenerateParticle()
{
Particle.Velocity = Velocity;
Particle.Velocity += GetRandVector() * velocityVar;
normalize(Particle.Velocity);
Particle.Velocity *= ParticleSpeed;
// ...
}

From the above code I get the particles moving as a cone shape, and if I increase velocityVar the cone angle will increase

How do I set velocityVar based on certain degree so I can determine the cone angle based on degree?

// Pick a random Z between -1.0f and 1.0f.
vVector.z = RandomFloat( -1.0f, 1.0f );


Change that to pick a random number between cos(max_angle) and 1.0f. That should generate random points in a cone. If you want the cone pointing in some other direction, apply a rotation at the end.

@Nanoha:Here is what I'm doing:


void GenerateParticle()
{
Particle.Velocity = Velocity;
Particle.Velocity += GetRandVector() * velocityVar;
normalize(Particle.Velocity);
Particle.Velocity *= ParticleSpeed;
// ...
}

From the above code I get the particles moving as a cone shape, and if I increase velocityVar the cone angle will increase

How do I set velocityVar based on certain degree so I can determine the cone angle based on degree?

This might get a bit costly for doing with particles but here goes. I am assuming your particles are travelling in some direction and you want to add a random cone like deviation for them of a specified angle. I haven't really used directx in a very long time so this will be more pseudo code-ish.

First you need to decide what direction the particle is going as if it had no deviation at all


float magnitude = magnitude(Velocity); // magnitude is effectively the particle speed
Vector direction = Velocity/magnitude; // normalizing

// Then you apply your random vector
Particle.Velocity = RandomVector(direction, 45);
// And make use of the magnitude/speed again since RandomVector will return a normalized vector
Particle.Velocity *= magnitude;

RandomVector will look something like this:


// assumes direction is normalized
Vector RandomVector(const Vector& direction, float degrees)
{
    // We need an orthogonal vector, we do this by using the cross product of
    // direction and 'up' but if direction is up we have a problem so check
    Vector axis;
    if(direction == UP) // whatever DX calls up
        axis = RIGHT; // whatever DX calls right
    else
        axis = CrossProduct(direction, UP);

    float random = Random(0, degrees);
    // Apply first rotation,
    Quaternion rotation = FromAxisAngle(axis, DegToRad(random));
    Vector out = rotation*direction;
    random = Random(-Pi, Pi);
    rotation = FromAxisAngle(direction, random);
    // Apply second rotation
    out = rotation*out;
    return out;
}

That's a fair chunk of work for each particle though. What it's doing (or what it should be doing..) is imagine you have a clock at shoulder height, you first point at the centre of that clock and then you raise your arm a random amount (the 0 to 45 degrees). You then go around the clock face a random amount either clockwise or anticlockwise. Where your arm now faces is the returned value.

It should work for whatever direction you choose, whether the original is pointing up or left or 45 degrees from the horizontal. There's probably a more efficient method similar to what I believe you were first trying but I think that will get complicated once you need particles go in other directions (it's quite easy to do if you are only going to be looking one axis aligned direction).

Interested in Fractals? Check out my App, Fractal Scout, free on the Google Play store.

@Nanoha: I believe that this can be much more simpler

When I multiply GetRandVector() with 0.04f I get a cone shape which is approximately 45 degree

If I multiply GetRandVector() with 0.08f the cone will be approximately 90 degree


Particle.Velocity = ParticleDirection;
float coneAngle = 0.04f;
Particle.Velocity  += GetRandVector() * coneAngle;
// From the above code the cone will be look towards ParticleDirection and it will be approximately 45 degree

Which means I can set the cone angle by simply multiplying GetRandVector() with a float, the problem is that I'm not sure how do I generate the variable coneAngle from a degree

Example:

GetRandVector() * foo(45.0f) // foo() should return a value similar to 0.04f so I can get 45 degree cone

Advertisement

Could you explain how your GetRandVector() works, line by line. I am not sure what the radius you are working out is. Where does the 1 come from? I think you are going about this backwards trying to work out that magic number to make it work. It looks to me like you are making a point on the horizontal of a circle and then rotating it around to get the x/y position but instead of working out the radius of that circle you should be setting it's radius (since it's radius is dependant on the angle you want). A bigger angle would require a bigger radius and vice-versa.

Imagine you point your arm out in front of you and draw a circle, if your arm is limited to only moving 10 degrees then that circle you draw will be a certain size and any random vector chosen will be limited to only fitting in there but if instead of 10 degrees of movement you had 20 degrees the circle you could create would be much bigger. To work out the radius of the circle you just need a little bit of trig. Make the distance that the circle is away from you fixed (say 1 unit) then the radius of the circle would be:


float radius = distance*tan(angle); // tan(angle) since distance is 1. 
//Once you have that you work out a random distance along it (x axis)
float r = RandomFloat(0, radius);
// Pick a random point on your circle
float t = RandomFloat( (float)-D3DX_PI, (float)D3DX_PI );
// Compute matching X and Y for our Z.
float x = cos(t) * r;
float y = sin(t) * r; 
// The point on the circle will then be at (x, y, 1) so that's your vector
// You should normalize it

I'm not sure what is considered 'up' with direct x but I am assuming it is y.

Interested in Fractals? Check out my App, Fractal Scout, free on the Google Play store.

This topic is closed to new replies.

Advertisement