Search code examples
c#unity-game-enginevectortrigonometryorthogonal

How to find a randomic Vector orthogonal to a given Vector


I have a Vector with 3 components (X,Y,Z) and i want to find a Vector orthogonal to the given one. Since the Vectors orthogonal to any Vector are infinite, i just need one that is randomic.

I've tried using an equation with the Dot Product formula, since the dot product between two orthogonal Vectors is always 0, and I managed to write a bit of code that works only when the given Vector is axis-aligned, but that is probably because the randomized components of the vectors are X and Y. I really can't get my head around this.

I wrote my code on the Unity3D engine in order to be able to easly visualize it:

Vector3 GenerateOrthogonal(Vector3 normal)
    {
        float x = Random.Range(1f, -1f);
        float y = Random.Range(1f, -1f);

        float total = normal.x * x + normal.y * y;
        float z = -total / -normal.z;

        return new Vector3(x, y, z).normalized;
    }

Solution

  • There are a few methods for doing this. I'll provide two. The first is a one-liner that uses Quaternions to generate a random vector and then rotate it into place:

    Vector3 RandomTangent(Vector3 vector) {
        return Quaternion.FromToRotation(Vector3.forward, vector) * (Quaternion.AngleAxis(Random.Range(0f, 360f), Vector3.forward) * Vector3.right);
    }
    

    The second is longer, more mathematically rigorous, and less platform dependent:

    Vector3 RandomTangent(Vector3 vector) {
        var normal = vector.normalized;
        var tangent = Vector3.Cross(normal, new Vector3(-normal.z, normal.x, normal.y));
        var bitangent = Vector3.Cross(normal, tangent);
        var angle = Random.Range(-Mathf.PI, Mathf.PI);
        return tangent * Mathf.Sin(angle) + bitangent * Mathf.Cos(angle);
    }
    

    Here are some notes on their differences:

    • Both of these functions generate a random perpendicular vector (or "tangent") with a uniform distribution.
    • You can measure the accuracy of these functions by getting the angle between the input and the output. While most of the time it will be exactly 90, there will sometimes be very minor deviations, owing mainly to floating point rounding errors.
    • While neither of these functions generate large errors, the second function generates them far less frequently.
    • Initial experimentation suggests the performance of these functions is close enough that the faster function may vary depending on platform. The first one was actually faster on my machine for standard Windows builds, which caught me off guard.
    • If you're prepared to assume that the input to the second function is a normalized vector, you can remove the explicit normalization of the input and get a performance increase. If you do this and then pass it a non-normalized vector, you'll still get a perpendicular vector as a result, but its length and distribution will no longer be reliably uniform.
    • In the degenerate case of passing a zero vector, the first function will generate random vectors on the XY plane, while the second function will propagate the error and return a zero vector itself.